Class: Representable::Binding

Inherits:
Object
  • Object
show all
Defined in:
lib/representable/binding.rb

Overview

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 -> href="http://frag">frag//frag -> Binding#write

Direct Known Subclasses

Hash::Binding, XML::Binding

Defined Under Namespace

Modules: Collection, Hash Classes: FragmentNotFound, Options

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(definition, represented, decorator, user_options = {}) ⇒ Binding

TODO: remove default arg for user options.



22
23
24
25
26
# File 'lib/representable/binding.rb', line 22

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

Instance Attribute Details

#representedObject (readonly)

TODO: make private/remove.



28
29
30
# File 'lib/representable/binding.rb', line 28

def represented
  @represented
end

#user_optionsObject (readonly)

TODO: make private/remove.



28
29
30
# File 'lib/representable/binding.rb', line 28

def user_options
  @user_options
end

Class Method Details

.build(definition, *args) ⇒ Object



16
17
18
19
20
# File 'lib/representable/binding.rb', line 16

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

Instance Method Details

#[](name) ⇒ Object



110
111
112
# File 'lib/representable/binding.rb', line 110

def [](name)
  @definition[name]
end

#array?Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/representable/binding.rb', line 154

def array?
  @definition.array?
end

#asObject

DISCUSS: private?



30
31
32
# File 'lib/representable/binding.rb', line 30

def as # DISCUSS: private?
  @as ||= evaluate_option(:as)
end

#compile_fragment(doc) ⇒ Object

Retrieve value and write fragment to the doc.



35
36
37
38
39
40
# File 'lib/representable/binding.rb', line 35

def compile_fragment(doc)
  evaluate_option(:writer, doc) do
    value = render_filter(get, doc)
    write_fragment(doc, value)
  end
end

#default_for(value) ⇒ Object



149
150
151
152
# File 'lib/representable/binding.rb', line 149

def default_for(value)
  return self[:default] if skipable_empty_value?(value)
  value
end

#evaluate_option(name, *args) ⇒ Object

Evaluate the option (either nil, static, a block or an instance method call) or executes passed block when option not defined.



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/representable/binding.rb', line 98

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

#getObject



79
80
81
82
83
# File 'lib/representable/binding.rb', line 79

def get
  evaluate_option(:getter) do
    exec_context.send(getter)
  end
end

#getterObject

TODO: i don't want to define all methods here, but it is faster! TODO: test public interface.



115
116
117
# File 'lib/representable/binding.rb', line 115

def getter
  @definition.getter
end

#has_default?(*args) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/representable/binding.rb', line 127

def has_default?(*args)
  @definition.has_default?(*args)
end

#nameObject



130
131
132
# File 'lib/representable/binding.rb', line 130

def name
  @definition.name
end

#parse_filter(value, doc) ⇒ Object



74
75
76
# File 'lib/representable/binding.rb', line 74

def parse_filter(value, doc)
  evaluate_option(:parse_filter, value, doc) { value }
end

#read_fragment(doc) ⇒ Object



64
65
66
67
68
# File 'lib/representable/binding.rb', line 64

def read_fragment(doc)
  fragment = read(doc) # scalar, Array, or Hash (abstract format) or un-deserialised fragment(s).

  populator.call(fragment, doc)
end

#render_filter(value, doc) ⇒ Object



70
71
72
# File 'lib/representable/binding.rb', line 70

def render_filter(value, doc)
  evaluate_option(:render_filter, value, doc) { value }
end

#render_fragment(value, doc) ⇒ Object



57
58
59
60
61
62
# File 'lib/representable/binding.rb', line 57

def render_fragment(value, doc)
  # DISCUSS: should we return a Skip object instead of this block trick? (same in Populator?)
  fragment = serialize(value) { return } # render fragments of hash, xml, yaml.

  write(doc, fragment)
end

#representable?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/representable/binding.rb', line 124

def representable?
  @definition.representable?
end

#representer_moduleObject



133
134
135
# File 'lib/representable/binding.rb', line 133

def representer_module
  @definition.representer_module
end

#representer_module_for(object, *args) ⇒ Object

DISCUSS: do we really need that?



92
93
94
# File 'lib/representable/binding.rb', line 92

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

#set(value) ⇒ Object



85
86
87
88
89
# File 'lib/representable/binding.rb', line 85

def set(value)
  evaluate_option(:setter, value) do
    exec_context.send(setter, value)
  end
end

#setterObject



118
119
120
# File 'lib/representable/binding.rb', line 118

def setter
  @definition.setter
end

#skipable_empty_value?(value) ⇒ Boolean

perf : 1.7-1.9 extend Forwardable def_delegators :@definition, *%w([] getter setter typed? representable? has_default? name representer_module) perf : 1.7-1.9 %w([] getter setter typed? representable? has_default? name representer_module).each do |name|

define_method(name.to_sym) { |*args| @definition.send(name, *args) }

end

Returns:

  • (Boolean)


144
145
146
147
# File 'lib/representable/binding.rb', line 144

def skipable_empty_value?(value)
  return true if array? and self[:render_empty] == false and value and value.size == 0  # TODO: change in 2.0, don't render emtpy.
  value.nil? and not self[:render_nil]
end

#typed?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/representable/binding.rb', line 121

def typed?
  @definition.typed?
end

#uncompile_fragment(doc) ⇒ Object

Parse value from doc and update the model property.



43
44
45
46
47
# File 'lib/representable/binding.rb', line 43

def uncompile_fragment(doc)
  evaluate_option(:reader, doc) do
    read_fragment(doc)
  end
end

#write_fragment(doc, value) ⇒ Object



49
50
51
52
53
54
55
# File 'lib/representable/binding.rb', line 49

def write_fragment(doc, value)
  value = default_for(value)

  return if skipable_empty_value?(value)

  render_fragment(value, doc)
end