representable 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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