representable 0.10.2 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,11 @@
1
+ h2. 0.10.3
2
+
3
+ * Added @representable_property :default => ...@ option which is considered for both serialization and deserialization. The default is applied when the value is @nil@. Note that an empty string ain't @nil@.
4
+ * @representable_attrs@ are now pushed to instance level as soon as possible.
5
+
1
6
  h2. 0.10.2
2
7
 
3
- * Added `representable_property :accessors => false` option to suppress adding accessors.
8
+ * Added @representable_property :accessors => false@ option to suppress adding accessors.
4
9
  * @Representable.representation_wrap@ is no longer inherited.
5
10
  * Representers can now be defined in modules. They inherit to including modules.
6
11
 
@@ -37,7 +37,7 @@ Since you keep forgetting the heroes of your childhood you decide to implement a
37
37
 
38
38
  This declares two simple properties. Representable will automatically add accessors to the class.
39
39
 
40
- If you don't want declarations in your models, use a module.
40
+ Alternatively, if you don't want declarations in your models, use a module.
41
41
 
42
42
  module HeroRepresentation
43
43
  include Representable
@@ -14,10 +14,10 @@ module Representable
14
14
 
15
15
  # Reads values from +doc+ and sets properties accordingly.
16
16
  def update_properties_from(doc, &block)
17
- self.class.representable_bindings.each do |bin|
17
+ representable_bindings.each do |bin|
18
18
  next if eval_property_block(bin, &block) # skip if block is false.
19
19
 
20
- value = bin.read(doc)
20
+ value = bin.read(doc) || bin.definition.default
21
21
  send(bin.definition.setter, value)
22
22
  end
23
23
  self
@@ -26,10 +26,10 @@ module Representable
26
26
  private
27
27
  # Compiles the document going through all properties.
28
28
  def create_representation_with(doc, &block)
29
- self.class.representable_bindings.each do |bin|
29
+ representable_bindings.each do |bin|
30
30
  next if eval_property_block(bin, &block) # skip if block is false.
31
31
 
32
- value = send(bin.definition.getter) # DISCUSS: eventually move back to Ref.
32
+ value = send(bin.definition.getter) || bin.definition.default # DISCUSS: eventually move back to Ref.
33
33
  bin.write(doc, value) if value
34
34
  end
35
35
  doc
@@ -42,6 +42,14 @@ private
42
42
  block_given? and not yield binding.definition.name.to_sym
43
43
  end
44
44
 
45
+ def representable_attrs
46
+ @representable_attrs ||= self.class.representable_attrs # DISCUSS: copy, or better not?
47
+ end
48
+
49
+ def representable_bindings
50
+ representable_attrs.map {|attr| binding_for_definition(attr) }
51
+ end
52
+
45
53
 
46
54
  module ClassMethods # :nodoc:
47
55
  module Declarations
@@ -62,6 +70,7 @@ private
62
70
  # representable_property :name, :from => :title
63
71
  # representable_property :name, :as => Name
64
72
  # representable_property :name, :accessors => false
73
+ # representable_property :name, :default => "Mike"
65
74
  def representable_property(name, options={})
66
75
  attr = add_representable_property(name, options)
67
76
 
@@ -8,14 +8,10 @@ module Representable
8
8
  end
9
9
 
10
10
  def read(hash)
11
- value_from_hash(hash) or default
11
+ value_from_hash(hash)
12
12
  end
13
13
 
14
14
  private
15
- def default
16
- ""
17
- end
18
-
19
15
  def collect_for(hash)
20
16
  nodes = hash[definition.from] or return
21
17
  nodes = [nodes] unless nodes.is_a?(Array)
@@ -51,16 +47,11 @@ module Representable
51
47
  end
52
48
 
53
49
  private
54
- def default
55
- []
56
- end
57
-
58
50
  def value_from_hash(hash)
59
51
  collect_for(hash) do |node|
60
52
  definition.sought_type.from_hash(node) # call #from_hash as it's already deserialized.
61
53
  end
62
54
  end
63
-
64
55
  end
65
56
  end
66
57
  end
@@ -8,14 +8,10 @@ module Representable
8
8
  end
9
9
 
10
10
  def read(xml)
11
- value_from_node(xml) or default
11
+ value_from_node(xml)
12
12
  end
13
13
 
14
14
  private
15
- def default
16
- ""
17
- end
18
-
19
15
  def xpath
20
16
  definition.from
21
17
  end
@@ -80,10 +76,6 @@ module Representable
80
76
  end
81
77
 
82
78
  private
83
- def default
84
- []
85
- end
86
-
87
79
  # Deserializes the ref's element from +xml+.
88
80
  def value_from_node(xml)
89
81
  collect_for(xml) do |node|
@@ -1,7 +1,7 @@
1
1
  module Representable
2
2
  # Created at class compile time. Keeps configuration options for one property.
3
3
  class Definition
4
- attr_reader :name, :sought_type, :from
4
+ attr_reader :name, :sought_type, :from, :default
5
5
  alias_method :getter, :name
6
6
 
7
7
  def initialize(sym, options={})
@@ -9,6 +9,8 @@ module Representable
9
9
  @array = options[:collection]
10
10
  @from = (options[:from] || name).to_s
11
11
  @sought_type = options[:as] || :text
12
+ @default = options[:default]
13
+ @default ||= [] if array?
12
14
 
13
15
  # FIXME: move me to xml.
14
16
  if options[:from].to_s =~ /^@/
@@ -14,17 +14,13 @@ module Representable
14
14
 
15
15
  def self.included(base)
16
16
  base.class_eval do
17
- include Representable
17
+ include Representable # either in Hero or HeroRepresentation.
18
18
  extend ClassMethods
19
19
  end
20
20
  end
21
21
 
22
22
 
23
23
  module ClassMethods
24
- def binding_for_definition(definition)
25
- (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
26
- end
27
-
28
24
  # Creates a new object from the passed JSON document.
29
25
  def from_json(*args, &block)
30
26
  new.from_json(*args, &block)
@@ -61,5 +57,9 @@ module Representable
61
57
  def to_json(*args, &block)
62
58
  to_hash(*args, &block).to_json
63
59
  end
60
+
61
+ def binding_for_definition(definition)
62
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
63
+ end
64
64
  end
65
65
  end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "0.10.2"
2
+ VERSION = "0.10.3"
3
3
  end
@@ -19,10 +19,6 @@ module Representable
19
19
 
20
20
 
21
21
  module ClassMethods
22
- def binding_for_definition(definition)
23
- (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
24
- end
25
-
26
22
  # Creates a new Ruby object from XML using mapping information declared in the class.
27
23
  #
28
24
  # Accepts a block yielding the currently iterated Definition. If the block returns false
@@ -59,5 +55,9 @@ module Representable
59
55
  def to_xml(*args, &block)
60
56
  to_node(*args, &block).to_s
61
57
  end
58
+
59
+ def binding_for_definition(definition)
60
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
61
+ end
62
62
  end
63
63
  end
@@ -1,17 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class BindingsTest < MiniTest::Spec
4
- describe "ObjectRef with []" do
5
- before do
6
- @ref = Representable::XML::ObjectBinding.new(Representable::Definition.new(:songs, :as => [Hash]))
7
- end
8
-
9
- it "responds to #default" do
10
- assert_equal [], @ref.send(:default)
11
- end
12
- end
13
-
14
-
15
4
  describe "TextRef#read" do
16
5
  def parse_xml(xml); Nokogiri::XML(xml).root; end
17
6
 
@@ -62,6 +62,10 @@ class DefinitionTest < MiniTest::Spec
62
62
  it "responds to #sought_type" do
63
63
  assert_equal :text, @def.sought_type
64
64
  end
65
+
66
+ it "responds to #default" do
67
+ assert_equal [], @def.default
68
+ end
65
69
  end
66
70
 
67
71
  describe ":as => Item" do
@@ -73,4 +77,16 @@ class DefinitionTest < MiniTest::Spec
73
77
  assert_equal Hash, @def.sought_type
74
78
  end
75
79
  end
80
+
81
+ describe ":default => value" do
82
+ it "responds to #default" do
83
+ @def = Representable::Definition.new(:song)
84
+ assert_equal nil, @def.default
85
+ end
86
+
87
+ it "accepts a default value" do
88
+ @def = Representable::Definition.new(:song, :default => "Atheist Peace")
89
+ assert_equal "Atheist Peace", @def.default
90
+ end
91
+ end
76
92
  end
@@ -17,6 +17,8 @@ module JsonTest
17
17
  self.name = name if name
18
18
  end
19
19
  end
20
+
21
+ @band = @Band.new
20
22
  end
21
23
 
22
24
 
@@ -136,18 +138,18 @@ module JsonTest
136
138
 
137
139
  describe "#binding_for_definition" do
138
140
  it "returns ObjectBinding" do
139
- assert_kind_of Json::ObjectBinding, @Band.binding_for_definition(Def.new(:band, :as => Hash))
141
+ assert_kind_of Json::ObjectBinding, @band.binding_for_definition(Def.new(:band, :as => Hash))
140
142
  end
141
143
 
142
144
  it "returns TextBinding" do
143
- assert_kind_of Json::TextBinding, @Band.binding_for_definition(Def.new(:band))
145
+ assert_kind_of Json::TextBinding, @band.binding_for_definition(Def.new(:band))
144
146
  end
145
147
  end
146
148
 
147
149
  describe "#representable_bindings" do
148
150
  it "returns bindings for each property" do
149
- assert_equal 2, @Band.representable_bindings.size
150
- assert_equal "name", @Band.representable_bindings.first.definition.name
151
+ assert_equal 2, @band.send(:representable_bindings).size
152
+ assert_equal "name", @band.send(:representable_bindings).first.definition.name
151
153
  end
152
154
  end
153
155
  end
@@ -229,7 +231,51 @@ module JsonTest
229
231
  assert_equal '{"songName":"Run To The Hills"}', song.to_json
230
232
  end
231
233
  end
234
+
235
+ describe ":default => :value" do
236
+ before do
237
+ @Album = Class.new do
238
+ include Representable::JSON
239
+ representable_property :name, :default => "30 Years Live"
240
+ end
241
+ end
242
+
243
+ describe "#from_json" do
244
+ it "uses default when property nil in doc" do
245
+ album = @Album.from_json({}.to_json)
246
+ assert_equal "30 Years Live", album.name
247
+ end
248
+
249
+ it "uses value from doc when present" do
250
+ album = @Album.from_json({:name => "Live At The Wireless"}.to_json)
251
+ assert_equal "Live At The Wireless", album.name
252
+ end
253
+
254
+ it "uses value from doc when empty string" do
255
+ album = @Album.from_json({:name => ""}.to_json)
256
+ assert_equal "", album.name
257
+ end
258
+ end
259
+
260
+ describe "#to_json" do
261
+ it "uses default when not available in object" do
262
+ assert_equal "{\"name\":\"30 Years Live\"}", @Album.new.to_json
263
+ end
264
+
265
+ it "uses value from represented object when present" do
266
+ album = @Album.new
267
+ album.name = "Live At The Wireless"
268
+ assert_equal "{\"name\":\"Live At The Wireless\"}", album.to_json
269
+ end
270
+
271
+ it "uses value from represented object when emtpy string" do
272
+ album = @Album.new
273
+ album.name = ""
274
+ assert_equal "{\"name\":\"\"}", album.to_json
275
+ end
276
+ end
232
277
  end
278
+ end
233
279
 
234
280
 
235
281
  class CollectionTest < MiniTest::Spec
@@ -275,7 +321,7 @@ module JsonTest
275
321
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
276
322
  end
277
323
 
278
- it "collections can be empty" do
324
+ it "creates emtpy array per default" do
279
325
  cd = Compilation.from_json({:compilation => {}}.to_json)
280
326
  assert_equal [], cd.bands
281
327
  end
@@ -64,6 +64,18 @@ class RepresentableTest < MiniTest::Spec
64
64
  assert_equal "{\"name\":\"Vention Dention\",\"street_cred\":1}", vd.to_json
65
65
  end
66
66
 
67
+ #it "allows including the concrete representer module only" do
68
+ # require 'representable/json'
69
+ # module RockBandRepresentation
70
+ # include Representable::JSON
71
+ # representable_property :name
72
+ # end
73
+ # vd = class VH
74
+ # include RockBandRepresentation
75
+ # end.new
76
+ # vd.name = "Van Halen"
77
+ # assert_equal "{\"name\":\"Van Halen\"}", vd.to_json
78
+ #end
67
79
  end
68
80
  end
69
81
 
@@ -32,6 +32,8 @@ class XmlTest < MiniTest::Spec
32
32
  representable_property :name
33
33
  representable_property :label
34
34
  end
35
+
36
+ @band = @Band.new
35
37
  end
36
38
 
37
39
 
@@ -138,15 +140,15 @@ class XmlTest < MiniTest::Spec
138
140
 
139
141
  describe "#binding_for_definition" do
140
142
  it "returns AttributeBinding" do
141
- assert_kind_of XML::AttributeBinding, @Band.binding_for_definition(Def.new(:band, :from => "@band"))
143
+ assert_kind_of XML::AttributeBinding, @band.binding_for_definition(Def.new(:band, :from => "@band"))
142
144
  end
143
145
 
144
146
  it "returns ObjectBinding" do
145
- assert_kind_of XML::ObjectBinding, @Band.binding_for_definition(Def.new(:band, :as => Hash))
147
+ assert_kind_of XML::ObjectBinding, @band.binding_for_definition(Def.new(:band, :as => Hash))
146
148
  end
147
149
 
148
150
  it "returns TextBinding" do
149
- assert_kind_of XML::TextBinding, @Band.binding_for_definition(Def.new(:band, :from => :content))
151
+ assert_kind_of XML::TextBinding, @band.binding_for_definition(Def.new(:band, :from => :content))
150
152
  end
151
153
  end
152
154
  end
metadata CHANGED
@@ -1,90 +1,111 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: representable
3
- version: !ruby/object:Gem::Version
4
- version: 0.10.2
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 10
8
+ - 3
9
+ version: 0.10.3
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Nick Sutterer
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2011-12-03 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
16
+
17
+ date: 2011-12-07 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: nokogiri
16
- requirement: &75503660 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
22
31
  type: :runtime
23
- prerelease: false
24
- version_requirements: *75503660
25
- - !ruby/object:Gem::Dependency
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
26
34
  name: json
27
- requirement: &75503430 !ruby/object:Gem::Requirement
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
28
37
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
33
44
  type: :runtime
34
- prerelease: false
35
- version_requirements: *75503430
36
- - !ruby/object:Gem::Dependency
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
37
47
  name: rake
38
- requirement: &75503180 !ruby/object:Gem::Requirement
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
39
50
  none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
44
57
  type: :development
45
- prerelease: false
46
- version_requirements: *75503180
47
- - !ruby/object:Gem::Dependency
58
+ version_requirements: *id003
59
+ - !ruby/object:Gem::Dependency
48
60
  name: test_xml
49
- requirement: &75502870 !ruby/object:Gem::Requirement
61
+ prerelease: false
62
+ requirement: &id004 !ruby/object:Gem::Requirement
50
63
  none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
55
70
  type: :development
56
- prerelease: false
57
- version_requirements: *75502870
58
- - !ruby/object:Gem::Dependency
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
59
73
  name: minitest
60
- requirement: &75502410 !ruby/object:Gem::Requirement
74
+ prerelease: false
75
+ requirement: &id005 !ruby/object:Gem::Requirement
61
76
  none: false
62
- requirements:
77
+ requirements:
63
78
  - - ~>
64
- - !ruby/object:Gem::Version
65
- version: '2.8'
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 2
82
+ - 8
83
+ version: "2.8"
66
84
  type: :development
67
- prerelease: false
68
- version_requirements: *75502410
69
- - !ruby/object:Gem::Dependency
85
+ version_requirements: *id005
86
+ - !ruby/object:Gem::Dependency
70
87
  name: mocha
71
- requirement: &75501950 !ruby/object:Gem::Requirement
88
+ prerelease: false
89
+ requirement: &id006 !ruby/object:Gem::Requirement
72
90
  none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
77
97
  type: :development
78
- prerelease: false
79
- version_requirements: *75501950
80
- description: Maps representation documents from and to Ruby objects. Includes XML
81
- and JSON support, plain properties, collections and compositions.
82
- email:
98
+ version_requirements: *id006
99
+ description: Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties, collections and compositions.
100
+ email:
83
101
  - apotonick@gmail.com
84
102
  executables: []
103
+
85
104
  extensions: []
105
+
86
106
  extra_rdoc_files: []
87
- files:
107
+
108
+ files:
88
109
  - .gitignore
89
110
  - .gitmodules
90
111
  - .rspec
@@ -108,29 +129,37 @@ files:
108
129
  - test/representable_test.rb
109
130
  - test/test_helper.rb
110
131
  - test/xml_test.rb
132
+ has_rdoc: true
111
133
  homepage: http://representable.apotomo.de
112
134
  licenses: []
135
+
113
136
  post_install_message:
114
137
  rdoc_options: []
115
- require_paths:
138
+
139
+ require_paths:
116
140
  - lib
117
- required_ruby_version: !ruby/object:Gem::Requirement
141
+ required_ruby_version: !ruby/object:Gem::Requirement
118
142
  none: false
119
- requirements:
120
- - - ! '>='
121
- - !ruby/object:Gem::Version
122
- version: '0'
123
- required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ segments:
147
+ - 0
148
+ version: "0"
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
150
  none: false
125
- requirements:
126
- - - ! '>='
127
- - !ruby/object:Gem::Version
128
- version: '0'
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ segments:
155
+ - 0
156
+ version: "0"
129
157
  requirements: []
158
+
130
159
  rubyforge_project:
131
- rubygems_version: 1.8.10
160
+ rubygems_version: 1.3.7
132
161
  signing_key:
133
162
  specification_version: 3
134
- summary: Maps representation documents from and to Ruby objects. Includes XML and
135
- JSON support, plain properties, collections and compositions.
163
+ summary: Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties, collections and compositions.
136
164
  test_files: []
165
+