representable 1.2.5 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ h2. 1.2.6
2
+
3
+ Extracted @HashRepresenter@ which operates on hash structures. This allows you to "parse" form data, e.g. as in Rails' @params@ hash. Internally, this is used by JSON and partly by YAML.
4
+
1
5
  h2. 1.2.5
2
6
 
3
7
  * Add support for YAML.
@@ -21,6 +25,7 @@ h2. 1.2.1
21
25
  * Deprecated @:represent_nil@ favor of @:render_nil@.
22
26
  * 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.
23
27
 
28
+
24
29
  h2. 1.2.0
25
30
 
26
31
  * Deprecated @:except@ in favor of @:exclude@.
@@ -80,7 +80,7 @@ Those two properties are considered when rendering in #to_json.
80
80
 
81
81
  == Parsing
82
82
 
83
- The cool thing about Representable is: it works bidirectional. By declaring properties you can not only render but also parse!
83
+ The cool thing about Representable is: it works bidirectional. The document definition in your representer module can also be used for parsing documents and assigning property values.
84
84
 
85
85
  hook = Hero.from_json('{"forename":"Captain","surename":"Hook"}')
86
86
  hook.forename #=> "Captain"
@@ -11,17 +11,13 @@ module Representable
11
11
  end
12
12
 
13
13
  # Main entry point for rendering/parsing a property object.
14
- module Hooks
15
- def serialize(value)
16
- value
17
- end
18
-
19
- def deserialize(fragment)
20
- fragment
21
- end
14
+ def serialize(value)
15
+ value
22
16
  end
23
17
 
24
- include Hooks
18
+ def deserialize(fragment)
19
+ fragment
20
+ end
25
21
 
26
22
 
27
23
  # Hooks into #serialize and #deserialize to extend typed properties
@@ -1,7 +1,7 @@
1
1
  require 'representable/binding'
2
2
 
3
3
  module Representable
4
- module JSON
4
+ module Hash
5
5
  module ObjectBinding
6
6
  include Binding::Object
7
7
 
@@ -15,7 +15,7 @@ module Representable
15
15
  end
16
16
 
17
17
 
18
- class JSONBinding < Representable::Binding
18
+ class PropertyBinding < Representable::Binding
19
19
  def initialize(definition) # FIXME. make generic.
20
20
  super
21
21
  extend ObjectBinding if definition.typed?
@@ -31,10 +31,7 @@ module Representable
31
31
  def write(hash, value)
32
32
  hash[definition.from] = serialize_for(value)
33
33
  end
34
- end
35
-
36
-
37
- class PropertyBinding < JSONBinding
34
+
38
35
  def serialize_for(value)
39
36
  serialize(value)
40
37
  end
@@ -45,7 +42,7 @@ module Representable
45
42
  end
46
43
 
47
44
 
48
- class CollectionBinding < JSONBinding
45
+ class CollectionBinding < PropertyBinding
49
46
  def serialize_for(value)
50
47
  value.collect { |obj| serialize(obj) }
51
48
  end
@@ -56,7 +53,7 @@ module Representable
56
53
  end
57
54
 
58
55
 
59
- class HashBinding < JSONBinding
56
+ class HashBinding < PropertyBinding
60
57
  def serialize_for(value)
61
58
  # requires value to respond to #each with two block parameters.
62
59
  {}.tap do |hash|
@@ -18,34 +18,20 @@ module Representable
18
18
  end
19
19
  end
20
20
 
21
- class YAMLBinding < Representable::Binding
21
+ class PropertyBinding < Representable::Hash::PropertyBinding
22
22
  def initialize(definition) # FIXME. make generic.
23
23
  super
24
24
  extend ObjectBinding if definition.typed?
25
25
  end
26
26
 
27
- def read(hash)
28
- 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.
29
-
30
- fragment = hash[definition.from]
31
- deserialize_from(fragment)
32
- end
33
-
34
27
  def write(map, value)
35
28
  map.children << Psych::Nodes::Scalar.new(definition.from)
36
29
  map.children << serialize_for(value) # FIXME: should be serialize.
37
30
  end
38
- end
39
-
40
-
41
- class PropertyBinding < YAMLBinding
31
+
42
32
  def serialize_for(value)
43
33
  write_scalar serialize(value)
44
34
  end
45
-
46
- def deserialize_from(fragment)
47
- deserialize(fragment)
48
- end
49
35
 
50
36
  def write_scalar(value)
51
37
  Psych::Nodes::Scalar.new(value.to_s)
@@ -61,7 +47,7 @@ module Representable
61
47
  end
62
48
  end
63
49
 
64
- def deserialize_from(fragment)
50
+ def deserialize_from(fragment) # FIXME: redundant from Hash::Bindings
65
51
  fragment.collect { |item_fragment| deserialize(item_fragment) }
66
52
  end
67
53
  end
@@ -0,0 +1,47 @@
1
+ require 'representable'
2
+ require 'representable/bindings/hash_bindings'
3
+ require 'json'
4
+
5
+ module Representable
6
+ # The generic representer. Brings #to_hash and #from_hash to your object.
7
+ # If you plan to write your own representer for a new media type, try to use this module (e.g., check how JSON reuses Hash's internal
8
+ # architecture).
9
+ module Hash
10
+ def self.included(base)
11
+ base.class_eval do
12
+ include Representable # either in Hero or HeroRepresentation.
13
+ extend ClassMethods # DISCUSS: do that only for classes?
14
+ end
15
+ end
16
+
17
+
18
+ module ClassMethods
19
+ def binding_for_definition(definition)
20
+ return Representable::Hash::CollectionBinding.new(definition) if definition.array?
21
+ return Representable::Hash::HashBinding.new(definition) if definition.hash?
22
+ Representable::Hash::PropertyBinding.new(definition)
23
+ end
24
+
25
+ def from_hash(*args, &block)
26
+ create_represented(*args, &block).from_hash(*args)
27
+ end
28
+ end
29
+
30
+
31
+ def from_hash(data, options={})
32
+ if wrap = options[:wrap] || representation_wrap
33
+ data = data[wrap.to_s]
34
+ end
35
+
36
+ update_properties_from(data, options, JSON)
37
+ end
38
+
39
+ def to_hash(options={})
40
+ hash = create_representation_with({}, options, JSON)
41
+
42
+ return hash unless wrap = options[:wrap] || representation_wrap
43
+
44
+ {wrap => hash}
45
+ end
46
+ end
47
+ end
@@ -1,23 +1,17 @@
1
- require 'representable'
2
- require 'representable/bindings/json_bindings'
1
+ require 'representable/hash'
3
2
  require 'json'
4
3
 
5
4
  module Representable
6
- # Brings #to_xml, #to_hash, #from_xml and #from_hash to your object.
7
- #
8
- # Note: The authorative methods are #to_hash and #from_hash, if you override #to_json instead,
9
- # things might work as expected.
5
+ # Brings #to_json and #from_json to your object.
10
6
  module JSON
11
- def self.binding_for_definition(definition)
12
- return CollectionBinding.new(definition) if definition.array?
13
- return HashBinding.new(definition) if definition.hash?
14
- PropertyBinding.new(definition)
15
- end
16
-
7
+ extend Hash::ClassMethods
8
+ include Hash
9
+
17
10
  def self.included(base)
18
11
  base.class_eval do
19
12
  include Representable # either in Hero or HeroRepresentation.
20
13
  extend ClassMethods # DISCUSS: do that only for classes?
14
+ extend Representable::Hash::ClassMethods # DISCUSS: this is only for .from_hash, remove in 2.3?
21
15
  end
22
16
  end
23
17
 
@@ -27,10 +21,6 @@ module Representable
27
21
  def from_json(*args, &block)
28
22
  create_represented(*args, &block).from_json(*args)
29
23
  end
30
-
31
- def from_hash(*args, &block)
32
- create_represented(*args, &block).from_hash(*args)
33
- end
34
24
  end
35
25
 
36
26
 
@@ -40,22 +30,6 @@ module Representable
40
30
  from_hash(data, *args)
41
31
  end
42
32
 
43
- def from_hash(data, options={})
44
- if wrap = options[:wrap] || representation_wrap
45
- data = data[wrap.to_s]
46
- end
47
-
48
- update_properties_from(data, options, JSON)
49
- end
50
-
51
- def to_hash(options={})
52
- hash = create_representation_with({}, options, JSON)
53
-
54
- return hash unless wrap = options[:wrap] || representation_wrap
55
-
56
- {wrap => hash}
57
- end
58
-
59
33
  # Returns a JSON string representing this object.
60
34
  def to_json(*args)
61
35
  to_hash(*args).to_json
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.2.5"
2
+ VERSION = "1.2.6"
3
3
  end
@@ -1,8 +1,10 @@
1
- require 'representable'
1
+ require 'representable/hash'
2
2
  require 'representable/bindings/yaml_bindings'
3
3
 
4
4
  module Representable
5
5
  module YAML
6
+ include Hash
7
+
6
8
  def self.binding_for_definition(definition)
7
9
  return CollectionBinding.new(definition) if definition.array?
8
10
  #return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
@@ -28,12 +30,8 @@ module Representable
28
30
  #
29
31
  # Example:
30
32
  # band.from_xml("<band><name>Nofx</name></band>")
31
- def from_xml(*args, &block)
32
- create_represented(*args, &block).from_xml(*args)
33
- end
34
-
35
- def from_node(*args, &block)
36
- create_represented(*args, &block).from_node(*args)
33
+ def from_yaml(*args, &block)
34
+ create_represented(*args, &block).from_yaml(*args)
37
35
  end
38
36
  end
39
37
 
@@ -43,10 +41,6 @@ module Representable
43
41
  from_hash(hash, *args)
44
42
  end
45
43
 
46
- def from_hash(hash, options={})
47
- update_properties_from(hash, options, YAML)
48
- end
49
-
50
44
  # Returns a Nokogiri::XML object representing this object.
51
45
  def to_ast(options={})
52
46
  #root_tag = options[:wrap] || representation_wrap
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class JSONBindingTest < MiniTest::Spec
3
+ class HashBindingTest < MiniTest::Spec
4
4
  module SongRepresenter
5
5
  include Representable::JSON
6
6
  property :name
@@ -15,7 +15,7 @@ class JSONBindingTest < MiniTest::Spec
15
15
  describe "PropertyBinding" do
16
16
  describe "#read" do
17
17
  before do
18
- @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song))
18
+ @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song))
19
19
  end
20
20
 
21
21
  it "returns fragment if present" do
@@ -32,7 +32,7 @@ class JSONBindingTest < MiniTest::Spec
32
32
 
33
33
  describe "with plain text" do
34
34
  before do
35
- @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song))
35
+ @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song))
36
36
  end
37
37
 
38
38
  it "extracts with #read" do
@@ -48,7 +48,7 @@ class JSONBindingTest < MiniTest::Spec
48
48
 
49
49
  describe "with an object" do
50
50
  before do
51
- @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song, :class => SongWithRepresenter))
51
+ @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song, :class => SongWithRepresenter))
52
52
  @doc = {}
53
53
  end
54
54
 
@@ -64,7 +64,7 @@ class JSONBindingTest < MiniTest::Spec
64
64
 
65
65
  describe "with an object and :extend" do
66
66
  before do
67
- @property = Representable::JSON::PropertyBinding.new(Representable::Definition.new(:song, :class => Song, :extend => SongRepresenter))
67
+ @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song, :class => Song, :extend => SongRepresenter))
68
68
  @doc = {}
69
69
  end
70
70
 
@@ -83,7 +83,7 @@ class JSONBindingTest < MiniTest::Spec
83
83
  describe "CollectionBinding" do
84
84
  describe "with plain text items" do
85
85
  before do
86
- @property = Representable::JSON::CollectionBinding.new(Representable::Definition.new(:songs, :collection => true))
86
+ @property = Representable::Hash::CollectionBinding.new(Representable::Definition.new(:songs, :collection => true))
87
87
  end
88
88
 
89
89
  it "extracts with #read" do
@@ -104,7 +104,7 @@ class JSONBindingTest < MiniTest::Spec
104
104
  describe "HashBinding" do
105
105
  describe "with plain text items" do
106
106
  before do
107
- @property = Representable::JSON::HashBinding.new(Representable::Definition.new(:songs, :hash => true))
107
+ @property = Representable::Hash::HashBinding.new(Representable::Definition.new(:songs, :hash => true))
108
108
  end
109
109
 
110
110
  it "extracts with #read" do
@@ -120,7 +120,7 @@ class JSONBindingTest < MiniTest::Spec
120
120
 
121
121
  describe "with objects" do
122
122
  before do
123
- @property = Representable::JSON::HashBinding.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter))
123
+ @property = Representable::Hash::HashBinding.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter))
124
124
  end
125
125
 
126
126
  it "doesn't change the represented hash in #write" do
@@ -0,0 +1,117 @@
1
+ require 'test_helper'
2
+ require 'representable/hash'
3
+
4
+ class YamlTest < MiniTest::Spec
5
+ def self.hash_representer(&block)
6
+ Module.new do
7
+ include Representable::Hash
8
+ instance_exec &block
9
+ end
10
+ end
11
+
12
+ def hash_representer(&block)
13
+ self.class.hash_representer(&block)
14
+ end
15
+
16
+
17
+ describe "property" do
18
+ let (:hash) { hash_representer do property :best_song end }
19
+
20
+ let (:album) { Album.new.tap do |album|
21
+ album.best_song = "Liar"
22
+ end }
23
+
24
+ describe "#to_hash" do
25
+ it "renders plain property" do
26
+ album.extend(hash).to_hash.must_equal("best_song" => "Liar")
27
+ end
28
+ end
29
+
30
+
31
+ describe "#from_hash" do
32
+ it "parses plain property" do
33
+ album.extend(hash).from_hash("best_song" => "This Song Is Recycled").best_song.must_equal "This Song Is Recycled"
34
+ end
35
+ end
36
+
37
+
38
+ describe "with :class and :extend" do
39
+ hash_song = hash_representer do property :name end
40
+ let (:hash_album) { Module.new do
41
+ include Representable::Hash
42
+ property :best_song, :extend => hash_song, :class => Song
43
+ end }
44
+
45
+ let (:album) { Album.new.tap do |album|
46
+ album.best_song = Song.new("Liar")
47
+ end }
48
+
49
+
50
+ describe "#to_hash" do
51
+ it "renders embedded typed property" do
52
+ album.extend(hash_album).to_hash.must_equal("best_song" => {"name" => "Liar"})
53
+ end
54
+ end
55
+
56
+ describe "#from_hash" do
57
+ it "parses embedded typed property" do
58
+ album.extend(hash_album).from_hash("best_song" => {"name" => "Go With Me"}).must_equal Album.new(nil,Song.new("Go With Me"))
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ describe "collection" do
66
+ let (:hash) { hash_representer do collection :songs end }
67
+
68
+ let (:album) { Album.new.tap do |album|
69
+ album.songs = ["Jackhammer", "Terrible Man"]
70
+ end }
71
+
72
+
73
+ describe "#to_hash" do
74
+ it "renders a block style list per default" do
75
+ album.extend(hash).to_hash.must_equal("songs" => ["Jackhammer", "Terrible Man"])
76
+ end
77
+ end
78
+
79
+
80
+ describe "#from_hash" do
81
+ it "parses a block style list" do
82
+ album.extend(hash).from_hash("songs" => ["Off Key Melody", "Sinking"]).must_equal Album.new(["Off Key Melody", "Sinking"])
83
+
84
+ end
85
+ end
86
+
87
+
88
+ describe "with :class and :extend" do
89
+ hash_song = hash_representer do
90
+ property :name
91
+ property :track
92
+ end
93
+ let (:hash_album) { Module.new do
94
+ include Representable::Hash
95
+ collection :songs, :extend => hash_song, :class => Song
96
+ end }
97
+
98
+ let (:album) { Album.new.tap do |album|
99
+ album.songs = [Song.new("Liar", 1), Song.new("What I Know", 2)]
100
+ end }
101
+
102
+
103
+ describe "#to_hash" do
104
+ it "renders collection of typed property" do
105
+ album.extend(hash_album).to_hash.must_equal("songs" => [{"name" => "Liar", "track" => 1}, {"name" => "What I Know", "track" => 2}])
106
+ end
107
+ end
108
+
109
+ describe "#from_hash" do
110
+ it "parses collection of typed property" do
111
+ album.extend(hash_album).from_hash("songs" => [{"name" => "One Shot Deal", "track" => 4},
112
+ {"name" => "Three Way Dance", "track" => 5}]).must_equal Album.new([Song.new("One Shot Deal", 4), Song.new("Three Way Dance", 5)])
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -128,19 +128,19 @@ module JsonTest
128
128
 
129
129
  describe "#binding_for_definition" do
130
130
  it "returns ObjectBinding" do
131
- assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :class => Hash))
131
+ assert_kind_of Representable::Hash::ObjectBinding, Json.binding_for_definition(Def.new(:band, :class => Hash))
132
132
  end
133
133
 
134
134
  it "returns TextBinding" do
135
- assert_kind_of Json::PropertyBinding, Json.binding_for_definition(Def.new(:band))
135
+ assert_kind_of Representable::Hash::PropertyBinding, Json.binding_for_definition(Def.new(:band))
136
136
  end
137
137
 
138
138
  it "returns HashBinding" do
139
- assert_kind_of Json::HashBinding, Json.binding_for_definition(Def.new(:band, :hash => true))
139
+ assert_kind_of Representable::Hash::HashBinding, Json.binding_for_definition(Def.new(:band, :hash => true))
140
140
  end
141
141
 
142
142
  it "returns CollectionBinding" do
143
- assert_kind_of Json::CollectionBinding, Json.binding_for_definition(Def.new(:band, :collection => true))
143
+ assert_kind_of Representable::Hash::CollectionBinding, Json.binding_for_definition(Def.new(:band, :collection => true))
144
144
  end
145
145
  end
146
146
 
@@ -136,12 +136,6 @@ class RepresentableTest < MiniTest::Spec
136
136
 
137
137
 
138
138
  describe "#property" do
139
- it "creates accessors for the attribute" do
140
- @band = PunkBand.new
141
- assert @band.name = "Bad Religion"
142
- assert_equal "Bad Religion", @band.name
143
- end
144
-
145
139
  describe ":from" do
146
140
  # TODO: do this with all options.
147
141
  it "can be set explicitly" do
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.5
4
+ version: 1.2.6
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-08-27 00:00:00.000000000 Z
12
+ date: 2012-10-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -158,12 +158,13 @@ files:
158
158
  - gemfiles/Gemfile.mongoid-2.4
159
159
  - lib/representable.rb
160
160
  - lib/representable/binding.rb
161
- - lib/representable/bindings/json_bindings.rb
161
+ - lib/representable/bindings/hash_bindings.rb
162
162
  - lib/representable/bindings/xml_bindings.rb
163
163
  - lib/representable/bindings/yaml_bindings.rb
164
164
  - lib/representable/coercion.rb
165
165
  - lib/representable/definition.rb
166
166
  - lib/representable/deprecations.rb
167
+ - lib/representable/hash.rb
167
168
  - lib/representable/hash_methods.rb
168
169
  - lib/representable/json.rb
169
170
  - lib/representable/json/collection.rb
@@ -176,7 +177,8 @@ files:
176
177
  - representable.gemspec
177
178
  - test/coercion_test.rb
178
179
  - test/definition_test.rb
179
- - test/json_bindings_test.rb
180
+ - test/hash_bindings_test.rb
181
+ - test/hash_test.rb
180
182
  - test/json_test.rb
181
183
  - test/mongoid_test.rb
182
184
  - test/polymorphic_test.rb