representable 3.0.0 → 3.1.0
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 +5 -5
- data/.github/workflows/ci.yml +17 -0
- data/CHANGES.md +25 -0
- data/Gemfile +4 -12
- data/LICENSE +1 -1
- data/README.md +6 -6
- data/Rakefile +1 -6
- data/TODO +1 -3
- data/TODO-4.0.md +72 -0
- data/lib/representable.rb +19 -25
- data/lib/representable/binding.rb +32 -12
- data/lib/representable/cached.rb +1 -1
- data/lib/representable/coercion.rb +8 -6
- data/lib/representable/config.rb +13 -3
- data/lib/representable/debug.rb +23 -15
- data/lib/representable/declarative.rb +12 -7
- data/lib/representable/decorator.rb +1 -1
- data/lib/representable/definition.rb +7 -3
- data/lib/representable/deserializer.rb +5 -4
- data/lib/representable/for_collection.rb +1 -1
- data/lib/representable/hash.rb +9 -2
- data/lib/representable/hash/allow_symbols.rb +9 -11
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/hash/collection.rb +4 -2
- data/lib/representable/hash_methods.rb +3 -2
- data/lib/representable/insert.rb +1 -1
- data/lib/representable/json.rb +8 -7
- data/lib/representable/json/collection.rb +3 -0
- data/lib/representable/object.rb +1 -1
- data/lib/representable/object/binding.rb +5 -1
- data/lib/representable/option.rb +19 -0
- data/lib/representable/pipeline.rb +3 -2
- data/lib/representable/pipeline_factories.rb +4 -2
- data/lib/representable/populator.rb +1 -1
- data/lib/representable/represent.rb +1 -0
- data/lib/representable/serializer.rb +3 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/virtus_coercion.rb +38 -0
- data/lib/representable/xml.rb +12 -10
- data/lib/representable/xml/binding.rb +19 -13
- data/lib/representable/xml/namespace.rb +122 -0
- data/lib/representable/yaml.rb +6 -2
- data/lib/representable/yaml/binding.rb +1 -0
- data/representable.gemspec +8 -9
- data/test/as_test.rb +7 -7
- data/test/binding_test.rb +14 -14
- data/test/cached_test.rb +59 -49
- data/test/class_test.rb +9 -9
- data/test/coercion_test.rb +33 -22
- data/test/config/inherit_test.rb +14 -14
- data/test/config_test.rb +20 -20
- data/test/decorator_scope_test.rb +4 -4
- data/test/decorator_test.rb +33 -20
- data/test/default_test.rb +8 -8
- data/test/defaults_options_test.rb +3 -3
- data/test/definition_test.rb +38 -40
- data/test/{example.rb → examples/example.rb} +0 -1
- data/test/examples/object.rb +1 -5
- data/test/exec_context_test.rb +8 -8
- data/test/features_test.rb +6 -6
- data/test/filter_test.rb +8 -8
- data/test/for_collection_test.rb +10 -10
- data/test/generic_test.rb +13 -13
- data/test/getter_setter_test.rb +5 -5
- data/test/hash_bindings_test.rb +1 -1
- data/test/hash_test.rb +45 -23
- data/test/heritage_test.rb +16 -13
- data/test/if_test.rb +9 -9
- data/test/include_exclude_test.rb +14 -14
- data/test/inherit_test.rb +18 -18
- data/test/inline_test.rb +24 -24
- data/test/instance_test.rb +31 -31
- data/test/is_representable_test.rb +10 -10
- data/test/json_test.rb +29 -7
- data/test/lonely_test.rb +31 -31
- data/test/nested_test.rb +13 -13
- data/test/object_test.rb +9 -9
- data/test/option_test.rb +36 -0
- data/test/parse_pipeline_test.rb +3 -5
- data/test/pipeline_test.rb +50 -50
- data/test/populator_test.rb +18 -18
- data/test/prepare_test.rb +4 -4
- data/test/private_options_test.rb +2 -2
- data/test/reader_writer_test.rb +2 -2
- data/test/render_nil_test.rb +2 -2
- data/test/represent_test.rb +14 -14
- data/test/representable_test.rb +34 -36
- data/test/schema_test.rb +8 -11
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +14 -14
- data/test/stringify_hash_test.rb +3 -3
- data/test/test_helper.rb +26 -14
- data/test/uncategorized_test.rb +10 -10
- data/test/user_options_test.rb +4 -4
- data/test/virtus_coercion_test.rb +52 -0
- data/test/wrap_test.rb +19 -19
- data/test/xml_bindings_test.rb +0 -4
- data/test/xml_namespace_test.rb +186 -0
- data/test/xml_test.rb +103 -43
- data/test/yaml_test.rb +51 -26
- metadata +101 -39
- data/.travis.yml +0 -7
- data/lib/representable/TODO.getting_serious +0 -11
- data/lib/representable/autoload.rb +0 -10
- data/test/mongoid_test.rb +0 -31
data/lib/representable/config.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
require 'declarative/definitions'
|
2
|
+
|
1
3
|
module Representable
|
4
|
+
autoload :Option, 'representable/option'
|
5
|
+
|
2
6
|
# Stores Definitions from ::property. It preserves the adding order (1.9+).
|
3
7
|
# Same-named properties get overridden, just like in a Hash.
|
4
8
|
#
|
5
9
|
# Overwrite definition_class if you need a custom Definition object (helpful when using
|
6
10
|
# representable in other gems).
|
7
11
|
class Config < ::Declarative::Definitions
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
@wrap = nil
|
15
|
+
end
|
16
|
+
|
8
17
|
def remove(name)
|
9
18
|
delete(name.to_s)
|
10
19
|
end
|
@@ -15,16 +24,17 @@ module Representable
|
|
15
24
|
|
16
25
|
def wrap=(value)
|
17
26
|
value = value.to_s if value.is_a?(Symbol)
|
18
|
-
@wrap =
|
27
|
+
@wrap = ::Representable::Option(value)
|
19
28
|
end
|
20
29
|
|
21
30
|
# Computes the wrap string or returns false.
|
22
|
-
def wrap_for(represented,
|
31
|
+
def wrap_for(represented, options = {}, &block)
|
23
32
|
return unless @wrap
|
24
33
|
|
25
|
-
value = @wrap.
|
34
|
+
value = @wrap.(exec_context: represented, keyword_arguments: options.to_hash)
|
26
35
|
|
27
36
|
return value if value != true
|
37
|
+
|
28
38
|
infer_name_for(represented.class.name)
|
29
39
|
end
|
30
40
|
|
data/lib/representable/debug.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Representable
|
2
4
|
module Debug
|
5
|
+
module_function def _representable_logger
|
6
|
+
@logger ||= Logger.new(STDOUT)
|
7
|
+
end
|
8
|
+
|
9
|
+
module_function def representable_log(message)
|
10
|
+
_representable_logger.debug { message }
|
11
|
+
end
|
12
|
+
|
3
13
|
def update_properties_from(doc, options, format)
|
4
|
-
|
5
|
-
|
6
|
-
puts "[Deserialize] document #{doc.inspect}"
|
14
|
+
representable_log "[Deserialize]........."
|
15
|
+
representable_log "[Deserialize] document #{doc.inspect}"
|
7
16
|
super
|
8
17
|
end
|
9
18
|
|
10
19
|
def create_representation_with(doc, options, format)
|
11
|
-
|
12
|
-
|
13
|
-
puts "[Serialize]"
|
20
|
+
representable_log "[Serialize]........."
|
21
|
+
representable_log "[Serialize]"
|
14
22
|
super
|
15
23
|
end
|
16
24
|
|
@@ -22,13 +30,13 @@ module Representable
|
|
22
30
|
|
23
31
|
module Binding
|
24
32
|
def evaluate_option(name, *args, &block)
|
25
|
-
|
26
|
-
|
33
|
+
Debug.representable_log "=====#{self[name]}" if name ==:prepare
|
34
|
+
Debug.representable_log (evaled = self[name]) ?
|
27
35
|
" #evaluate_option [#{name}]: eval!!!" :
|
28
36
|
" #evaluate_option [#{name}]: skipping"
|
29
37
|
value = super
|
30
|
-
|
31
|
-
|
38
|
+
Debug.representable_log " #evaluate_option [#{name}]: --> #{value}" if evaled
|
39
|
+
Debug.representable_log " #evaluate_option [#{name}]: -->= #{args.first}" if name == :setter
|
32
40
|
value
|
33
41
|
end
|
34
42
|
|
@@ -45,17 +53,17 @@ module Representable
|
|
45
53
|
|
46
54
|
module Pipeline::Debug
|
47
55
|
def call(input, options)
|
48
|
-
|
49
|
-
|
56
|
+
Debug.representable_log "Pipeline#call: #{inspect}"
|
57
|
+
Debug.representable_log " input: #{input.inspect}"
|
50
58
|
super
|
51
59
|
end
|
52
60
|
|
53
61
|
def evaluate(block, memo, options)
|
54
62
|
block.extend(Pipeline::Debug) if block.is_a?(Collect)
|
55
63
|
|
56
|
-
|
64
|
+
Debug.representable_log " Pipeline : -> #{_inspect_function(block)} "
|
57
65
|
super.tap do |res|
|
58
|
-
|
66
|
+
Debug.representable_log " Pipeline : result: #{res.inspect}"
|
59
67
|
end
|
60
68
|
end
|
61
69
|
|
@@ -70,8 +78,8 @@ module Representable
|
|
70
78
|
def _inspect_function(func)
|
71
79
|
return func.extend(Pipeline::Debug).inspect if func.is_a?(Collect)
|
72
80
|
return func unless func.is_a?(Proc)
|
81
|
+
|
73
82
|
File.readlines(func.source_location[0])[func.source_location[1]-1].match(/^\s+(\w+)/)[1]
|
74
83
|
end
|
75
84
|
end
|
76
85
|
end
|
77
|
-
|
@@ -1,4 +1,9 @@
|
|
1
|
+
require "declarative/schema"
|
2
|
+
|
1
3
|
module Representable
|
4
|
+
autoload :Decorator, "representation/decorator"
|
5
|
+
autoload :Definition, "representation/definition"
|
6
|
+
|
2
7
|
module Declarative
|
3
8
|
def representation_wrap=(name)
|
4
9
|
heritage.record(:representation_wrap=, name)
|
@@ -21,9 +26,9 @@ module Representable
|
|
21
26
|
# them to the original object.
|
22
27
|
def nested(name, options={}, &block)
|
23
28
|
options = options.merge(
|
24
|
-
getter: ->(
|
25
|
-
setter: ->(
|
26
|
-
instance: ->(
|
29
|
+
getter: ->(_opts) { self },
|
30
|
+
setter: ->(opts) { },
|
31
|
+
instance: ->(_opts) { self },
|
27
32
|
)
|
28
33
|
|
29
34
|
if block
|
@@ -47,10 +52,10 @@ module Representable
|
|
47
52
|
NestedBuilder = ->(options) do
|
48
53
|
Module.new do
|
49
54
|
include Representable # FIXME: do we really need this?
|
50
|
-
feature
|
51
|
-
include
|
55
|
+
feature(*options[:_features])
|
56
|
+
include(*options[:_base]) # base when :inherit, or in decorator.
|
52
57
|
|
53
|
-
module_eval
|
58
|
+
module_eval(&options[:_block])
|
54
59
|
end
|
55
60
|
end
|
56
61
|
|
@@ -64,4 +69,4 @@ module Representable
|
|
64
69
|
|
65
70
|
alias_method :representable_attrs, :definitions
|
66
71
|
end
|
67
|
-
end
|
72
|
+
end
|
@@ -1,7 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require "representable/populator"
|
1
|
+
require 'declarative/definitions'
|
3
2
|
|
4
3
|
module Representable
|
4
|
+
autoload :Pipeline, "representable/pipeline"
|
5
|
+
autoload :Populator, "representable/populator"
|
6
|
+
autoload :Option, "representable/option"
|
7
|
+
|
5
8
|
# Created at class compile time. Keeps configuration options for one property.
|
6
9
|
class Definition < ::Declarative::Definitions::Definition
|
7
10
|
|
@@ -54,6 +57,7 @@ module Representable
|
|
54
57
|
|
55
58
|
def representable?
|
56
59
|
return if self[:representable] == false
|
60
|
+
|
57
61
|
self[:representable] or typed?
|
58
62
|
end
|
59
63
|
|
@@ -102,7 +106,7 @@ module Representable
|
|
102
106
|
@runtime_options = {}
|
103
107
|
|
104
108
|
for name, value in options
|
105
|
-
value =
|
109
|
+
value = ::Representable::Option(value) if dynamic_options.include?(name)
|
106
110
|
@runtime_options[name] = value
|
107
111
|
end
|
108
112
|
end
|
@@ -11,11 +11,11 @@ module Representable
|
|
11
11
|
ReadFragment = ->(input, options) { options[:binding].read(input, options[:as]) }
|
12
12
|
Reader = ->(input, options) { options[:binding].evaluate_option(:reader, input, options) }
|
13
13
|
|
14
|
-
StopOnNotFound = ->(input,
|
14
|
+
StopOnNotFound = ->(input, _options) do
|
15
15
|
Binding::FragmentNotFound == input ? Pipeline::Stop : input
|
16
16
|
end
|
17
17
|
|
18
|
-
StopOnNil = ->(input,
|
18
|
+
StopOnNil = ->(input, _options) do # DISCUSS: Not tested/used, yet.
|
19
19
|
input.nil? ? Pipeline::Stop : input
|
20
20
|
end
|
21
21
|
|
@@ -96,13 +96,14 @@ module Representable
|
|
96
96
|
If = ->(input, options) { options[:binding].evaluate_option(:if, nil, options) ? input : Pipeline::Stop }
|
97
97
|
|
98
98
|
StopOnExcluded = ->(input, options) do
|
99
|
-
return input unless
|
99
|
+
return input unless options[:options]
|
100
100
|
return input unless props = (options[:options][:exclude] || options[:options][:include])
|
101
101
|
|
102
102
|
res = props.include?(options[:binding].name.to_sym) # false with include: Stop. false with exclude: go!
|
103
103
|
|
104
104
|
return input if options[:options][:include]&&res
|
105
105
|
return input if options[:options][:exclude]&&!res
|
106
|
+
|
106
107
|
Pipeline::Stop
|
107
108
|
end
|
108
|
-
end
|
109
|
+
end
|
data/lib/representable/hash.rb
CHANGED
@@ -6,6 +6,9 @@ module Representable
|
|
6
6
|
# 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
|
7
7
|
# architecture).
|
8
8
|
module Hash
|
9
|
+
autoload :Collection, 'representable/hash/collection'
|
10
|
+
autoload :AllowSymbols, 'representable/hash/allow_symbols'
|
11
|
+
|
9
12
|
def self.included(base)
|
10
13
|
base.class_eval do
|
11
14
|
include Representable # either in Hero or HeroRepresentation.
|
@@ -34,15 +37,19 @@ module Representable
|
|
34
37
|
hash = create_representation_with({}, options, binding_builder)
|
35
38
|
|
36
39
|
return hash if options[:wrap] == false
|
37
|
-
return hash unless wrap = options[:wrap] || representation_wrap(options)
|
40
|
+
return hash unless (wrap = options[:wrap] || representation_wrap(options))
|
38
41
|
|
39
42
|
{wrap => hash}
|
40
43
|
end
|
41
44
|
|
45
|
+
alias_method :render, :to_hash
|
46
|
+
alias_method :parse, :from_hash
|
47
|
+
|
42
48
|
private
|
43
49
|
def filter_wrap(data, options)
|
44
50
|
return data if options[:wrap] == false
|
45
|
-
return data unless wrap = options[:wrap] || representation_wrap(options)
|
51
|
+
return data unless (wrap = options[:wrap] || representation_wrap(options))
|
52
|
+
|
46
53
|
filter_wrap_for(data, wrap)
|
47
54
|
end
|
48
55
|
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Representable
|
2
4
|
module Hash
|
3
5
|
module AllowSymbols
|
4
|
-
|
6
|
+
private
|
7
|
+
|
5
8
|
def filter_wrap_for(data, *args)
|
6
9
|
super(Conversion.stringify_keys(data), *args)
|
7
10
|
end
|
@@ -11,17 +14,12 @@ module Representable
|
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
|
-
|
15
|
-
# DISCUSS: we could think about mixin in IndifferentAccess here (either hashie or ActiveSupport).
|
16
|
-
# or decorating the hash.
|
17
|
+
module Conversion
|
17
18
|
def self.stringify_keys(hash)
|
18
|
-
hash
|
19
|
-
|
20
|
-
|
21
|
-
hash[k.to_s] = hash.delete(k)
|
22
|
-
end
|
23
|
-
hash
|
19
|
+
hash.keys.collect do |key|
|
20
|
+
[ key.to_s, hash[key] ]
|
21
|
+
end.to_h
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
27
|
-
end
|
25
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'representable/hash'
|
2
|
+
|
1
3
|
module Representable::Hash
|
2
4
|
module Collection
|
3
5
|
include Representable::Hash
|
@@ -20,7 +22,7 @@ module Representable::Hash
|
|
20
22
|
# TODO: revise lonely collection and build separate pipeline where we just use Serialize, etc.
|
21
23
|
|
22
24
|
def create_representation_with(doc, options, format)
|
23
|
-
options = normalize_options(options)
|
25
|
+
options = normalize_options(**options)
|
24
26
|
options[:_self] = options
|
25
27
|
|
26
28
|
bin = representable_bindings_for(format, options).first
|
@@ -30,7 +32,7 @@ module Representable::Hash
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def update_properties_from(doc, options, format)
|
33
|
-
options = normalize_options(options)
|
35
|
+
options = normalize_options(**options)
|
34
36
|
options[:_self] = options
|
35
37
|
|
36
38
|
bin = representable_bindings_for(format, options).first
|
@@ -20,8 +20,9 @@ module Representable
|
|
20
20
|
def filter_keys_for!(hash, options)
|
21
21
|
excluding = options[:exclude]
|
22
22
|
# TODO: use same filtering method as in normal representer in Representable#create_representation_with.
|
23
|
-
return hash unless props = options.delete(:exclude) || options.delete(:include)
|
24
|
-
|
23
|
+
return hash unless (props = (options.delete(:exclude) || options.delete(:include)))
|
24
|
+
|
25
|
+
hash.select { |k, _v| excluding ? !props.include?(k.to_sym) : props.include?(k.to_sym) }
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
data/lib/representable/insert.rb
CHANGED
data/lib/representable/json.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
|
2
|
-
require "
|
1
|
+
gem "multi_json", '>= 1.14.1'
|
2
|
+
require "multi_json"
|
3
3
|
|
4
|
-
|
5
|
-
require "multi_json"
|
6
|
-
rescue LoadError => _
|
7
|
-
abort "Missing dependency 'multi_json' for Representable::JSON. See dependencies section in README.md for details."
|
8
|
-
end
|
4
|
+
require "representable"
|
9
5
|
|
10
6
|
module Representable
|
11
7
|
# Brings #to_json and #from_json to your object.
|
12
8
|
module JSON
|
9
|
+
autoload :Collection, "representable/json/collection"
|
10
|
+
|
13
11
|
extend Hash::ClassMethods
|
14
12
|
include Hash
|
15
13
|
|
@@ -43,5 +41,8 @@ module Representable
|
|
43
41
|
def to_json(*args)
|
44
42
|
MultiJson.dump to_hash(*args)
|
45
43
|
end
|
44
|
+
|
45
|
+
alias_method :render, :to_json
|
46
|
+
alias_method :parse, :from_json
|
46
47
|
end
|
47
48
|
end
|
data/lib/representable/object.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'representable/binding'
|
2
|
+
|
1
3
|
module Representable
|
2
4
|
module Object
|
3
5
|
class Binding < Representable::Binding
|
4
6
|
def self.build_for(definition) # TODO: remove default arg.
|
5
7
|
return Collection.new(definition) if definition.array?
|
8
|
+
|
6
9
|
new(definition)
|
7
10
|
end
|
8
11
|
|
@@ -10,6 +13,7 @@ module Representable
|
|
10
13
|
fragment = hash.send(as) # :getter? no, that's for parsing!
|
11
14
|
|
12
15
|
return FragmentNotFound if fragment.nil? and typed?
|
16
|
+
|
13
17
|
fragment
|
14
18
|
end
|
15
19
|
|
@@ -31,4 +35,4 @@ module Representable
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
end
|
34
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "trailblazer/option"
|
2
|
+
|
3
|
+
module Representable
|
4
|
+
# Extend `Trailblazer::Option` to support static values as callables too.
|
5
|
+
class Option < ::Trailblazer::Option
|
6
|
+
def self.callable?(value)
|
7
|
+
[Proc, Symbol, Uber::Callable].any?{ |kind| value.is_a?(kind) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.build(value)
|
11
|
+
return ->(*) { value } unless callable?(value) # Wrap static `value` into a proc.
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.Option(value)
|
17
|
+
::Representable::Option.build(value)
|
18
|
+
end
|
19
|
+
end
|