representable 2.3.0 → 2.4.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/CHANGES.md +47 -0
- data/Gemfile +5 -0
- data/README.md +33 -0
- data/lib/representable.rb +60 -73
- data/lib/representable/binding.rb +37 -194
- data/lib/representable/cached.rb +10 -46
- data/lib/representable/coercion.rb +8 -8
- data/lib/representable/config.rb +15 -75
- data/lib/representable/debug.rb +41 -59
- data/lib/representable/declarative.rb +34 -53
- data/lib/representable/decorator.rb +11 -40
- data/lib/representable/definition.rb +14 -15
- data/lib/representable/deprecations.rb +90 -0
- data/lib/representable/deserializer.rb +87 -82
- data/lib/representable/for_collection.rb +5 -3
- data/lib/representable/hash.rb +5 -3
- data/lib/representable/hash/binding.rb +6 -15
- data/lib/representable/hash/collection.rb +10 -6
- data/lib/representable/hash_methods.rb +5 -5
- data/lib/representable/insert.rb +31 -0
- data/lib/representable/json.rb +7 -3
- data/lib/representable/json/hash.rb +1 -1
- data/lib/representable/object/binding.rb +5 -5
- data/lib/representable/parse_strategies.rb +37 -3
- data/lib/representable/pipeline.rb +37 -5
- data/lib/representable/pipeline_factories.rb +88 -0
- data/lib/representable/serializer.rb +38 -44
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +4 -0
- data/lib/representable/xml/binding.rb +25 -31
- data/lib/representable/xml/collection.rb +5 -3
- data/lib/representable/xml/hash.rb +7 -2
- data/lib/representable/yaml.rb +6 -3
- data/lib/representable/yaml/binding.rb +4 -4
- data/representable.gemspec +3 -3
- data/test/---deserialize-pipeline_test.rb +37 -0
- data/test/binding_test.rb +7 -7
- data/test/cached_test.rb +31 -19
- data/test/coercion_test.rb +2 -2
- data/test/config/inherit_test.rb +13 -12
- data/test/config_test.rb +12 -67
- data/test/decorator_test.rb +4 -5
- data/test/default_test.rb +34 -0
- data/test/defaults_options_test.rb +93 -0
- data/test/definition_test.rb +19 -39
- data/test/exec_context_test.rb +1 -1
- data/test/filter_test.rb +18 -20
- data/test/getter_setter_test.rb +1 -8
- data/test/hash_bindings_test.rb +13 -13
- data/test/heritage_test.rb +62 -0
- data/test/if_test.rb +1 -0
- data/test/inherit_test.rb +5 -3
- data/test/instance_test.rb +3 -4
- data/test/json_test.rb +3 -59
- data/test/lonely_test.rb +47 -3
- data/test/nested_test.rb +8 -2
- data/test/pipeline_test.rb +259 -0
- data/test/populator_test.rb +76 -0
- data/test/realistic_benchmark.rb +39 -7
- data/test/render_nil_test.rb +21 -0
- data/test/represent_test.rb +2 -2
- data/test/representable_test.rb +33 -103
- data/test/schema_test.rb +5 -15
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +1 -1
- data/test/test_helper.rb +6 -0
- data/test/uncategorized_test.rb +67 -0
- data/test/xml_bindings_test.rb +6 -6
- data/test/xml_test.rb +6 -6
- metadata +33 -13
- data/lib/representable/apply.rb +0 -13
- data/lib/representable/inheritable.rb +0 -71
- data/lib/representable/mapper.rb +0 -83
- data/lib/representable/populator.rb +0 -56
- data/test/inheritable_test.rb +0 -97
data/lib/representable/cached.rb
CHANGED
@@ -1,58 +1,22 @@
|
|
1
1
|
module Representable
|
2
2
|
# Using this module only makes sense with Decorator representers.
|
3
3
|
module Cached
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Every binding in turn stores its nested representer (if it has one), implementing a recursive caching.
|
9
|
-
#
|
10
|
-
# Decorator -> Mapper -> [Binding->Decorator, Binding]
|
11
|
-
def representable_mapper(format, options)
|
12
|
-
@mapper ||= super.tap do |mapper|
|
13
|
-
mapper.bindings(represented, options).each { |binding| binding.extend(Binding) }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# replace represented for each property in this representer.
|
18
|
-
def update!(represented)
|
19
|
-
@represented = represented
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
# TODO: also for deserializer.
|
24
|
-
# TODO: create Populator in Binding, too (easier to override).
|
25
|
-
module Binding
|
26
|
-
def serializer
|
27
|
-
@__serializer ||= super.tap do |serializer|
|
28
|
-
serializer.extend(Serializer)
|
29
|
-
end
|
30
|
-
end
|
4
|
+
module Property
|
5
|
+
def property(*)
|
6
|
+
super.tap do |property|
|
7
|
+
binding_builder = format_engine::Binding
|
31
8
|
|
32
|
-
|
33
|
-
@__deserializer ||= super.tap do |deserializer|
|
34
|
-
deserializer.extend(Serializer)
|
9
|
+
map << binding_builder.build(property)
|
35
10
|
end
|
36
11
|
end
|
37
12
|
end
|
38
13
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
return representer.update!(object)
|
43
|
-
end
|
44
|
-
|
45
|
-
# puts "--------> caching representer for #{object} in #{@binding.object_id}"
|
46
|
-
@binding.cached_representer = super(mod, object)
|
47
|
-
end
|
14
|
+
def self.included(includer)
|
15
|
+
includer.extend(Property)
|
16
|
+
end
|
48
17
|
|
49
|
-
|
50
|
-
|
51
|
-
def item_deserializer
|
52
|
-
@__item_deserializer ||= super.tap do |deserializer|
|
53
|
-
deserializer.extend(Serializer)
|
54
|
-
end
|
55
|
-
end
|
18
|
+
def representable_map(*)
|
19
|
+
self.class.map
|
56
20
|
end
|
57
21
|
end
|
58
22
|
end
|
@@ -9,8 +9,8 @@ module Representable
|
|
9
9
|
|
10
10
|
# This gets called when the :render_filter or :parse_filter option is evaluated.
|
11
11
|
# Usually the Coercer instance is an element in a Pipeline to allow >1 filters per property.
|
12
|
-
def call(
|
13
|
-
Virtus::Attribute.build(@type).coerce(
|
12
|
+
def call(input, options)
|
13
|
+
Virtus::Attribute.build(@type).coerce(input)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -24,13 +24,13 @@ module Representable
|
|
24
24
|
|
25
25
|
|
26
26
|
module ClassMethods
|
27
|
-
def
|
28
|
-
|
27
|
+
def property(name, options={}, &block)
|
28
|
+
super.tap do |definition|
|
29
|
+
return definition unless type = options[:type]
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
super
|
31
|
+
definition.merge!(render_filter: coercer = Coercer.new(type))
|
32
|
+
definition.merge!(parse_filter: coercer)
|
33
|
+
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/lib/representable/config.rb
CHANGED
@@ -1,91 +1,31 @@
|
|
1
|
-
# Caching of Bindings
|
2
|
-
# in Decorators, this could be an instance class var. when inherited, it is automatically busted.
|
3
|
-
# however, in modules we can't do that. we never know when a module is "finalised", so we don't really know when to bust the cache.
|
4
|
-
|
5
1
|
module Representable
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# You may access/alter the property Definitions using #each, #collect, #add, #get.
|
2
|
+
# Stores Definitions from ::property. It preserves the adding order (1.9+).
|
3
|
+
# Same-named properties get overridden, just like in a Hash.
|
10
4
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# Keep in mind that performance doesn't matter here as 99.9% of all representers are created
|
17
|
-
# at compile-time.
|
18
|
-
|
19
|
-
# Stores Definitions from ::property. It preserves the adding order (1.9+).
|
20
|
-
# Same-named properties get overridden, just like in a Hash.
|
21
|
-
#
|
22
|
-
# Overwrite definition_class if you need a custom Definition object (helpful when using
|
23
|
-
# representable in other gems).
|
24
|
-
class Definitions < Inheritable::Hash # TODO: cloneable!
|
25
|
-
def initialize(definition_class)
|
26
|
-
@definition_class = definition_class
|
27
|
-
super()
|
28
|
-
end
|
29
|
-
|
30
|
-
def add(name, options, &block)
|
31
|
-
if options[:inherit] and parent_property = get(name) # i like that: the :inherit shouldn't be handled outside.
|
32
|
-
return parent_property.merge!(options, &block)
|
33
|
-
end
|
34
|
-
options.delete(:inherit) # TODO: can we handle the :inherit in one single place?
|
35
|
-
|
36
|
-
self[name.to_s] = definition_class.new(name, options, &block)
|
37
|
-
end
|
38
|
-
|
39
|
-
def get(name)
|
40
|
-
self[name.to_s]
|
41
|
-
end
|
42
|
-
|
43
|
-
def remove(name)
|
44
|
-
delete(name.to_s)
|
45
|
-
end
|
46
|
-
|
47
|
-
extend Forwardable
|
48
|
-
def_delegators :values, :each # so we look like an array. this is only used in Mapper. we could change that so we don't need to hide the hash.
|
49
|
-
|
50
|
-
private
|
51
|
-
attr_reader :definition_class
|
5
|
+
# Overwrite definition_class if you need a custom Definition object (helpful when using
|
6
|
+
# representable in other gems).
|
7
|
+
class Config < ::Declarative::Definitions
|
8
|
+
def remove(name)
|
9
|
+
delete(name.to_s)
|
52
10
|
end
|
53
11
|
|
54
|
-
|
55
|
-
|
56
|
-
super()
|
57
|
-
merge!(
|
58
|
-
:features => @features = Inheritable::Hash.new,
|
59
|
-
:definitions => @definitions = Definitions.new(definition_class),
|
60
|
-
:options => @options = Inheritable::Hash.new,
|
61
|
-
:wrap => nil )
|
12
|
+
def options # FIXME: this is not inherited.
|
13
|
+
@options ||= {}
|
62
14
|
end
|
63
|
-
attr_reader :options
|
64
|
-
|
65
|
-
def features
|
66
|
-
@features.keys
|
67
|
-
end
|
68
|
-
|
69
|
-
# delegate #collect etc to Definitions instance.
|
70
|
-
extend Forwardable
|
71
|
-
def_delegators :@definitions, :get, :add, :each, :size, :remove
|
72
|
-
# #collect comes from Hash and then gets delegated to @definitions. don't like that.
|
73
15
|
|
74
16
|
def wrap=(value)
|
75
17
|
value = value.to_s if value.is_a?(Symbol)
|
76
|
-
|
18
|
+
@wrap = Uber::Options::Value.new(value)
|
77
19
|
end
|
78
20
|
|
79
21
|
# Computes the wrap string or returns false.
|
80
|
-
def wrap_for(
|
81
|
-
return unless
|
82
|
-
|
83
|
-
value = self[:wrap].evaluate(context, *args)
|
22
|
+
def wrap_for(represented, *args, &block)
|
23
|
+
return unless @wrap
|
84
24
|
|
85
|
-
|
25
|
+
value = @wrap.evaluate(represented, *args)
|
86
26
|
|
87
|
-
return
|
88
|
-
|
27
|
+
return value if value != true
|
28
|
+
infer_name_for(represented.class.name)
|
89
29
|
end
|
90
30
|
|
91
31
|
private
|
data/lib/representable/debug.rb
CHANGED
@@ -1,36 +1,26 @@
|
|
1
1
|
module Representable
|
2
2
|
module Debug
|
3
|
-
def
|
4
|
-
|
3
|
+
def update_properties_from(doc, options, format)
|
4
|
+
puts
|
5
|
+
puts "[Deserialize]........."
|
6
|
+
puts "[Deserialize] document #{doc.inspect}"
|
7
|
+
super
|
5
8
|
end
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def create_representation_with(doc, options, format)
|
16
|
-
puts
|
17
|
-
puts "[Serialize]........."
|
18
|
-
puts "[Serialize]"
|
19
|
-
super
|
20
|
-
end
|
10
|
+
def create_representation_with(doc, options, format)
|
11
|
+
puts
|
12
|
+
puts "[Serialize]........."
|
13
|
+
puts "[Serialize]"
|
14
|
+
super
|
15
|
+
end
|
21
16
|
|
22
|
-
|
23
|
-
|
17
|
+
def representable_map(*)
|
18
|
+
super.tap do |arr|
|
19
|
+
arr.collect { |bin| bin.extend(Binding) }
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
27
23
|
module Binding
|
28
|
-
def read(doc)
|
29
|
-
value = super
|
30
|
-
puts " #read --> #{value.inspect}"
|
31
|
-
value
|
32
|
-
end
|
33
|
-
|
34
24
|
def evaluate_option(name, *args, &block)
|
35
25
|
puts "=====#{self[name]}" if name ==:prepare
|
36
26
|
puts (evaled = self[name]) ?
|
@@ -42,54 +32,46 @@ module Representable
|
|
42
32
|
value
|
43
33
|
end
|
44
34
|
|
45
|
-
def
|
46
|
-
super.extend(
|
35
|
+
def parse_pipeline(*)
|
36
|
+
super.extend(Pipeline::Debug)
|
47
37
|
end
|
48
38
|
|
49
|
-
def
|
50
|
-
super.extend(
|
39
|
+
def render_pipeline(*)
|
40
|
+
super.extend(Pipeline::Debug)
|
51
41
|
end
|
52
42
|
end
|
43
|
+
end
|
53
44
|
|
54
|
-
module Populator
|
55
|
-
def deserialize(fragment)
|
56
|
-
puts " Populator#deserialize: #{fragment.inspect}"
|
57
|
-
puts " : typed? is false, skipping Deserializer." if ! @binding.typed?
|
58
|
-
super
|
59
|
-
end
|
60
45
|
|
61
|
-
|
62
|
-
|
63
|
-
|
46
|
+
module Pipeline::Debug
|
47
|
+
def call(input, options)
|
48
|
+
puts "Pipeline#call: #{inspect}"
|
49
|
+
puts " input: #{input.inspect}"
|
50
|
+
super
|
64
51
|
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
value = super
|
69
|
-
puts " Deserializer#create_object: --> #{value.inspect}"
|
70
|
-
value
|
71
|
-
end
|
72
|
-
end
|
53
|
+
def evaluate(block, memo, options)
|
54
|
+
block.extend(Pipeline::Debug) if block.is_a?(Collect)
|
73
55
|
|
74
|
-
|
75
|
-
|
76
|
-
puts "
|
77
|
-
super
|
56
|
+
puts " Pipeline : -> #{_inspect_function(block)} "
|
57
|
+
super.tap do |res|
|
58
|
+
puts " Pipeline : result: #{res.inspect}"
|
78
59
|
end
|
79
60
|
end
|
80
61
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
62
|
+
def inspect
|
63
|
+
functions = collect do |func|
|
64
|
+
_inspect_function(func)
|
65
|
+
end.join(", ")
|
66
|
+
"#{self.class.to_s.split("::").last}[#{functions}]"
|
67
|
+
end
|
87
68
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
69
|
+
# prints SkipParse instead of <Proc>. i know, i can make this better, but not now.
|
70
|
+
def _inspect_function(func)
|
71
|
+
return func.extend(Pipeline::Debug).inspect if func.is_a?(Collect)
|
72
|
+
return func unless func.is_a?(Proc)
|
73
|
+
File.readlines(func.source_location[0])[func.source_location[1]-1].match(/^\s+(\w+)/)[1]
|
93
74
|
end
|
94
75
|
end
|
95
76
|
end
|
77
|
+
|
@@ -1,16 +1,13 @@
|
|
1
1
|
module Representable
|
2
2
|
module Declarative
|
3
|
-
def representable_attrs
|
4
|
-
@representable_attrs ||= build_config
|
5
|
-
end
|
6
|
-
|
7
3
|
def representation_wrap=(name)
|
8
|
-
|
4
|
+
heritage.record(:representation_wrap=, name)
|
5
|
+
|
6
|
+
definitions.wrap = name
|
9
7
|
end
|
10
8
|
|
11
9
|
def collection(name, options={}, &block)
|
12
|
-
options
|
13
|
-
property(name, options, &block)
|
10
|
+
property(name, options.merge(collection: true), &block)
|
14
11
|
end
|
15
12
|
|
16
13
|
def hash(name=nil, options={}, &block)
|
@@ -20,67 +17,51 @@ module Representable
|
|
20
17
|
property(name, options, &block)
|
21
18
|
end
|
22
19
|
|
23
|
-
# Allows you to nest a block of properties in a separate section while still mapping
|
20
|
+
# Allows you to nest a block of properties in a separate section while still mapping
|
21
|
+
# them to the original object.
|
24
22
|
def nested(name, options={}, &block)
|
25
23
|
options = options.merge(
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
|
30
|
-
|
24
|
+
getter: ->(options) { self },
|
25
|
+
setter: ->(options) { },
|
26
|
+
instance: ->(options) { self },
|
27
|
+
)
|
28
|
+
|
29
|
+
if block
|
30
|
+
options[:_nested_builder] = Decorator.nested_builder
|
31
|
+
options[:_base] = Decorator.default_nested_class
|
32
|
+
end
|
31
33
|
|
32
34
|
property(name, options, &block)
|
33
35
|
end
|
34
36
|
|
35
|
-
def property(name, options={}, &block)
|
36
|
-
representable_attrs.add(name, options) do |default_options| # handles :inherit.
|
37
|
-
build_definition(name, default_options, &block)
|
38
|
-
end
|
39
|
-
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
feature *features # Representable::JSON or similar.
|
45
|
-
include base if base # base when :inherit, or in decorator.
|
38
|
+
include ::Declarative::Schema::DSL # ::property
|
39
|
+
include ::Declarative::Schema::Feature
|
40
|
+
include ::Declarative::Heritage::DSL
|
46
41
|
|
47
|
-
|
48
|
-
|
42
|
+
def default_nested_class
|
43
|
+
Module.new # FIXME: make that unnecessary in Declarative
|
49
44
|
end
|
50
45
|
|
51
|
-
private
|
52
|
-
# This method is meant to be overridden if you want to add or change DSL options.
|
53
|
-
# The options hash is already initialized, add or change elements as you need.
|
54
|
-
#
|
55
|
-
# Example:
|
56
|
-
#
|
57
|
-
# def build_definition(name, options, &block)
|
58
|
-
# options[:pass_options] = true
|
59
|
-
# options[:render_filter] << Sanitizer.new if options[:sanitize]
|
60
|
-
# options[:setter] << lambda { |fragment, options| send("set_#{options.binding.name}=", fragment) }
|
61
|
-
# super # don't forget to call super
|
62
|
-
# end
|
63
|
-
def build_definition(name, options, &block)
|
64
|
-
base = nil
|
65
|
-
|
66
|
-
if options[:inherit] # TODO: move this to Definition.
|
67
|
-
base = representable_attrs.get(name).representer_module
|
68
|
-
end # FIXME: can we handle this in super/Definition.new ?
|
69
46
|
|
70
|
-
|
71
|
-
|
72
|
-
|
47
|
+
NestedBuilder = ->(options) do
|
48
|
+
Module.new do
|
49
|
+
include Representable # FIXME: do we really need this?
|
50
|
+
feature *options[:_features]
|
51
|
+
include *options[:_base] # base when :inherit, or in decorator.
|
52
|
+
|
53
|
+
module_eval &options[:_block]
|
73
54
|
end
|
74
55
|
end
|
75
56
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
representer.build_inline(base, features, name, options, &block)
|
57
|
+
def nested_builder
|
58
|
+
NestedBuilder
|
80
59
|
end
|
81
60
|
|
82
|
-
def
|
83
|
-
Config.new
|
61
|
+
def definitions
|
62
|
+
@definitions ||= Config.new(Representable::Definition)
|
84
63
|
end
|
85
|
-
|
64
|
+
|
65
|
+
alias_method :representable_attrs, :definitions
|
66
|
+
end
|
86
67
|
end
|