representable 1.5.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,59 +1,4 @@
1
1
  require 'representable/coercion'
2
2
 
3
- class Representable::Decorator
4
- module Coercion
5
- def self.included(base)
6
- base.class_eval do
7
- # DISCUSS: this assumes we have a Representer included, yet.
8
- alias_method :representable_initialize, :initialize
9
- alias_method :representable_to_hash, :to_hash
10
-
11
- # FIXME: allow including coercion only from virtus.
12
- include Virtus
13
- undef_method(:initialize)
14
- undef_method(:to_hash)
15
-
16
- extend Representable::Coercion::ClassMethods
17
- extend ClassMethods
18
-
19
- def initialize(*args) # override Virtus' #initialize.
20
- representable_initialize(*args)
21
- end
22
-
23
- def to_hash(*args) # override Virtus' #to_hash.
24
- representable_to_hash(*args)
25
- end
26
- end
27
- end
28
-
29
- module ClassMethods
30
- def property(name, options={})
31
- if options[:type]
32
- options[:decorator_scope] = true # call setter on decorator so coercion kicks in.
33
- create_writer(name)
34
- create_reader(name)
35
- end
36
-
37
- super # Representable::Coercion.
38
- end
39
-
40
- private
41
- # FIXME: dear @solnic, please make this better!
42
- def create_writer(name)
43
- # the call to super makes the actual coercion, which is then delegated to the represented instance.
44
- define_method "#{name}=" do |v|
45
- coerced_value = super(v).get(self)
46
- represented.send("#{name}=", coerced_value)
47
- end
48
- end
49
-
50
- def create_reader(name)
51
- # the call to super makes the actual coercion, which is then delegated to the represented instance.
52
- define_method "#{name}" do
53
- send("#{name}=", represented.send(name))
54
- super()
55
- end
56
- end
57
- end
58
- end
59
- end
3
+ # TODO: deprecate for 1.7.
4
+ Representable::Decorator::Coercion = Representable::Coercion
@@ -36,6 +36,7 @@ module Representable
36
36
  end
37
37
 
38
38
  def from
39
+ # TODO: deprecate :from.
39
40
  (options[:from] || options[:as] || name).to_s
40
41
  end
41
42
 
@@ -14,7 +14,7 @@ module Representable
14
14
  end
15
15
 
16
16
  # TODO: i hate monkey-patching Definition here since it globally adds this options. However, for now this should be ok :-)
17
- class Definition
17
+ Definition.class_eval do
18
18
  # Checks and returns if the property is writeable
19
19
  def writeable?
20
20
  return options[:writeable] if options.has_key?(:writeable)
@@ -18,6 +18,11 @@ module Representable
18
18
  def from_hash(*args, &block)
19
19
  create_represented(*args, &block).from_hash(*args)
20
20
  end
21
+
22
+ private
23
+ def representer_engine
24
+ Representable::Hash
25
+ end
21
26
  end
22
27
 
23
28
 
@@ -0,0 +1,38 @@
1
+ module Representable::Hash
2
+ module Collection
3
+ include Representable::Hash
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ include Representable
8
+ extend ClassMethods
9
+ end
10
+ end
11
+
12
+
13
+ module ClassMethods
14
+ def items(options)
15
+ collection :_self, options
16
+ end
17
+ end
18
+
19
+
20
+ def create_representation_with(doc, options, format)
21
+ bin = representable_mapper(format, options).bindings.first
22
+ bin.write(doc, represented)
23
+ end
24
+
25
+ def update_properties_from(doc, options, format)
26
+ bin = representable_mapper(format, options).bindings.first
27
+ value = bin.deserialize_from(doc)
28
+ represented.replace(value)
29
+ end
30
+
31
+ # FIXME: refactor Definition so we can simply add options in #items to existing definition.
32
+ def representable_attrs
33
+ attrs = super
34
+ attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
35
+ attrs
36
+ end
37
+ end
38
+ end
@@ -8,13 +8,13 @@ module Representable
8
8
  end
9
9
 
10
10
  def create_representation_with(doc, options, format)
11
- bin = representable_bindings_for(format, options).first
11
+ bin = representable_mapper(format, options).bindings.first
12
12
  hash = filter_keys_for(represented, options)
13
13
  bin.write(doc, hash)
14
14
  end
15
15
 
16
16
  def update_properties_from(doc, options, format)
17
- bin = representable_bindings_for(format, options).first
17
+ bin = representable_mapper(format, options).bindings.first
18
18
  hash = filter_keys_for(doc, options)
19
19
  value = bin.deserialize_from(hash)
20
20
  represented.replace(value)
@@ -21,6 +21,11 @@ module Representable
21
21
  def from_json(*args, &block)
22
22
  create_represented(*args, &block).from_json(*args)
23
23
  end
24
+
25
+ private
26
+ def representer_engine
27
+ Representable::JSON
28
+ end
24
29
  end
25
30
 
26
31
 
@@ -1,38 +1,13 @@
1
+ require 'representable/hash/collection'
2
+
1
3
  module Representable::JSON
2
4
  module Collection
3
5
  include Representable::JSON
4
6
 
5
7
  def self.included(base)
6
8
  base.class_eval do
7
- include Representable
8
- extend ClassMethods
9
- end
10
- end
11
-
12
-
13
- module ClassMethods
14
- def items(options)
15
- collection :_self, options
9
+ include Representable::Hash::Collection
16
10
  end
17
11
  end
18
-
19
-
20
- def create_representation_with(doc, options, format)
21
- bin = representable_bindings_for(format, options).first
22
- bin.serialize_for(represented)
23
- end
24
-
25
- def update_properties_from(doc, options, format)
26
- bin = representable_bindings_for(format, options).first
27
- value = bin.deserialize_from(doc)
28
- represented.replace(value)
29
- end
30
-
31
- # FIXME: refactor Definition so we can simply add options in #items to existing definition.
32
- def representable_attrs
33
- attrs = super
34
- attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
35
- attrs
36
- end
37
12
  end
38
13
  end
@@ -0,0 +1,78 @@
1
+ require 'representable/feature/readable_writeable'
2
+
3
+ module Representable
4
+ # Render and parse by looping over the representer's properties and dispatching to bindings.
5
+ # Conditionals are handled here, too.
6
+ class Mapper
7
+ # DISCUSS: we need @represented here for evaluating the :if blocks. this could be done in the bindings_for asset.
8
+ module Methods
9
+ def initialize(bindings, represented, options)
10
+ @represented = represented # the (extended) model.
11
+ @bindings = bindings
12
+ end
13
+
14
+ attr_reader :bindings
15
+
16
+ def deserialize(doc, options)
17
+ bindings.each do |bin|
18
+ deserialize_property(bin, doc, options)
19
+ end
20
+ represented
21
+ end
22
+
23
+ def serialize(doc, options)
24
+ bindings.each do |bin|
25
+ serialize_property(bin, doc, options)
26
+ end
27
+ doc
28
+ end
29
+
30
+ private
31
+ attr_reader :represented
32
+
33
+ def serialize_property(binding, doc, options)
34
+ return if skip_property?(binding, options)
35
+ compile_fragment(binding, doc)
36
+ end
37
+
38
+ def deserialize_property(binding, doc, options)
39
+ return if skip_property?(binding, options)
40
+ uncompile_fragment(binding, doc)
41
+ end
42
+
43
+ # Checks and returns if the property should be included.
44
+ def skip_property?(binding, options)
45
+ return true if skip_excluded_property?(binding, options) # no need for further evaluation when :exclude'ed
46
+
47
+ skip_conditional_property?(binding)
48
+ end
49
+
50
+ def skip_excluded_property?(binding, options)
51
+ return unless props = options[:exclude] || options[:include]
52
+ res = props.include?(binding.name.to_sym)
53
+ options[:include] ? !res : res
54
+ end
55
+
56
+ def skip_conditional_property?(binding)
57
+ # TODO: move to Binding.
58
+ return unless condition = binding.options[:if]
59
+
60
+ args = []
61
+ args << binding.user_options if condition.arity > 0 # TODO: remove arity check. users should know whether they pass options or not.
62
+
63
+ not represented.instance_exec(*args, &condition)
64
+ end
65
+
66
+ def compile_fragment(bin, doc)
67
+ bin.compile_fragment(doc)
68
+ end
69
+
70
+ def uncompile_fragment(bin, doc)
71
+ bin.uncompile_fragment(doc)
72
+ end
73
+ end
74
+
75
+ include Methods
76
+ include Feature::ReadableWriteable # DISCUSS: make this pluggable.
77
+ end
78
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.5.3"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -11,12 +11,12 @@ module Representable
11
11
  self.representation_wrap = true # let representable compute it.
12
12
  end
13
13
  end
14
-
15
-
14
+
15
+
16
16
  module ClassMethods
17
17
  # Creates a new Ruby object from XML using mapping information declared in the class.
18
18
  #
19
- # Accepts a block yielding the currently iterated Definition. If the block returns false
19
+ # Accepts a block yielding the currently iterated Definition. If the block returns false
20
20
  # the property is skipped.
21
21
  #
22
22
  # Example:
@@ -24,29 +24,34 @@ module Representable
24
24
  def from_xml(*args, &block)
25
25
  create_represented(*args, &block).from_xml(*args)
26
26
  end
27
-
27
+
28
28
  def from_node(*args, &block)
29
29
  create_represented(*args, &block).from_node(*args)
30
30
  end
31
+
32
+ private
33
+ def representer_engine
34
+ Representable::XML
35
+ end
31
36
  end
32
-
33
-
37
+
38
+
34
39
  def from_xml(doc, *args)
35
40
  node = Nokogiri::XML(doc).root
36
41
  from_node(node, *args)
37
42
  end
38
-
43
+
39
44
  def from_node(node, options={})
40
45
  update_properties_from(node, options, PropertyBinding)
41
46
  end
42
-
47
+
43
48
  # Returns a Nokogiri::XML object representing this object.
44
49
  def to_node(options={})
45
50
  root_tag = options[:wrap] || representation_wrap
46
-
51
+
47
52
  create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), options, PropertyBinding)
48
53
  end
49
-
54
+
50
55
  def to_xml(*args)
51
56
  to_node(*args).to_s
52
57
  end
@@ -1,38 +1,20 @@
1
+ require 'representable/hash/collection'
2
+
1
3
  module Representable::XML
2
4
  module Collection
3
5
  include Representable::XML
4
6
 
5
7
  def self.included(base)
6
8
  base.class_eval do
7
- include Representable
8
- extend ClassMethods
9
+ include Representable::Hash::Collection
10
+ include Methods
9
11
  end
10
12
  end
11
13
 
12
-
13
- module ClassMethods
14
- def items(options)
15
- collection :_self, options
14
+ module Methods
15
+ def update_properties_from(doc, *args)
16
+ super(doc.search("./*"), *args) # pass the list of collection items to Hash::Collection#update_properties_from.
16
17
  end
17
18
  end
18
-
19
-
20
- def create_representation_with(doc, options, format)
21
- bin = representable_bindings_for(format, options).first
22
- bin.write(doc, represented)
23
- end
24
-
25
- def update_properties_from(doc, options, format)
26
- bin = representable_bindings_for(format, options).first
27
- value = bin.deserialize_from(doc.search("./*")) # FIXME: use Binding#read.
28
- represented.replace(value)
29
- end
30
-
31
- # FIXME: refactor Definition so we can simply add options in #items to existing definition.
32
- def representable_attrs
33
- attrs = super
34
- attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
35
- attrs
36
- end
37
19
  end
38
20
  end
@@ -4,7 +4,7 @@ require 'representable/bindings/yaml_bindings'
4
4
  module Representable
5
5
  module YAML
6
6
  include Hash
7
-
7
+
8
8
  def self.included(base)
9
9
  base.class_eval do
10
10
  include Representable
@@ -12,12 +12,12 @@ module Representable
12
12
  #self.representation_wrap = true # let representable compute it.
13
13
  end
14
14
  end
15
-
16
-
15
+
16
+
17
17
  module ClassMethods
18
18
  # Creates a new Ruby object from XML using mapping information declared in the class.
19
19
  #
20
- # Accepts a block yielding the currently iterated Definition. If the block returns false
20
+ # Accepts a block yielding the currently iterated Definition. If the block returns false
21
21
  # the property is skipped.
22
22
  #
23
23
  # Example:
@@ -25,27 +25,32 @@ module Representable
25
25
  def from_yaml(*args, &block)
26
26
  create_represented(*args, &block).from_yaml(*args)
27
27
  end
28
+
29
+ private
30
+ def representer_engine
31
+ Representable::YAML
32
+ end
28
33
  end
29
-
30
-
34
+
35
+
31
36
  def from_yaml(doc, options={})
32
37
  hash = Psych.load(doc)
33
38
  from_hash(hash, options, PropertyBinding)
34
39
  end
35
-
40
+
36
41
  # Returns a Nokogiri::XML object representing this object.
37
42
  def to_ast(options={})
38
43
  #root_tag = options[:wrap] || representation_wrap
39
-
44
+
40
45
  Psych::Nodes::Mapping.new.tap do |map|
41
46
  create_representation_with(map, options, PropertyBinding)
42
47
  end
43
48
  end
44
-
49
+
45
50
  def to_yaml(*args)
46
- stream = Psych::Nodes::Stream.new
51
+ stream = Psych::Nodes::Stream.new
47
52
  stream.children << doc = Psych::Nodes::Document.new
48
-
53
+
49
54
  doc.children << to_ast(*args)
50
55
  stream.to_yaml
51
56
  end