require "representable/populator" require "representable/deserializer" require "representable/serializer" module Representable # The Binding wraps the Definition instance for this property and provides methods to read/write fragments. # The flow when parsing is Binding#read_fragment -> Populator -> Deserializer. # Actual parsing the fragment from the document happens in Binding#read, everything after that is generic. # # Serialization: Serializer -> {frag}/[frag]/frag -> Binding#write class Binding class FragmentNotFound end def self.build(definition, *args) # DISCUSS: move #create_binding to this class? return definition.create_binding(*args) if definition[:binding] build_for(definition, *args) end def initialize(definition, represented, decorator, user_options={}) # TODO: remove default arg for user options. @definition = definition setup!(represented, decorator, user_options) # this can be used in #compile_fragment/#uncompile_fragment in case we wanna reuse the Binding instance. 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) evaluate_option(:writer, doc) do value = render_filter(get, doc) write_fragment(doc, value) end end # Parse value from doc and update the model property. def uncompile_fragment(doc) evaluate_option(:reader, doc) do read_fragment(doc) end end def write_fragment(doc, value) value = default_for(value) return if skipable_empty_value?(value) render_fragment(value, doc) end def render_fragment(value, doc) fragment = serialize(value) # render fragments of hash, xml, yaml. write(doc, fragment) end def read_fragment(doc) fragment = read(doc) # scalar, Array, or Hash (abstract format) or un-deserialised fragment(s). populator.call(fragment, doc) end def render_filter(value, doc) evaluate_option(:render_filter, value, doc) { value } end def parse_filter(value, doc) evaluate_option(:parse_filter, value, doc) { value } end def get evaluate_option(:getter) do exec_context.send(getter) end end def set(value) evaluate_option(:setter, value) do exec_context.send(setter, value) end end # 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 # 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<