-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Default value + Callable syntax #8
Changes from all commits
ec94456
321d8bc
2ad2229
f7d1e94
7b8cfa6
6b6ccb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ class BatchLoader | |
IMPLEMENTED_INSTANCE_METHODS = %i[object_id __id__ __send__ singleton_method_added __sync respond_to? batch inspect].freeze | ||
REPLACABLE_INSTANCE_METHODS = %i[batch inspect].freeze | ||
LEFT_INSTANCE_METHODS = (IMPLEMENTED_INSTANCE_METHODS - REPLACABLE_INSTANCE_METHODS).freeze | ||
NULL_VALUE = :batch_loader_null | ||
|
||
NoBatchError = Class.new(StandardError) | ||
|
||
|
@@ -22,7 +23,8 @@ def initialize(item:) | |
@item = item | ||
end | ||
|
||
def batch(cache: true, &batch_block) | ||
def batch(default_value: nil, cache: true, &batch_block) | ||
@default_value = default_value | ||
@cache = cache | ||
@batch_block = batch_block | ||
__executor_proxy.add(item: @item) | ||
|
@@ -75,12 +77,20 @@ def __ensure_batched | |
return if __executor_proxy.value_loaded?(item: @item) | ||
|
||
items = __executor_proxy.list_items | ||
loader = ->(item, value) { __executor_proxy.load(item: item, value: value) } | ||
loader = -> (item, value = NULL_VALUE, &block) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not very common but there is another way to check whether the optional argument was passed or not: -> (item, value = (value_set = true; nil), &block) {
if block
raise ... if value_set
...
else
raise ... unless value_set
...
end
} This way you can leave your argument checks ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's neat, i didn't know that was possible in that context, although I don't find it particularly legible. I also consider I think the existing implementation is the most readable of the 3, if not necessarily the most clever. |
||
if block | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had no idea you could pass a block to a lambda, that was the only reason I extracted this, so I have re-inlined it. |
||
raise ArgumentError, "Please pass a value or a block, not both" if value != NULL_VALUE | ||
next_value = block.call(__executor_proxy.loaded_value(item: item)) | ||
else | ||
next_value = value | ||
end | ||
__executor_proxy.load(item: item, value: next_value) | ||
} | ||
|
||
@batch_block.call(items, loader) | ||
items.each do |item| | ||
next if __executor_proxy.value_loaded?(item: item) | ||
loader.call(item, nil) # use "nil" for not loaded item after succesfull batching | ||
loader.call(item, @default_value) | ||
end | ||
__executor_proxy.delete(items: items) | ||
end | ||
|
@@ -109,7 +119,7 @@ def __purge_cache | |
def __executor_proxy | ||
@__executor_proxy ||= begin | ||
raise NoBatchError.new("Please provide a batch block first") unless @batch_block | ||
BatchLoader::ExecutorProxy.new(&@batch_block) | ||
BatchLoader::ExecutorProxy.new(@default_value, &@batch_block) | ||
end | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,10 @@ | |
|
||
class BatchLoader | ||
class ExecutorProxy | ||
attr_reader :block, :global_executor | ||
attr_reader :default_value, :block, :global_executor | ||
|
||
def initialize(&block) | ||
def initialize(default_value, &block) | ||
@default_value = default_value | ||
@block = block | ||
@block_hash_key = block.source_location | ||
@global_executor = BatchLoader::Executor.ensure_current | ||
|
@@ -29,7 +30,11 @@ def load(item:, value:) | |
end | ||
|
||
def loaded_value(item:) | ||
loaded[item] | ||
if value_loaded?(item: item) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realized that with the changes the source code became not thread-safe :) It's possible to have race-conditions when Thread1 read the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was able to reproduce the race-condition in #10. Will think what's the best way to fix it. |
||
loaded[item] | ||
else | ||
@default_value.dup | ||
end | ||
end | ||
|
||
def value_loaded?(item:) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to update the
Contents
section as well :)