representable 1.8.5 → 2.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGES.md +36 -0
- data/Gemfile +3 -3
- data/README.md +62 -2
- data/Rakefile +1 -1
- data/lib/representable.rb +32 -109
- data/lib/representable/TODO.getting_serious +7 -1
- data/lib/representable/autoload.rb +10 -0
- data/lib/representable/binding.rb +20 -16
- data/lib/representable/bindings/xml_bindings.rb +0 -1
- data/lib/representable/coercion.rb +23 -31
- data/lib/representable/config.rb +45 -46
- data/lib/representable/declarative.rb +78 -0
- data/lib/representable/decorator.rb +39 -10
- data/lib/representable/definition.rb +40 -33
- data/lib/representable/deserializer.rb +2 -0
- data/lib/representable/for_collection.rb +25 -0
- data/lib/representable/hash.rb +4 -8
- data/lib/representable/hash/collection.rb +2 -9
- data/lib/representable/hash_methods.rb +0 -7
- data/lib/representable/inheritable.rb +50 -0
- data/lib/representable/json.rb +3 -9
- data/lib/representable/json/collection.rb +1 -3
- data/lib/representable/json/hash.rb +4 -9
- data/lib/representable/mapper.rb +8 -5
- data/lib/representable/parse_strategies.rb +1 -0
- data/lib/representable/pipeline.rb +14 -0
- data/lib/representable/represent.rb +6 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +3 -18
- data/lib/representable/xml/collection.rb +2 -4
- data/lib/representable/xml/hash.rb +2 -10
- data/lib/representable/yaml.rb +1 -20
- data/representable.gemspec +2 -2
- data/test/class_test.rb +5 -10
- data/test/coercion_test.rb +31 -92
- data/test/config/inherit_test.rb +128 -0
- data/test/config_test.rb +114 -80
- data/test/definition_test.rb +107 -64
- data/test/features_test.rb +41 -0
- data/test/filter_test.rb +59 -0
- data/test/for_collection_test.rb +74 -0
- data/test/inherit_test.rb +44 -3
- data/test/inheritable_test.rb +97 -0
- data/test/inline_test.rb +0 -18
- data/test/instance_test.rb +0 -19
- data/test/json_test.rb +9 -44
- data/test/lonely_test.rb +1 -0
- data/test/parse_strategy_test.rb +30 -0
- data/test/represent_test.rb +88 -0
- data/test/representable_test.rb +3 -50
- data/test/schema_test.rb +123 -0
- data/test/test_helper.rb +1 -1
- data/test/xml_test.rb +34 -38
- metadata +25 -15
- data/lib/representable/decorator/coercion.rb +0 -4
- data/lib/representable/readable_writeable.rb +0 -29
- data/test/inheritance_test.rb +0 -22
@@ -5,6 +5,8 @@ module Representable
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def deserialize(fragment)
|
8
|
+
# puts "deserialize #{@binding.name}" # TODO: introduce Representable::Debug.
|
9
|
+
|
8
10
|
# next step: get rid of collect.
|
9
11
|
fragment.enum_for(:each_with_index).collect do |item_fragment, i|
|
10
12
|
@deserializer = ObjectDeserializer.new(@binding)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Representable
|
2
|
+
# Gives us Representer::for_collection and its configuration directive
|
3
|
+
# ::collection_representer.
|
4
|
+
module ForCollection
|
5
|
+
def for_collection
|
6
|
+
# this is done at run-time, not a big fan of this. however, it saves us from inheritance/self problems.
|
7
|
+
@collection_representer ||= collection_representer!({}) # DON'T make it inheritable as it would inherit the wrong singular.
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def collection_representer!(options)
|
12
|
+
singular = self
|
13
|
+
|
14
|
+
# what happens here is basically
|
15
|
+
# Module.new { include Representable::JSON::Collection; ... }
|
16
|
+
build_inline(nil, [singular.collection_representer_class], "", {}) {
|
17
|
+
items options.merge(:extend => singular)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection_representer(options={})
|
22
|
+
@collection_representer = collection_representer!(options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/representable/hash.rb
CHANGED
@@ -9,19 +9,15 @@ module Representable
|
|
9
9
|
def self.included(base)
|
10
10
|
base.class_eval do
|
11
11
|
include Representable # either in Hero or HeroRepresentation.
|
12
|
-
extend ClassMethods
|
12
|
+
extend ClassMethods
|
13
|
+
register_feature Representable::Hash
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
17
|
|
17
18
|
module ClassMethods
|
18
|
-
def
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
def representer_engine
|
24
|
-
Representable::Hash
|
19
|
+
def collection_representer_class
|
20
|
+
Collection
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
@@ -4,9 +4,9 @@ module Representable::Hash
|
|
4
4
|
|
5
5
|
def self.included(base)
|
6
6
|
base.class_eval do
|
7
|
-
include Representable
|
7
|
+
include Representable::Hash
|
8
8
|
extend ClassMethods
|
9
|
-
|
9
|
+
representable_attrs.add(:_self, {:collection => true})
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -28,12 +28,5 @@ module Representable::Hash
|
|
28
28
|
value = bin.deserialize_from(doc)
|
29
29
|
represented.replace(value)
|
30
30
|
end
|
31
|
-
|
32
|
-
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
33
|
-
def representable_attrs
|
34
|
-
attrs = super
|
35
|
-
attrs << Definition.new(:_self, :collection => true) if attrs.size == 0
|
36
|
-
attrs
|
37
|
-
end
|
38
31
|
end
|
39
32
|
end
|
@@ -1,12 +1,5 @@
|
|
1
1
|
module Representable
|
2
2
|
module HashMethods
|
3
|
-
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
4
|
-
def representable_attrs
|
5
|
-
attrs = super
|
6
|
-
attrs << Definition.new(*definition_opts) if attrs.size == 0
|
7
|
-
attrs
|
8
|
-
end
|
9
|
-
|
10
3
|
def create_representation_with(doc, options, format)
|
11
4
|
bin = representable_mapper(format, options).bindings.first
|
12
5
|
hash = filter_keys_for(represented, options)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Representable
|
2
|
+
# Objects marked cloneable will be cloned in #inherit!.
|
3
|
+
module Cloneable
|
4
|
+
end
|
5
|
+
|
6
|
+
# Objects marked cloneable will be inherit!ed in #inherit! when available in parent and child.
|
7
|
+
module Inheritable
|
8
|
+
include Cloneable # all Inheritable are also Cloneable since #clone is one step of our inheritance.
|
9
|
+
|
10
|
+
class Array < ::Array
|
11
|
+
include Inheritable
|
12
|
+
|
13
|
+
def inherit!(parent)
|
14
|
+
push(*parent.clone)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Hash < ::Hash
|
19
|
+
include Inheritable
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
def inherit!(parent)
|
23
|
+
#merge!(parent.clone)
|
24
|
+
for key in (parent.keys + keys).uniq
|
25
|
+
next unless parent_value = parent[key]
|
26
|
+
|
27
|
+
self[key].inherit!(parent_value) and next if self[key].is_a?(Inheritable)
|
28
|
+
self[key] = parent_value.clone and next if parent_value.is_a?(Cloneable)
|
29
|
+
|
30
|
+
self[key] = parent_value # merge! behaviour
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def clone
|
37
|
+
self.class[ collect { |k,v| [k, clone_value(v)] } ]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def clone_value(value)
|
42
|
+
return value.clone if value.is_a?(Cloneable)
|
43
|
+
value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
include InstanceMethods
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/representable/json.rb
CHANGED
@@ -11,20 +11,14 @@ module Representable
|
|
11
11
|
base.class_eval do
|
12
12
|
include Representable # either in Hero or HeroRepresentation.
|
13
13
|
extend ClassMethods # DISCUSS: do that only for classes?
|
14
|
-
|
14
|
+
register_feature Representable::JSON
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
|
19
19
|
module ClassMethods
|
20
|
-
|
21
|
-
|
22
|
-
create_represented(*args, &block).from_json(*args)
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def representer_engine
|
27
|
-
Representable::JSON
|
20
|
+
def collection_representer_class
|
21
|
+
JSON::Collection
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
@@ -2,15 +2,15 @@ require 'representable/json'
|
|
2
2
|
require 'representable/hash_methods'
|
3
3
|
|
4
4
|
module Representable::JSON
|
5
|
+
# "Lonely Hash" support.
|
5
6
|
module Hash
|
6
|
-
include Representable::JSON
|
7
|
-
include Representable::HashMethods
|
8
|
-
|
9
7
|
def self.included(base)
|
10
8
|
base.class_eval do
|
11
9
|
include Representable
|
12
10
|
extend ClassMethods
|
13
|
-
|
11
|
+
include Representable::JSON
|
12
|
+
include Representable::HashMethods
|
13
|
+
representable_attrs.add(:_self, {:hash => true})
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -20,10 +20,5 @@ module Representable::JSON
|
|
20
20
|
hash(:_self, options, &block)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
|
25
|
-
def definition_opts
|
26
|
-
[:_self, {:hash => true, :use_attributes => true}]
|
27
|
-
end
|
28
23
|
end
|
29
24
|
end
|
data/lib/representable/mapper.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'representable/readable_writeable'
|
2
|
-
|
3
1
|
module Representable
|
4
2
|
# Render and parse by looping over the representer's properties and dispatching to bindings.
|
5
3
|
# Conditionals are handled here, too.
|
@@ -28,18 +26,19 @@ module Representable
|
|
28
26
|
|
29
27
|
private
|
30
28
|
def serialize_property(binding, doc, options)
|
31
|
-
return if skip_property?(binding, options)
|
29
|
+
return if skip_property?(binding, options.merge(:action => :serialize))
|
32
30
|
compile_fragment(binding, doc)
|
33
31
|
end
|
34
32
|
|
35
33
|
def deserialize_property(binding, doc, options)
|
36
|
-
return if skip_property?(binding, options)
|
34
|
+
return if skip_property?(binding, options.merge(:action => :deserialize))
|
37
35
|
uncompile_fragment(binding, doc)
|
38
36
|
end
|
39
37
|
|
40
38
|
# Checks and returns if the property should be included.
|
41
39
|
def skip_property?(binding, options)
|
42
40
|
return true if skip_excluded_property?(binding, options) # no need for further evaluation when :exclude'ed
|
41
|
+
return true if skip_protected_property(binding, options)
|
43
42
|
|
44
43
|
skip_conditional_property?(binding)
|
45
44
|
end
|
@@ -56,6 +55,11 @@ module Representable
|
|
56
55
|
not binding.send(:evaluate_option, :if)
|
57
56
|
end
|
58
57
|
|
58
|
+
# DISCUSS: this could be just another :if option in a Pipeline?
|
59
|
+
def skip_protected_property(binding, options)
|
60
|
+
options[:action] == :serialize ? binding[:readable] == false : binding[:writeable] == false
|
61
|
+
end
|
62
|
+
|
59
63
|
def compile_fragment(bin, doc)
|
60
64
|
bin.compile_fragment(doc)
|
61
65
|
end
|
@@ -66,6 +70,5 @@ module Representable
|
|
66
70
|
end
|
67
71
|
|
68
72
|
include Methods
|
69
|
-
include ReadableWriteable # DISCUSS: make this pluggable.
|
70
73
|
end
|
71
74
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Representable
|
2
|
+
# Allows to implement a pipeline of filters where a value gets passed in and the result gets
|
3
|
+
# passed to the next callable object.
|
4
|
+
#
|
5
|
+
# Note: this is still experimental.
|
6
|
+
class Pipeline < Array
|
7
|
+
include Uber::Callable
|
8
|
+
# include Representable::Cloneable
|
9
|
+
|
10
|
+
def call(context, value, *args)
|
11
|
+
inject(value) { |memo, block| block.call(memo, *args) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/representable/xml.rb
CHANGED
@@ -9,6 +9,7 @@ module Representable
|
|
9
9
|
include Representable
|
10
10
|
extend ClassMethods
|
11
11
|
self.representation_wrap = true # let representable compute it.
|
12
|
+
register_feature Representable::XML
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
@@ -18,24 +19,8 @@ module Representable
|
|
18
19
|
representable_attrs.options[:remove_namespaces] = true
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
# Accepts a block yielding the currently iterated Definition. If the block returns false
|
24
|
-
# the property is skipped.
|
25
|
-
#
|
26
|
-
# Example:
|
27
|
-
# band.from_xml("<band><name>Nofx</name></band>")
|
28
|
-
def from_xml(*args, &block)
|
29
|
-
create_represented(*args, &block).from_xml(*args)
|
30
|
-
end
|
31
|
-
|
32
|
-
def from_node(*args, &block)
|
33
|
-
create_represented(*args, &block).from_node(*args)
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
def representer_engine
|
38
|
-
Representable::XML
|
22
|
+
def collection_representer_class
|
23
|
+
Collection
|
39
24
|
end
|
40
25
|
end
|
41
26
|
|
@@ -3,10 +3,8 @@ module Representable::XML
|
|
3
3
|
include Representable::XML
|
4
4
|
|
5
5
|
def self.included(base)
|
6
|
-
base.
|
7
|
-
|
8
|
-
include Methods
|
9
|
-
end
|
6
|
+
base.send :include, Representable::Hash::Collection
|
7
|
+
base.send :include, Methods
|
10
8
|
end
|
11
9
|
|
12
10
|
module Methods
|
@@ -10,6 +10,7 @@ module Representable::XML
|
|
10
10
|
base.class_eval do
|
11
11
|
include Representable
|
12
12
|
extend ClassMethods
|
13
|
+
representable_attrs.add(:_self, {:hash => true, :use_attributes => true})
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -19,11 +20,6 @@ module Representable::XML
|
|
19
20
|
hash :_self, options.merge!(:use_attributes => true)
|
20
21
|
end
|
21
22
|
end
|
22
|
-
|
23
|
-
|
24
|
-
def definition_opts
|
25
|
-
[:_self, {:hash => true, :use_attributes => true}]
|
26
|
-
end
|
27
23
|
end
|
28
24
|
|
29
25
|
module Hash
|
@@ -34,6 +30,7 @@ module Representable::XML
|
|
34
30
|
base.class_eval do
|
35
31
|
include Representable
|
36
32
|
extend ClassMethods
|
33
|
+
representable_attrs.add(:_self, {:hash => true})
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
@@ -43,10 +40,5 @@ module Representable::XML
|
|
43
40
|
hash :_self, options
|
44
41
|
end
|
45
42
|
end
|
46
|
-
|
47
|
-
|
48
|
-
def definition_opts
|
49
|
-
[:_self, {:hash => true}]
|
50
|
-
end
|
51
43
|
end
|
52
44
|
end
|
data/lib/representable/yaml.rb
CHANGED
@@ -7,27 +7,8 @@ module Representable
|
|
7
7
|
def self.included(base)
|
8
8
|
base.class_eval do
|
9
9
|
include Representable
|
10
|
-
extend ClassMethods
|
11
10
|
#self.representation_wrap = true # let representable compute it.
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
module ClassMethods
|
17
|
-
# Creates a new Ruby object from XML using mapping information declared in the class.
|
18
|
-
#
|
19
|
-
# Accepts a block yielding the currently iterated Definition. If the block returns false
|
20
|
-
# the property is skipped.
|
21
|
-
#
|
22
|
-
# Example:
|
23
|
-
# band.from_xml("<band><name>Nofx</name></band>")
|
24
|
-
def from_yaml(*args, &block)
|
25
|
-
create_represented(*args, &block).from_yaml(*args)
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
def representer_engine
|
30
|
-
Representable::YAML
|
11
|
+
register_feature Representable::YAML
|
31
12
|
end
|
32
13
|
end
|
33
14
|
|
data/representable.gemspec
CHANGED
@@ -21,13 +21,13 @@ Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
s.add_dependency "nokogiri"
|
23
23
|
s.add_dependency "multi_json"
|
24
|
-
s.add_dependency "uber"
|
24
|
+
s.add_dependency "uber", "~> 0.0.7"
|
25
25
|
|
26
26
|
s.add_development_dependency "rake"
|
27
27
|
s.add_development_dependency "test_xml", ">= 0.1.6"
|
28
28
|
s.add_development_dependency "minitest", "~> 5.0.0"
|
29
29
|
s.add_development_dependency "mocha", ">= 0.13.0"
|
30
30
|
s.add_development_dependency "mongoid"
|
31
|
-
s.add_development_dependency "virtus"
|
31
|
+
s.add_development_dependency "virtus"
|
32
32
|
s.add_development_dependency "json", '~> 1.7.7'
|
33
33
|
end
|
data/test/class_test.rb
CHANGED
@@ -39,21 +39,16 @@ class ClassTest < BaseTest
|
|
39
39
|
end
|
40
40
|
|
41
41
|
|
42
|
-
|
42
|
+
# this throws a DeserializationError now.
|
43
|
+
describe "lambda { nil }" do
|
43
44
|
representer! do
|
44
45
|
property :title, :class => nil
|
45
46
|
end
|
46
47
|
|
47
|
-
it
|
48
|
-
|
49
|
-
|
50
|
-
def from_hash(hash, *args)
|
51
|
-
hash
|
52
|
-
end
|
48
|
+
it do
|
49
|
+
assert_raises Representable::DeserializeError do
|
50
|
+
OpenStruct.new.extend(representer).from_hash({"title" => {}})
|
53
51
|
end
|
54
|
-
|
55
|
-
song = OpenStruct.new.extend(representer).from_hash(hash = {"title" => object})
|
56
|
-
song.title.must_equal object
|
57
52
|
end
|
58
53
|
end
|
59
54
|
|