representable 0.9.0 → 0.9.1

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.
@@ -0,0 +1,3 @@
1
+ rvm: 1.9.2
2
+ notifications:
3
+ irc: "irc.freenode.org#cells"
@@ -1,3 +1,8 @@
1
+ h2. 0.9.1
2
+
3
+ * Extracted common serialization into @Representable#create_representation_with@ and deserialization into @#update_properties_from@.
4
+ * Both serialization and deserialization now accept a block to make them skip elements while iterating the property definitions.
5
+
1
6
  h2. 0.9.0
2
7
 
3
8
  h3. Changes
@@ -1,5 +1,4 @@
1
1
  require 'hooks/inheritable_attribute'
2
-
3
2
  require 'representable/definition'
4
3
  require 'representable/nokogiri_extensions'
5
4
 
@@ -17,13 +16,41 @@ module Representable
17
16
  end
18
17
  end
19
18
 
19
+ # Reads values from +doc+ and sets properties accordingly.
20
+ def update_properties_from(doc)
21
+ self.class.representable_bindings.each do |ref|
22
+ next if block_given? and not yield ref # skip if block is false. # DISCUSS: will we keep that?
23
+
24
+ value = ref.read(doc)
25
+ send(ref.definition.setter, value)
26
+ end
27
+ self
28
+ end
29
+
30
+ private
31
+ # Compiles the document going through all properties.
32
+ def create_representation_with(doc)
33
+ self.class.representable_bindings.each do |ref|
34
+ next if block_given? and not yield ref # skip if block is false. # DISCUSS: will we keep that?
35
+
36
+ value = public_send(ref.definition.getter) # DISCUSS: eventually move back to Ref.
37
+ ref.write(doc, value) if value
38
+ end
39
+ doc
40
+ end
41
+
42
+
20
43
  module ClassMethods # :nodoc:
21
44
  module Declarations
22
45
  def definition_class
23
46
  Definition
24
47
  end
25
48
 
26
-
49
+ # Returns bindings for all properties.
50
+ def representable_bindings
51
+ representable_attrs.map {|attr| binding_for_definition(attr) }
52
+ end
53
+
27
54
  # Declares a reference to a certain xml element, whether an attribute, a node,
28
55
  # or a typed collection of nodes. This method does not add a corresponding accessor
29
56
  # to the object. For that behavior see the similar methods: .xml_reader and .xml_accessor.
@@ -183,9 +210,9 @@ module Representable
183
210
  # [:to_xml] this proc is applied to the attributes value outputting the instance via #to_xml
184
211
  #
185
212
  def representable_property(*args) # TODO: make it accept 1-n props.
186
- attr = representable_attr(*args)
187
- add_reader(attr)
188
- attr_writer(attr.accessor)
213
+ attr = add_representable_property(*args)
214
+ attr_reader(attr.getter)
215
+ attr_writer(attr.getter)
189
216
  end
190
217
 
191
218
  def representable_collection(name, options={})
@@ -194,23 +221,11 @@ module Representable
194
221
  end
195
222
 
196
223
  private
197
- def representable_attr(name, options={})
198
- definition_class.new(name, options).tap do |attr|
224
+ def add_representable_property(*args)
225
+ definition_class.new(*args).tap do |attr|
199
226
  representable_attrs << attr
200
227
  end
201
228
  end
202
-
203
- def representable_reader(*syms, &block)
204
- representable_attr(*syms, &block).each do |attr|
205
- add_reader(attr)
206
- end
207
- end
208
-
209
- def add_reader(attr)
210
- define_method(attr.accessor) do
211
- instance_variable_get(attr.instance_variable_name)
212
- end
213
- end
214
229
  end
215
230
 
216
231
  module Accessors
@@ -2,13 +2,12 @@ module Representable
2
2
  module JSON
3
3
  class Binding
4
4
  attr_reader :definition
5
- #delegate :required?, :array?, :accessor, :from, :to => :definition
6
5
 
7
6
  def initialize(definition)
8
7
  @definition = definition
9
8
  end
10
9
 
11
- def value_in(hash)
10
+ def read(hash)
12
11
  value_from_hash(hash) or default
13
12
  end
14
13
 
@@ -29,7 +28,7 @@ module Representable
29
28
 
30
29
  # Represents plain key-value.
31
30
  class TextBinding < Binding
32
- def update_json(hash, value)
31
+ def write(hash, value)
33
32
  hash[definition.from] = value
34
33
  end
35
34
 
@@ -43,9 +42,7 @@ module Representable
43
42
 
44
43
  # Represents a tag with object binding.
45
44
  class ObjectBinding < Binding
46
- #delegate :sought_type, :to => :definition
47
-
48
- def update_json(hash, value)
45
+ def write(hash, value)
49
46
  if definition.array?
50
47
  hash.merge! ({definition.from => value.collect {|v| v.to_hash(:wrap => false)}}) # hier name=> wech.
51
48
  else
@@ -58,9 +55,9 @@ module Representable
58
55
  []
59
56
  end
60
57
 
61
- def value_from_hash(xml)
62
- collect_for(xml) do |node|
63
- definition.sought_type.from_json(node, :wrap => false) # hier name=> wech.
58
+ def value_from_hash(hash)
59
+ collect_for(hash) do |node|
60
+ definition.sought_type.from_hash(node) # call #from_hash as it's already deserialized.
64
61
  end
65
62
  end
66
63
 
@@ -7,7 +7,7 @@ module Representable
7
7
  @definition = definition
8
8
  end
9
9
 
10
- def value_in(xml)
10
+ def read(xml)
11
11
  xml = Nokogiri::XML::Node.from(xml) or return default
12
12
 
13
13
  value_from_node(xml) or default
@@ -43,7 +43,7 @@ module Representable
43
43
 
44
44
  # Represents a tag attribute.
45
45
  class AttributeBinding < Binding
46
- def update_xml(xml, values)
46
+ def write(xml, values)
47
47
  wrap(xml).tap do |xml|
48
48
  xml[definition.from] = values.to_s
49
49
  end
@@ -60,7 +60,7 @@ module Representable
60
60
  class TextBinding < Binding
61
61
  # Updates the text in the given _xml_ block to
62
62
  # the _value_ provided.
63
- def update_xml(xml, value)
63
+ def write(xml, value)
64
64
  wrap(xml).tap do |xml|
65
65
  if definition.content?
66
66
  add(xml, value)
@@ -92,12 +92,12 @@ module Representable
92
92
  # Represents a tag with object binding.
93
93
  class ObjectBinding < Binding
94
94
  # Adds the ref's markup to +xml+.
95
- def update_xml(xml, value)
95
+ def write(xml, value)
96
96
  wrap(xml).tap do |xml|
97
97
  if definition.array?
98
- update_xml_for_collection(xml, value)
98
+ write_collection(xml, value)
99
99
  else
100
- update_xml_for_entity(xml, value)
100
+ write_entity(xml, value)
101
101
  end
102
102
  end
103
103
  end
@@ -122,13 +122,13 @@ module Representable
122
122
  end
123
123
  end
124
124
 
125
- def update_xml_for_collection(xml, collection)
125
+ def write_collection(xml, collection)
126
126
  collection.each do |item|
127
- update_xml_for_entity(xml, item)
127
+ write_entity(xml, item)
128
128
  end
129
129
  end
130
130
 
131
- def update_xml_for_entity(xml, entity)
131
+ def write_entity(xml, entity)
132
132
  xml.add_child(serialize(entity))
133
133
  end
134
134
  end
@@ -1,10 +1,10 @@
1
1
  module Representable
2
2
  class Definition # :nodoc:
3
- attr_reader :name, :sought_type, :wrapper, :accessor, :from
4
-
3
+ attr_reader :name, :sought_type, :wrapper, :from
4
+ alias_method :getter, :name
5
5
 
6
6
  def initialize(sym, opts={})
7
- @accessor = @name = sym.to_s
7
+ @name = sym.to_s
8
8
 
9
9
  @array = true if opts[:as].is_a?(Array) # DISCUSS: move to ArrayDefinition.
10
10
  @from = (opts[:from] || name).to_s
@@ -25,11 +25,11 @@ module Representable
25
25
  end
26
26
 
27
27
  def instance_variable_name
28
- :"@#{accessor}"
28
+ :"@#{name}"
29
29
  end
30
30
 
31
31
  def setter
32
- :"#{accessor}="
32
+ :"#{name}="
33
33
  end
34
34
 
35
35
  def typed?
@@ -6,39 +6,34 @@ module Representable
6
6
  BINDING_FOR_TYPE = { # TODO: refactor #representable_accessor for better extendability.
7
7
  :text => TextBinding,
8
8
  }
9
- def self.binding_for_definition(definition)
10
- (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
11
- end
12
9
 
13
10
  def self.included(base)
14
11
  base.class_eval do
15
12
  include Representable
16
- include InstanceMethods
13
+ extend ClassMethods
17
14
  end
18
- base.extend ClassMethods # DISCUSS: do that dynamically?
19
15
  end
20
16
 
17
+
21
18
  module ClassMethods
19
+ def binding_for_definition(definition)
20
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
21
+ end
22
+
22
23
  # Creates a new Ruby object from XML using mapping information declared in the class.
23
24
  #
24
25
  # Example:
25
26
  # book = Book.from_xml("<book><name>Beyond Java</name></book>")
26
- def from_json(data, options={})
27
- # DISCUSS: extract #from_json call in Bindings to this place.
28
- data = ::JSON[data] if data.is_a?(String) # DISCUSS: #from_json sometimes receives a string (in nestings).
29
- data ||= {} # DISCUSS: is this needed?
30
- data = data[representation_name.to_s] unless options[:wrap] == false
31
- data ||= {} # FIXME: should we fail here? generate a warning?
32
-
33
-
34
- create_from_json.tap do |inst|
35
- refs = representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
36
-
37
- refs.each do |ref|
38
- value = ref.value_in(data)
39
-
40
- inst.send(ref.definition.setter, value)
41
- end
27
+ # DISCUSS: assumes shitty wrapping like :article => {:name => ...}
28
+ def from_json(data, options={}, &block)
29
+ create_from_json.tap do |object|
30
+ object.from_json(data, &block)
31
+ end
32
+ end
33
+
34
+ def from_hash(data)
35
+ create_from_json.tap do |object|
36
+ object.update_properties_from(data)
42
37
  end
43
38
  end
44
39
 
@@ -48,25 +43,24 @@ module Representable
48
43
  end
49
44
  end
50
45
 
51
- module InstanceMethods # :nodoc:
52
- def to_hash(options={})
53
- hash = {}.tap do |attrs|
54
- refs = self.class.representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
55
-
56
- refs.each do |ref|
57
- value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
58
- ref.update_json(attrs, value) if value
59
- end
60
- end
61
-
62
- # DISCUSS: where to wrap?
63
- options[:wrap] == false ? hash : {self.class.representation_name => hash}
64
- end
46
+ def from_json(data, options={}, &block)
47
+ data = ::JSON[data]
48
+ data = data[self.class.representation_name.to_s] unless options[:wrap] == false
49
+ data ||= {} # FIXME: should we fail here? generate a warning?
65
50
 
66
- # Returns a Nokogiri::XML object representing this object.
67
- def to_json(options={})
68
- to_hash(options).to_json
69
- end
51
+ update_properties_from(data, &block)
52
+ end
53
+
54
+ def to_hash(options={})
55
+ hash = create_representation_with({})
56
+
57
+ # DISCUSS: where to wrap?
58
+ options[:wrap] == false ? hash : {self.class.representation_name => hash}
59
+ end
60
+
61
+ # Returns a JSON string representing this object.
62
+ def to_json(options={})
63
+ to_hash(options).to_json
70
64
  end
71
65
  end
72
66
  end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "0.9.0"
2
+ VERSION = "0.9.1"
3
3
  end
@@ -8,42 +8,41 @@ module Representable
8
8
  :text => TextBinding,
9
9
  }
10
10
 
11
- def self.binding_for_definition(definition)
12
- (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
13
- end
14
-
15
11
  def self.included(base)
16
12
  base.class_eval do
17
13
  include Representable
18
- include InstanceMethods
19
14
  extend ClassMethods
15
+ alias_method :from_xml, :update_properties_from
20
16
  end
21
17
  end
22
18
 
19
+
23
20
  class Definition < Representable::Definition
24
21
  # FIXME: extract xml-specific from Definition.
25
22
  end
26
23
 
27
- module ClassMethods
28
- def definition_class
29
- Definition
30
- end
31
24
 
25
+ module ClassMethods
26
+ def definition_class
27
+ Definition
28
+ end
29
+
30
+ def binding_for_definition(definition)
31
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
32
+ end
33
+
32
34
  # Creates a new Ruby object from XML using mapping information declared in the class.
33
35
  #
36
+ # Accepts a block yielding the currently iterated Definition. If the block returns false
37
+ # the property is skipped.
38
+ #
34
39
  # Example:
35
- # book = Book.from_xml("<book><name>Beyond Java</name></book>")
40
+ # band.from_xml("<band><name>Nofx</name></band>")
36
41
  def from_xml(data, *args)
37
42
  xml = Nokogiri::XML::Node.from(data)
38
43
 
39
- create_from_xml(*args).tap do |inst|
40
- refs = representable_attrs.map {|attr| XML.binding_for_definition(attr) }
41
-
42
- refs.each do |ref|
43
- value = ref.value_in(xml)
44
-
45
- inst.send(ref.definition.setter, value)
46
- end
44
+ create_from_xml(*args).tap do |object|
45
+ object.update_properties_from(xml)
47
46
  end
48
47
  end
49
48
 
@@ -53,20 +52,11 @@ module Representable
53
52
  end
54
53
  end
55
54
 
56
- module InstanceMethods # :nodoc:
57
- # Returns a Nokogiri::XML object representing this object.
58
- def to_xml(params={})
59
- params[:name] ||= self.class.representation_name
60
-
61
- Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
62
- refs = self.class.representable_attrs.map {|attr| XML.binding_for_definition(attr) }
63
-
64
- refs.each do |ref|
65
- value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
66
- ref.update_xml(root, value) if value
67
- end
68
- end
69
- end
55
+ # Returns a Nokogiri::XML object representing this object.
56
+ def to_xml(params={})
57
+ root_tag = params[:name] || self.class.representation_name
58
+
59
+ create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new))
70
60
  end
71
- end # Xml
61
+ end
72
62
  end
@@ -24,6 +24,6 @@ Gem::Specification.new do |s|
24
24
  s.add_dependency "json"
25
25
 
26
26
  s.add_development_dependency "rake"
27
- s.add_development_dependency "rspec"
28
27
  s.add_development_dependency "test_xml"
28
+ s.add_development_dependency "minitest", "~>2.8"
29
29
  end
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class ReferenceTest < MiniTest::Spec
3
+ class BindingsTest < MiniTest::Spec
4
4
  describe "ObjectRef with []" do
5
5
  before do
6
6
  @ref = Representable::XML::ObjectBinding.new(Representable::Definition.new(:songs, :as => [Hash]))
@@ -12,7 +12,7 @@ class ReferenceTest < MiniTest::Spec
12
12
  end
13
13
 
14
14
 
15
- describe "TextRef#value_in" do
15
+ describe "TextRef#read" do
16
16
  def parse_xml(xml); Nokogiri::XML::Node.from(xml); end
17
17
 
18
18
  before do
@@ -20,95 +20,8 @@ class ReferenceTest < MiniTest::Spec
20
20
  end
21
21
 
22
22
  it "returns found value" do
23
- assert_equal "Unkoil", @ref.value_in(parse_xml("<a><song>Unkoil</song></a>"))
23
+ assert_equal "Unkoil", @ref.read(parse_xml("<a><song>Unkoil</song></a>"))
24
24
  end
25
25
  end
26
26
  end
27
27
 
28
- class DefinitionTest < MiniTest::Spec
29
- describe "generic API" do
30
- before do
31
- @def = Representable::Definition.new(:songs)
32
- end
33
-
34
- it "responds to #typed?" do
35
- assert ! @def.typed?
36
- assert Representable::Definition.new(:songs, :as => Hash).typed?
37
- assert Representable::Definition.new(:songs, :as => [Hash]).typed?
38
- end
39
-
40
- it "responds to #accessor" do
41
- assert_equal "songs", @def.accessor
42
- end
43
-
44
- it "responds to #name" do
45
- assert_equal "songs", @def.name
46
- end
47
-
48
- it "responds to #instance_variable_name" do
49
- assert_equal :"@songs", @def.instance_variable_name
50
- end
51
-
52
- it "responds to #setter" do
53
- assert_equal :"songs=", @def.setter
54
- end
55
-
56
- it "responds to #sought_type" do
57
- assert_equal :text, @def.sought_type
58
- end
59
- end
60
-
61
-
62
- describe "#apply" do
63
- it "works with a single item" do
64
- @d = Representable::Definition.new(:song)
65
- assert_equal 2, @d.apply(1) { |v| v+1 }
66
- end
67
-
68
- it "works with collection" do
69
- @d = Representable::Definition.new(:song, :as => [])
70
- assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
71
- end
72
-
73
- it "skips with collection and nil" do
74
- @d = Representable::Definition.new(:song, :as => [])
75
- assert_equal nil, @d.apply(nil) { |v| v+1 }
76
- end
77
- end
78
-
79
- describe ":as => []" do
80
- before do
81
- @def = Representable::Definition.new(:songs, :as => [], :tag => :song)
82
- end
83
-
84
- it "responds to #array?" do
85
- assert @def.array?
86
- end
87
-
88
- it "responds to #sought_type" do
89
- assert_equal :text, @def.sought_type
90
- end
91
- end
92
-
93
-
94
- describe ":as => [Item]" do
95
- before do
96
- @def = Representable::Definition.new(:songs, :as => [Hash])
97
- end
98
-
99
- it "responds to #sought_type" do
100
- assert_equal Hash, @def.sought_type
101
- end
102
- end
103
-
104
-
105
- describe ":as => Item" do
106
- before do
107
- @def = Representable::Definition.new(:songs, :as => Hash)
108
- end
109
-
110
- it "responds to #sought_type" do
111
- assert_equal Hash, @def.sought_type
112
- end
113
- end
114
- end
@@ -0,0 +1,89 @@
1
+ require 'test_helper'
2
+
3
+ class DefinitionTest < MiniTest::Spec
4
+ describe "generic API" do
5
+ before do
6
+ @def = Representable::Definition.new(:songs)
7
+ end
8
+
9
+ it "responds to #typed?" do
10
+ assert ! @def.typed?
11
+ assert Representable::Definition.new(:songs, :as => Hash).typed?
12
+ assert Representable::Definition.new(:songs, :as => [Hash]).typed?
13
+ end
14
+
15
+ it "responds to #getter and returns string" do
16
+ assert_equal "songs", @def.getter
17
+ end
18
+
19
+ it "responds to #name" do
20
+ assert_equal "songs", @def.name
21
+ end
22
+
23
+ it "responds to #instance_variable_name" do
24
+ assert_equal :"@songs", @def.instance_variable_name
25
+ end
26
+
27
+ it "responds to #setter" do
28
+ assert_equal :"songs=", @def.setter
29
+ end
30
+
31
+ it "responds to #sought_type" do
32
+ assert_equal :text, @def.sought_type
33
+ end
34
+ end
35
+
36
+
37
+ describe "#apply" do
38
+ it "works with a single item" do
39
+ @d = Representable::Definition.new(:song)
40
+ assert_equal 2, @d.apply(1) { |v| v+1 }
41
+ end
42
+
43
+ it "works with collection" do
44
+ @d = Representable::Definition.new(:song, :as => [])
45
+ assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
46
+ end
47
+
48
+ it "skips with collection and nil" do
49
+ @d = Representable::Definition.new(:song, :as => [])
50
+ assert_equal nil, @d.apply(nil) { |v| v+1 }
51
+ end
52
+ end
53
+
54
+ describe ":as => []" do
55
+ before do
56
+ @def = Representable::Definition.new(:songs, :as => [], :tag => :song)
57
+ end
58
+
59
+ it "responds to #array?" do
60
+ assert @def.array?
61
+ end
62
+
63
+ it "responds to #sought_type" do
64
+ assert_equal :text, @def.sought_type
65
+ end
66
+ end
67
+
68
+
69
+ describe ":as => [Item]" do
70
+ before do
71
+ @def = Representable::Definition.new(:songs, :as => [Hash])
72
+ end
73
+
74
+ it "responds to #sought_type" do
75
+ assert_equal Hash, @def.sought_type
76
+ end
77
+ end
78
+
79
+
80
+ describe ":as => Item" do
81
+ before do
82
+ @def = Representable::Definition.new(:songs, :as => Hash)
83
+ end
84
+
85
+ it "responds to #sought_type" do
86
+ assert_equal Hash, @def.sought_type
87
+ end
88
+ end
89
+ end
@@ -7,21 +7,74 @@ module JsonTest
7
7
  Def = Representable::Definition
8
8
 
9
9
  describe "JSON module" do
10
+ before do
11
+ @Band = Class.new(Band) do
12
+ self.representation_name= :band
13
+ representable_property :label
14
+ end
15
+ end
16
+
17
+ class Band
18
+ include Representable::JSON
19
+ representable_property :name
20
+ end
21
+
10
22
  describe "#binding_for_definition" do
11
23
  it "returns ObjectBinding" do
12
- assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :as => Hash))
24
+ assert_kind_of Json::ObjectBinding, Band.binding_for_definition(Def.new(:band, :as => Hash))
13
25
  end
14
26
 
15
27
  it "returns TextBinding" do
16
- assert_kind_of Json::TextBinding, Json.binding_for_definition(Def.new(:band))
28
+ assert_kind_of Json::TextBinding, Band.binding_for_definition(Def.new(:band))
29
+ end
30
+ end
31
+
32
+ describe "#representable_bindings" do
33
+ it "returns bindings for each property" do
34
+ assert_equal 1, Band.representable_bindings.size
35
+ assert_equal "name", Band.representable_bindings.first.definition.name
17
36
  end
18
37
  end
19
38
 
20
39
  describe "#from_json" do
21
- class Band
22
- include Json
40
+ before do
41
+ @band = @Band.new
42
+ end
43
+
44
+ it "accepts json string" do
45
+ @band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json)
46
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
23
47
  end
24
48
 
49
+ it "forwards block to #update_properties_from" do
50
+ @band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json) do |binding|
51
+ binding.definition.name == "name"
52
+ end
53
+
54
+ assert_equal ["Nofx", nil], [@band.name, @band.label]
55
+ end
56
+
57
+ it "accepts wrapped properties" do
58
+ band = Band.new
59
+ band.from_json({:band => {:name => "This Is A Standoff"}}.to_json)
60
+ assert_equal "This Is A Standoff", band.name
61
+ end
62
+
63
+ it "accepts unwrapped properties using the :wrap option" do # DISCUSS: should be default.
64
+ band = Band.new
65
+ band.from_json({name: "This Is A Standoff"}.to_json, :wrap => false)
66
+ assert_equal "This Is A Standoff", band.name
67
+ end
68
+ end
69
+
70
+
71
+ describe ".from_json" do
72
+ it "delegates to #from_json after object conception" do
73
+ band = @Band.from_json({band: {name: "Nofx", label: "NOFX"}}.to_json) do |binding| binding.definition.name == "name" end
74
+ assert_equal ["Nofx", nil], [band.name, band.label]
75
+ end
76
+
77
+ # TODO: move following tests to #from_json test.
25
78
  it "raises error with emtpy string" do
26
79
  assert_raises JSON::ParserError do
27
80
  Band.from_json("")
@@ -33,10 +86,15 @@ module JsonTest
33
86
  end
34
87
 
35
88
  it "generates warning with inappropriate hash in debugging mode" do
36
-
37
89
  end
38
90
  end
39
91
 
92
+ describe ".from_hash" do
93
+ it "accepts unwrapped hash with string keys" do
94
+ band = Band.from_hash("name" => "Bombshell Rocks")
95
+ assert_equal "Bombshell Rocks", band.name
96
+ end
97
+ end
40
98
  end
41
99
  end
42
100
 
@@ -52,11 +110,6 @@ module JsonTest
52
110
  assert_equal "Bombshell Rocks", band.name
53
111
  end
54
112
 
55
- it "#from_json accepts hash, too" do
56
- band = Band.from_json({"band" => {"name" => "Bombshell Rocks"}})
57
- assert_equal "Bombshell Rocks", band.name
58
- end
59
-
60
113
  it "#to_json serializes correctly" do
61
114
  band = Band.new
62
115
  band.name = "Cigar"
@@ -77,8 +130,8 @@ module JsonTest
77
130
  end
78
131
 
79
132
  it "#from_json creates one Item instance" do
80
- album = Album.from_json({:album => {:label => "Fat Wreck"}}.to_json)
81
- assert_equal "Bad Religion", album.label.name
133
+ album = Album.from_json('{"album":{"label":{"name":"Fat Wreck"}}}')
134
+ assert_equal "Fat Wreck", album.label.name
82
135
  end
83
136
 
84
137
  it "#to_json serializes" do
@@ -25,7 +25,6 @@ class RepresentableTest < MiniTest::Spec
25
25
  end
26
26
 
27
27
 
28
-
29
28
  describe "#representable_property" do
30
29
  it "creates accessors for the attribute" do
31
30
  @band = PunkBand.new
@@ -87,6 +86,53 @@ class RepresentableTest < MiniTest::Spec
87
86
  it "returns Definition class" do
88
87
  assert_equal Representable::Definition, Band.definition_class
89
88
  end
89
+ end
90
+
91
+
92
+ require 'representable/json' # DISCUSS: i don't like the JSON requirement here, what about some generic test module?
93
+ class PopBand
94
+ include Representable::JSON
95
+ representable_property :name
96
+ representable_property :groupies
97
+ end
98
+
99
+ describe "#update_properties_from" do
100
+ it "copies values from document to object" do
101
+ band = PopBand.new
102
+ band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2})
103
+ assert_equal "No One's Choice", band.name
104
+ assert_equal 2, band.groupies
105
+ end
106
+
107
+ it "skips elements when block returns false" do
108
+ band = PopBand.new
109
+ band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}) do |binding|
110
+ binding.definition.name == "name"
111
+ end
112
+ assert_equal "No One's Choice", band.name
113
+ assert_equal nil, band.groupies
114
+ end
115
+
116
+ it "always returns self" do
117
+ band = PopBand.new
118
+ assert_equal band, band.update_properties_from({"name"=>"Nofx"})
119
+ end
120
+ end
121
+
122
+ describe "#create_representation_with" do
123
+ before do
124
+ @band = PopBand.new
125
+ @band.name = "No One's Choice"
126
+ @band.groupies = 2
127
+ end
128
+
129
+ it "compiles document from properties in object" do
130
+ assert_equal({"name"=>"No One's Choice", "groupies"=>2}, @band.send(:create_representation_with, {}))
131
+ end
90
132
 
133
+ it "skips elements when block returns false" do
134
+ assert_equal({"name"=>"No One's Choice"}, @band.send(:create_representation_with, {}) do |binding| binding.definition.name == "name" end)
135
+ end
91
136
  end
137
+
92
138
  end
@@ -22,6 +22,15 @@ class XmlTest < MiniTest::Spec
22
22
  Def = Representable::Definition
23
23
 
24
24
  describe "Xml module" do
25
+ before do
26
+ @Band = Class.new do
27
+ include Representable::XML
28
+ self.representation_name= :band
29
+ representable_property :name
30
+ representable_property :label
31
+ end
32
+ end
33
+
25
34
  class Band
26
35
  include Representable::XML
27
36
  representable_property :href, :from => "@href"
@@ -34,19 +43,34 @@ class XmlTest < MiniTest::Spec
34
43
 
35
44
  describe "#binding_for_definition" do
36
45
  it "returns AttributeBinding" do
37
- assert_kind_of XML::AttributeBinding, XML.binding_for_definition(Def.new(:band, :from => "@band"))
46
+ assert_kind_of XML::AttributeBinding, Band.binding_for_definition(Def.new(:band, :from => "@band"))
38
47
  end
39
48
 
40
49
  it "returns ObjectBinding" do
41
- assert_kind_of XML::ObjectBinding, XML.binding_for_definition(Def.new(:band, :as => Hash))
50
+ assert_kind_of XML::ObjectBinding, Band.binding_for_definition(Def.new(:band, :as => Hash))
42
51
  end
43
52
 
44
- #it "returns NamespaceBinding" do
45
- # assert_kind_of Xml::AttributeBinding, Xml.binding_for_definition(Def.new(:band, :from => "@band"))
46
- #end
47
-
48
53
  it "returns TextBinding" do
49
- assert_kind_of XML::TextBinding, XML.binding_for_definition(Def.new(:band, :from => :content))
54
+ assert_kind_of XML::TextBinding, Band.binding_for_definition(Def.new(:band, :from => :content))
55
+ end
56
+ end
57
+
58
+ describe "#from_xml" do
59
+ before do
60
+ @band = @Band.new
61
+ end
62
+
63
+ it "accepts xml string" do
64
+ @band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>})
65
+ assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
66
+ end
67
+
68
+ it "forwards block to #update_properties_from" do
69
+ @band.from_xml(%{<band><name>Nofx</name><label>NOFX</label></band>}) do |binding|
70
+ binding.definition.name == "name"
71
+ end
72
+
73
+ assert_equal ["Nofx", nil], [@band.name, @band.label]
50
74
  end
51
75
  end
52
76
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 0
9
- version: 0.9.0
8
+ - 1
9
+ version: 0.9.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Nick Sutterer
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-10-05 00:00:00 +02:00
17
+ date: 2011-11-22 00:00:00 -02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -70,7 +70,7 @@ dependencies:
70
70
  type: :development
71
71
  version_requirements: *id004
72
72
  - !ruby/object:Gem::Dependency
73
- name: rspec
73
+ name: test_xml
74
74
  prerelease: false
75
75
  requirement: &id005 !ruby/object:Gem::Requirement
76
76
  none: false
@@ -83,16 +83,17 @@ dependencies:
83
83
  type: :development
84
84
  version_requirements: *id005
85
85
  - !ruby/object:Gem::Dependency
86
- name: test_xml
86
+ name: minitest
87
87
  prerelease: false
88
88
  requirement: &id006 !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
- - - ">="
91
+ - - ~>
92
92
  - !ruby/object:Gem::Version
93
93
  segments:
94
- - 0
95
- version: "0"
94
+ - 2
95
+ - 8
96
+ version: "2.8"
96
97
  type: :development
97
98
  version_requirements: *id006
98
99
  description: Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.
@@ -108,6 +109,7 @@ files:
108
109
  - .gitignore
109
110
  - .gitmodules
110
111
  - .rspec
112
+ - .travis.yml
111
113
  - CHANGES.textile
112
114
  - Gemfile
113
115
  - LICENSE
@@ -123,6 +125,7 @@ files:
123
125
  - lib/representable/xml.rb
124
126
  - representable.gemspec
125
127
  - test/bindings_test.rb
128
+ - test/definition_test.rb
126
129
  - test/json_test.rb
127
130
  - test/representable_test.rb
128
131
  - test/test_helper.rb