representable 0.9.0 → 0.9.1

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