lib/representable/binding.rb in representable-1.7.7 vs lib/representable/binding.rb in representable-1.8.0
- old
+ new
@@ -8,33 +8,39 @@
class FragmentNotFound
end
def self.build(definition, *args)
# DISCUSS: move #create_binding to this class?
- return definition.create_binding(*args) if definition.binding
+ return definition.create_binding(*args) if definition[:binding]
build_for(definition, *args)
end
- def initialize(definition, represented, user_options={}, exec_context=represented) # TODO: remove default arg for user options. # DISCUSS: make exec_context an options hash?
+ def initialize(definition, represented, decorator, user_options={}) # TODO: remove default arg for user options.
super(definition)
@represented = represented
+ @decorator = decorator
@user_options = user_options
- @exec_context = exec_context
+
+ setup_exec_context!
end
attr_reader :user_options, :represented # TODO: make private/remove.
+ def as # DISCUSS: private?
+ evaluate_option(:as)
+ end
+
# Retrieve value and write fragment to the doc.
def compile_fragment(doc)
- represented_exec_for(:writer, doc) do
+ evaluate_option(:writer, doc) do
write_fragment(doc, get)
end
end
# Parse value from doc and update the model property.
def uncompile_fragment(doc)
- represented_exec_for(:reader, doc) do
+ evaluate_option(:reader, doc) do
read_fragment(doc) do |value|
set(value)
end
end
end
@@ -53,90 +59,106 @@
def read_fragment(doc)
value = read_fragment_for(doc)
if value == FragmentNotFound
return unless has_default?
- value = default
+ value = self[:default]
end
yield value
end
def read_fragment_for(doc)
read(doc)
end
- # concept: Option#call(*args) => send(string)/lambda()
- # dynamic string
def get
- represented_exec_for(:getter) do
+ evaluate_option(:getter) do
exec_context.send(getter)
end
end
def set(value)
- represented_exec_for(:setter, value) do
+ evaluate_option(:setter, value) do
exec_context.send(setter, value)
end
end
- # the remaining methods in this class are format-independent and should be in Definition.
+ # DISCUSS: do we really need that?
+ def representer_module_for(object, *args)
+ evaluate_option(:extend, object) # TODO: pass args? do we actually have args at the time this is called (compile-time)?
+ end
private
- attr_reader :exec_context
+ def setup_exec_context!
+ context = represented
+ context = self if self[:exec_context] == :binding
+ context = decorator if self[:exec_context] == :decorator
- # Execute the block for +option_name+ on the represented object.
- # Executes passed block when there's no lambda for option.
- def represented_exec_for(option_name, *args)
- return yield unless options[option_name]
- call_proc_for(options[option_name], *args)
+ @exec_context = context
end
- # All lambdas are executed on exec_context which is either represented or the decorator instance.
- def call_proc_for(proc, *args)
- return proc unless proc.is_a?(Proc)
- # TODO: call method when proc is sympbol.
- args << user_options # DISCUSS: we assume user_options is a Hash!
- exec_context.instance_exec(*args, &proc)
- end
+ attr_reader :exec_context, :decorator
-
- module Prepare
- def representer_module_for(object, *args)
- call_proc_for(representer_module, object) # TODO: how to pass additional data to the computing block?`
+ # Evaluate the option (either nil, static, a block or an instance method call) or
+ # executes passed block when option not defined.
+ def evaluate_option(name, *args)
+ unless proc = self[name]
+ return yield if block_given?
+ return
end
+
+ # TODO: it would be better if user_options was nil per default and then we just don't pass it into lambdas.
+ options = self[:pass_options] ? Options.new(self, user_options, represented, decorator) : user_options
+
+ proc.evaluate(exec_context, *(args<<options)) # from Uber::Options::Value.
end
- include Prepare
+ # Options instance gets passed to lambdas when pass_options: true.
+ # This is considered the new standard way and should be used everywhere for forward-compat.
+ Options = Struct.new(:binding, :user_options, :represented, :decorator)
+
+
# Delegates to call #to_*/from_*.
module Object
def serialize(object)
ObjectSerializer.new(self, object).call
end
- def deserialize(data, object=lambda { get })
+ def deserialize(data)
# DISCUSS: does it make sense to skip deserialization of nil-values here?
- ObjectDeserializer.new(self, object).call(data)
+ ObjectDeserializer.new(self).call(data)
end
- def create_object(fragment)
- instance_for(fragment) or class_for(fragment)
+ def create_object(fragment, *args)
+ instance_for(fragment, *args) or class_for(fragment, *args)
end
private
def class_for(fragment, *args)
- item_class = class_from(fragment) or return fragment
+ item_class = class_from(fragment, *args) or return handle_deprecated_class(fragment)
item_class.new
end
def class_from(fragment, *args)
- call_proc_for(deserialize_class, fragment)
+ evaluate_option(:class, fragment, *args)
end
def instance_for(fragment, *args)
- return unless options[:instance]
- call_proc_for(options[:instance], fragment) or get
+ instance = evaluate_option(:instance, fragment, *args)
+
+ if instance === true # TODO: remove in 2.0.
+ warn "[Representable] `instance: lambda { true }` is deprecated. Apparently, you know what you're doing, so use `parse_strategy: :sync` instead."
+ return get
+ end
+
+ instance
+ end
+
+ def handle_deprecated_class(fragment) # TODO: remove in 2.0.
+ warn "[Representable] `class: lambda { nil }` is deprecated. To return the fragment from parsing, use `instance: lambda { |fragment, *args| fragment }` instead."
+ fragment
end
end
end
end