representable 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,8 @@
1
+ h2. 1.2.1
2
+
3
+ * Deprecated @:represent_nil@ favor of @:render_nil@.
4
+ * API change: if a property is missing in an incoming document and there is no default set it is completely ignored and *not* set in the represented object.
5
+
1
6
  h2. 1.2.0
2
7
 
3
8
  * Deprecated @:except@ in favor of @:exclude@.
@@ -251,11 +251,11 @@ When rendering or parsing, the +friends+ property is considered only if the cond
251
251
 
252
252
  === False and Nil Values
253
253
 
254
- Since 1.2 +false+ values are considered when parsing and rendering. That particularly means properties that used to be unset (i.e. @nil@) after parsing might be @false@ now. Vice versa, +false+ values that weren't included in the rendered document will be visible now.
254
+ Since 1.2 +false+ values are considered when parsing and rendering. That particularly means properties that used to be unset (i.e. +nil+) after parsing might be +false+ now. Vice versa, +false+ values that weren't included in the rendered document will be visible now.
255
255
 
256
- If you want +nil+ values to be included when rendering, use the +:represent_nil+ option.
256
+ If you want +nil+ values to be included when rendering, use the +:render_nil+ option.
257
257
 
258
- property :surename, :represent_nil => true
258
+ property :surename, :render_nil => true
259
259
 
260
260
  == DCI
261
261
 
@@ -1,5 +1,5 @@
1
- require 'representable/definition'
2
1
  require 'representable/deprecations'
2
+ require 'representable/definition'
3
3
 
4
4
  # Representable can be used in two ways.
5
5
  #
@@ -96,7 +96,11 @@ private
96
96
  # Parse value from doc and update the model property.
97
97
  def uncompile_fragment(bin, doc)
98
98
  value = read_fragment_for(bin, doc)
99
- value = bin.definition.default_for(value)
99
+
100
+ if value == Binding::FragmentNotFound
101
+ return unless bin.definition.has_default?
102
+ value = bin.definition.default
103
+ end
100
104
 
101
105
  send(bin.definition.setter, value)
102
106
  end
@@ -1,5 +1,9 @@
1
1
  module Representable
2
2
  class Binding
3
+ class FragmentNotFound
4
+ end
5
+
6
+
3
7
  attr_reader :definition # TODO: merge Binding and Definition.
4
8
 
5
9
  def initialize(definition)
@@ -27,6 +27,8 @@ module Representable
27
27
  end
28
28
 
29
29
  def read(hash)
30
+ return FragmentNotFound unless hash.has_key?(definition.from) # DISCUSS: put it all in #read for performance. not really sure if i like returning that special thing.
31
+
30
32
  fragment = hash[definition.from]
31
33
  deserialize_from(fragment)
32
34
  end
@@ -54,7 +56,6 @@ module Representable
54
56
  end
55
57
 
56
58
  def deserialize_from(fragment)
57
- fragment ||= {}
58
59
  fragment.collect { |item_fragment| deserialize(item_fragment) }
59
60
  end
60
61
  end
@@ -40,7 +40,7 @@ module Representable
40
40
 
41
41
  def read(node)
42
42
  nodes = node.search("./#{xpath}")
43
- return if nodes.size == 0 # TODO: write dedicated test!
43
+ return FragmentNotFound if nodes.size == 0 # TODO: write dedicated test!
44
44
 
45
45
  deserialize_from(nodes)
46
46
  end
@@ -7,6 +7,8 @@ module Representable
7
7
  def initialize(sym, options={})
8
8
  @name = sym.to_s
9
9
  @options = options
10
+
11
+ options[:default] ||= [] if array? # FIXME: move to CollectionBinding!
10
12
  end
11
13
 
12
14
  def clone
@@ -42,6 +44,10 @@ module Representable
42
44
  value
43
45
  end
44
46
 
47
+ def has_default?
48
+ options.has_key?(:default)
49
+ end
50
+
45
51
  def representer_module
46
52
  options[:extend]
47
53
  end
@@ -51,12 +57,10 @@ module Representable
51
57
  end
52
58
 
53
59
  def skipable_nil_value?(value)
54
- value.nil? and not options[:represent_nil]
60
+ value.nil? and not options[:render_nil]
55
61
  end
56
62
 
57
- private
58
63
  def default
59
- options[:default] ||= [] if array? # FIXME: move to CollectionBinding!
60
64
  options[:default]
61
65
  end
62
66
  end
@@ -1,9 +1,11 @@
1
- module Representable::Deprecations
2
- def skip_excluded_property?(binding, options) # TODO: remove with 1.3.
3
- if options[:except]
4
- options[:exclude] = options[:except]
5
- warn "The :except option is deprecated and will be removed in 1.3. Please use :exclude."
6
- end # i wanted a one-liner but failed :)
7
- super
1
+ module Representable
2
+ module Deprecations
3
+ def skip_excluded_property?(binding, options) # TODO: remove with 1.3.
4
+ if options[:except]
5
+ options[:exclude] = options[:except]
6
+ warn "The :except option is deprecated and will be removed in 1.3. Please use :exclude."
7
+ end # i wanted a one-liner but failed :)
8
+ super
9
+ end
8
10
  end
9
11
  end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -55,25 +55,41 @@ class DefinitionTest < MiniTest::Spec
55
55
  end
56
56
  end
57
57
 
58
+ describe "#has_default?" do
59
+ it "returns false if no :default set" do
60
+ assert_equal false, Representable::Definition.new(:song).has_default?
61
+ end
62
+
63
+ it "returns true if :default set" do
64
+ assert_equal true, Representable::Definition.new(:song, :default => nil).has_default?
65
+ end
66
+
67
+ it "returns true if :collection" do
68
+ assert_equal true, Representable::Definition.new(:songs, :collection => true).has_default?
69
+ end
70
+
71
+ end
72
+
73
+
58
74
  describe "#skipable_nil_value?" do
59
75
  # default if skipable_nil_value?
60
76
  before do
61
- @def = Representable::Definition.new(:song, :represent_nil => true)
77
+ @def = Representable::Definition.new(:song, :render_nil => true)
62
78
  end
63
79
 
64
80
  it "returns false when not nil" do
65
81
  assert_equal false, @def.skipable_nil_value?("Disconnect, Disconnect")
66
82
  end
67
83
 
68
- it "returns false when nil and :represent_nil => true" do
84
+ it "returns false when nil and :render_nil => true" do
69
85
  assert_equal false, @def.skipable_nil_value?(nil)
70
86
  end
71
87
 
72
- it "returns true when nil and :represent_nil => false" do
88
+ it "returns true when nil and :render_nil => false" do
73
89
  assert_equal true, Representable::Definition.new(:song).skipable_nil_value?(nil)
74
90
  end
75
91
 
76
- it "returns false when not nil and :represent_nil => false" do
92
+ it "returns false when not nil and :render_nil => false" do
77
93
  assert_equal false, Representable::Definition.new(:song).skipable_nil_value?("Fatal Flu")
78
94
  end
79
95
  end
@@ -96,13 +112,13 @@ class DefinitionTest < MiniTest::Spec
96
112
  assert_equal "Insider", @def.default_for(nil)
97
113
  end
98
114
 
99
- it "returns nil when value nil and :represent_nil true" do
100
- @def = Representable::Definition.new(:song, :represent_nil => true)
115
+ it "returns nil when value nil and :render_nil true" do
116
+ @def = Representable::Definition.new(:song, :render_nil => true)
101
117
  assert_equal nil, @def.default_for(nil)
102
118
  end
103
119
 
104
- it "returns nil when value nil and :represent_nil true even when :default is set" do
105
- @def = Representable::Definition.new(:song, :represent_nil => true, :default => "The Quest")
120
+ it "returns nil when value nil and :render_nil true even when :default is set" do
121
+ @def = Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")
106
122
  assert_equal nil, @def.default_for(nil)
107
123
  end
108
124
 
@@ -13,6 +13,23 @@ class JSONBindingTest < MiniTest::Spec
13
13
 
14
14
 
15
15
  describe "PropertyBinding" do
16
+ describe "#read" do
17
+ before do
18
+ @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song))
19
+ end
20
+
21
+ it "returns fragment if present" do
22
+ assert_equal "Stick The Flag Up Your Goddamn Ass, You Sonofabitch", @property.read({"song" => "Stick The Flag Up Your Goddamn Ass, You Sonofabitch"})
23
+ assert_equal "", @property.read({"song" => ""})
24
+ assert_equal nil, @property.read({"song" => nil})
25
+ end
26
+
27
+ it "returns FRAGMENT_NOT_FOUND if not in document" do
28
+ assert_equal Representable::Binding::FragmentNotFound, @property.read({})
29
+ end
30
+
31
+ end
32
+
16
33
  describe "with plain text" do
17
34
  before do
18
35
  @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song))
@@ -31,7 +31,7 @@ module JsonTest
31
31
 
32
32
  it "yields new object and options to block" do
33
33
  @Band.class_eval { attr_accessor :new_name }
34
- @band = @Band.from_json({}, :new_name => "Diesel Boy") do |band, options|
34
+ @band = @Band.from_json("{}", :new_name => "Diesel Boy") do |band, options|
35
35
  band.new_name= options[:new_name]
36
36
  end
37
37
  assert_equal "Diesel Boy", @band.new_name
@@ -378,8 +378,8 @@ end
378
378
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
379
379
  end
380
380
 
381
- it "creates emtpy array per default" do
382
- cd = Compilation.from_json({:compilation => {}}.to_json)
381
+ it "creates emtpy array from default if configured" do
382
+ cd = Compilation.from_json({}.to_json)
383
383
  assert_equal [], cd.bands
384
384
  end
385
385
  end
@@ -253,6 +253,26 @@ class RepresentableTest < MiniTest::Spec
253
253
  @band.update_properties_from({"groupies"=>false}, {}, Representable::JSON)
254
254
  assert_equal false, @band.groupies
255
255
  end
256
+
257
+ it "ignores (no-default) properties not present in the incoming document" do
258
+ { Representable::JSON => {},
259
+ Representable::XML => xml(%{<band/>})
260
+ }.each do |format, document|
261
+ nested_repr = Module.new do # this module is never applied.
262
+ include format
263
+ property :created_at
264
+ end
265
+
266
+ repr = Module.new do
267
+ include format
268
+ property :name, :class => Object, :extend => nested_repr
269
+ end
270
+
271
+ @band = Band.new.extend(repr)
272
+ @band.update_properties_from(document, {}, format)
273
+ assert_equal nil, @band.name, "Failed in #{format}"
274
+ end
275
+ end
256
276
  end
257
277
 
258
278
  describe "#create_representation_with" do
@@ -290,11 +310,11 @@ class RepresentableTest < MiniTest::Spec
290
310
  assert_equal({"name"=>"No One's Choice","groupies"=>false}, @band.send(:create_representation_with, {}, {}, Representable::JSON))
291
311
  end
292
312
 
293
- it "includes nil attribute when :represent_nil is true" do
313
+ it "includes nil attribute when :render_nil is true" do
294
314
  mod = Module.new do
295
315
  include Representable::JSON
296
316
  property :name
297
- property :groupies, :represent_nil => true
317
+ property :groupies, :render_nil => true
298
318
  end
299
319
 
300
320
  @band.extend(mod) # FIXME: use clean object.
@@ -4,6 +4,7 @@ Bundler.setup
4
4
  gem 'minitest'
5
5
  require 'representable'
6
6
  require 'representable/json'
7
+ require 'representable/xml'
7
8
  require 'test/unit'
8
9
  require 'minitest/spec'
9
10
  require 'minitest/autorun'
@@ -28,3 +29,13 @@ class Song
28
29
  name == other.name
29
30
  end
30
31
  end
32
+
33
+ module XmlHelper
34
+ def xml(document)
35
+ Nokogiri::XML(document).root
36
+ end
37
+ end
38
+
39
+ MiniTest::Spec.class_eval do
40
+ include XmlHelper
41
+ end
@@ -297,7 +297,7 @@ class CollectionTest < MiniTest::Spec
297
297
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
298
298
  end
299
299
 
300
- it "collections can be empty" do
300
+ it "collections can be empty when default set" do
301
301
  cd = Compilation.from_xml(%{
302
302
  <compilation>
303
303
  </compilation>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-31 00:00:00.000000000 Z
12
+ date: 2012-06-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri