ixtlan-babel 0.1.2 → 0.2.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.
- data/Gemfile.lock +9 -5
- data/README.md +5 -1
- data/lib/ixtlan/babel/abstract_filter.rb +26 -0
- data/lib/ixtlan/babel/abstract_filter.rb~ +111 -0
- data/lib/ixtlan/babel/config.rb~ +142 -0
- data/lib/ixtlan/babel/context.rb +43 -0
- data/lib/ixtlan/babel/context.rb~ +43 -0
- data/lib/ixtlan/babel/deserializer.rb +21 -19
- data/lib/ixtlan/babel/factory.rb +27 -12
- data/lib/ixtlan/babel/filter_config.rb +52 -0
- data/lib/ixtlan/babel/filter_config.rb~ +111 -0
- data/lib/ixtlan/babel/hash_filter.rb +17 -123
- data/lib/ixtlan/babel/hash_filter.rb- +170 -0
- data/lib/ixtlan/babel/hash_filter.rb~ +111 -0
- data/lib/ixtlan/babel/hash_only_filter.rb~ +142 -0
- data/lib/ixtlan/babel/model_filter.rb +53 -0
- data/lib/ixtlan/babel/model_filter.rb~ +170 -0
- data/lib/ixtlan/babel/no_timestamp_serializer.rb +3 -7
- data/lib/ixtlan/babel/serializer.rb +44 -20
- data/spec/hash_filter_spec.rb +99 -0
- data/spec/{filter_spec.rb → hash_filter_spec.rb~} +0 -0
- data/spec/model_filter_spec.rb +163 -0
- data/spec/model_filter_spec.rb~ +99 -0
- data/spec/model_filter_with_methods.spec.rb~ +167 -0
- data/spec/model_filter_with_methods_spec.rb +184 -0
- data/spec/model_filter_with_methods_spec.rb~ +170 -0
- metadata +82 -81
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'ixtlan/babel/config'
|
2
|
+
module Ixtlan
|
3
|
+
module Babel
|
4
|
+
class HashFilter
|
5
|
+
|
6
|
+
def initialize(context_or_options = nil)
|
7
|
+
use(context_or_options)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def context
|
13
|
+
@context ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
def add_custom_serializers(map)
|
19
|
+
@map = map
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_context_key( single = :single,
|
23
|
+
collection = :collection )
|
24
|
+
@single, @collection = single, collection
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=( key, options )
|
28
|
+
context[ key.to_sym ] = options if key
|
29
|
+
end
|
30
|
+
|
31
|
+
def []( key )
|
32
|
+
context[ key.to_sym ] if key
|
33
|
+
end
|
34
|
+
|
35
|
+
def use( context_or_options )
|
36
|
+
if context_or_options
|
37
|
+
case context_or_options
|
38
|
+
when Symbol
|
39
|
+
if opts = context[ context_or_options ]
|
40
|
+
@options = opts.dup
|
41
|
+
end
|
42
|
+
when Hash
|
43
|
+
@options = context_or_options
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@options = nil
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter( hash = {} )
|
52
|
+
if hash
|
53
|
+
filter_data( hash,
|
54
|
+
Config.new( options_for( hash ) ) )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def single_options
|
59
|
+
@options || context[default_context_key[0]] || {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def collection_options
|
63
|
+
@options || context[default_context_key[1]] || {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def root
|
67
|
+
@root ||= single_options.key?(:root) ? single_options[:root].to_s : nil
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def options_for( hash )
|
73
|
+
@options || (hash.is_a?(Array) ? collection_options : single_options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def filter_array( array, options )
|
77
|
+
array.collect do |item|
|
78
|
+
if item.is_a?( Array ) || item.is_a?( Hash )
|
79
|
+
filter_data( item, options )
|
80
|
+
else
|
81
|
+
item
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize( data )
|
87
|
+
if @map && ser = @map[ data.class.to_s ]
|
88
|
+
ser.call(data)
|
89
|
+
else
|
90
|
+
data
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def filter_data( data, config )
|
95
|
+
result = {}
|
96
|
+
data.each do |k,v|
|
97
|
+
k = k.to_s
|
98
|
+
case v
|
99
|
+
when Hash
|
100
|
+
result[ k ] = filter_data( v, config[ k ] ) if config.include?( k )
|
101
|
+
when Array
|
102
|
+
result[ k ] = filter_array( v, config[ k ] ) if config.include?( k )
|
103
|
+
else
|
104
|
+
result[ k ] = serialize( v ) if config.allowed?( k )
|
105
|
+
end
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Babel
|
3
|
+
class HashFilter
|
4
|
+
|
5
|
+
def initialize(context_or_options = nil)
|
6
|
+
use(context_or_options)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def context
|
12
|
+
@context ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
def add_custom_serializers(map)
|
18
|
+
@map = map
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_context_key( single = :single,
|
22
|
+
collection = :collection )
|
23
|
+
@single, @collection = single, collection
|
24
|
+
end
|
25
|
+
|
26
|
+
def []=( key, options )
|
27
|
+
context[ key.to_sym ] = options if key
|
28
|
+
end
|
29
|
+
|
30
|
+
def []( key )
|
31
|
+
context[ key.to_sym ] if key
|
32
|
+
end
|
33
|
+
|
34
|
+
def use( context_or_options )
|
35
|
+
if context_or_options
|
36
|
+
case context_or_options
|
37
|
+
when Symbol
|
38
|
+
if opts = context[ context_or_options ]
|
39
|
+
@options = opts.dup
|
40
|
+
end
|
41
|
+
when Hash
|
42
|
+
@options = context_or_options
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@options = nil
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def filter(hash = {}, model = nil, &block)
|
51
|
+
if hash
|
52
|
+
filter_data( hash,
|
53
|
+
Config.new( options_for( hash ) ),
|
54
|
+
&block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def options_for( hash )
|
59
|
+
@options || (hash.is_a?(Array) ? collection_options : single_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def single_options
|
63
|
+
@options || context[default_context_key[0]] || {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def collection_options
|
67
|
+
@options || context[default_context_key[1]] || {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def root
|
71
|
+
@root ||= single_options.key?(:root) ? single_options[:root].to_s : nil
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
class Config
|
77
|
+
|
78
|
+
def initialize( options )
|
79
|
+
@only = options[ :only ].collect { |o| o.to_s } if options[ :only ]
|
80
|
+
@except = ( options[:except] || [] ).collect { |e| e.to_s }
|
81
|
+
|
82
|
+
opts = options[ :include ]
|
83
|
+
@include =
|
84
|
+
case opts
|
85
|
+
when Array
|
86
|
+
opts.collect { |i| i.to_s }
|
87
|
+
when Hash
|
88
|
+
Hash[ opts.collect { |k,v| [ k.to_s, v ] } ]
|
89
|
+
else
|
90
|
+
[]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def allowed?( key )
|
95
|
+
( @only && @only.include?( key ) ) || ( @only.nil? && !@except.include?( key ) )
|
96
|
+
end
|
97
|
+
|
98
|
+
def include?( key )
|
99
|
+
@include.include? key
|
100
|
+
end
|
101
|
+
|
102
|
+
def []( key )
|
103
|
+
self.class.new( @include.is_a?( Array ) ? {} : @include[ key ] )
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def filter_array( array, options )
|
108
|
+
array.collect do |item|
|
109
|
+
if item.is_a?( Array ) || item.is_a?( Hash )
|
110
|
+
filter_data( item, options )
|
111
|
+
else
|
112
|
+
item
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def serialize( data )
|
118
|
+
if @map && ser = @map[ data.class.to_s ]
|
119
|
+
ser.call(data)
|
120
|
+
else
|
121
|
+
data
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def filter_data( data, config )
|
126
|
+
result = {}
|
127
|
+
data.each do |k,v|
|
128
|
+
k = k.to_s
|
129
|
+
case v
|
130
|
+
when Hash
|
131
|
+
result[ k ] = filter_data( v, config[ k ] ) if config.include?( k )
|
132
|
+
when Array
|
133
|
+
result[ k ] = filter_array( v, config[ k ] ) if config.include?( k )
|
134
|
+
else
|
135
|
+
result[ k ] = serialize( v ) if config.allowed?( k )
|
136
|
+
end
|
137
|
+
end
|
138
|
+
result
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'ixtlan/babel/abstract_filter'
|
2
|
+
module Ixtlan
|
3
|
+
module Babel
|
4
|
+
class ModelFilter < AbstractFilter
|
5
|
+
|
6
|
+
def filter( model, &block )
|
7
|
+
if model
|
8
|
+
data = block.call( model )
|
9
|
+
filter_data( model, data,
|
10
|
+
Context.new( options ),
|
11
|
+
&block )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def filter_array( models, options, &block )
|
18
|
+
models.collect do |i|
|
19
|
+
if i.respond_to? :attributes
|
20
|
+
filter_data(i, block.call(i), options, &block)
|
21
|
+
else
|
22
|
+
i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup_data(model, data, context)
|
28
|
+
context.methods.each do |m|
|
29
|
+
unless data.include?(m)
|
30
|
+
data[ m ] = model.send( m.to_sym )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def filter_data(model, data, context, &block)
|
36
|
+
setup_data(model, data, context)
|
37
|
+
|
38
|
+
result = {}
|
39
|
+
data.each do |k,v|
|
40
|
+
k = k.to_s
|
41
|
+
if v.respond_to? :attributes
|
42
|
+
result[ k ] = filter_data( v, block.call(v), context[ k ], &block ) if context.include?( k )
|
43
|
+
elsif v.is_a? Array
|
44
|
+
result[ k ] = filter_array( v, context[ k ], &block ) if context.include?( k )
|
45
|
+
else
|
46
|
+
result[ k ] = serialize( v ) if context.allowed?( k ) && ! v.respond_to?( :attributes )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Babel
|
3
|
+
class HashFilter
|
4
|
+
|
5
|
+
def initialize(context_or_options = nil)
|
6
|
+
use(context_or_options)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def context
|
12
|
+
@context ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
def add_custom_serializers(map)
|
18
|
+
@map = map
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_context_key(single = :single, collection = :collection)
|
22
|
+
@single, @collection = single, collection
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, options)
|
26
|
+
context[key.to_sym] = options if key
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
context[key.to_sym] if key
|
31
|
+
end
|
32
|
+
|
33
|
+
def use(context_or_options)
|
34
|
+
if context_or_options
|
35
|
+
case context_or_options
|
36
|
+
when Symbol
|
37
|
+
if opts = context[context_or_options]
|
38
|
+
@options = opts.dup
|
39
|
+
end
|
40
|
+
when Hash
|
41
|
+
@options = context_or_options
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@options = nil
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
NO_MODEL = Object.new
|
50
|
+
def NO_MODEL.send(*args)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
def NO_MODEL.[](*args)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def filter(hash = {}, model = NO_MODEL, &block)
|
58
|
+
filter_data(model, hash, hash.is_a?(Array) ? collection_options : single_options, &block) if hash
|
59
|
+
end
|
60
|
+
|
61
|
+
def single_options
|
62
|
+
@options || context[default_context_key[0]] || {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def collection_options
|
66
|
+
@options || context[default_context_key[1]] || {}
|
67
|
+
end
|
68
|
+
|
69
|
+
def root
|
70
|
+
@root ||= single_options.key?(:root) ? single_options[:root].to_s : nil
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def filter_data(model, data, options = {}, &block)
|
76
|
+
model = NO_MODEL if model == data
|
77
|
+
only = options[:only].collect { |o| o.to_s } if options[:only]
|
78
|
+
except = (options[:except] || []).collect { |e| e.to_s }
|
79
|
+
|
80
|
+
include =
|
81
|
+
case options[:include]
|
82
|
+
when Array
|
83
|
+
options[:include].collect { |i| i.to_s }
|
84
|
+
when Hash
|
85
|
+
Hash[options[:include].collect {|k,v| [k.to_s, v]}]
|
86
|
+
else
|
87
|
+
[]
|
88
|
+
end
|
89
|
+
if model != NO_MODEL
|
90
|
+
methods = (options[:methods] || []).collect { |e| e.to_s }
|
91
|
+
methods.each do |m|
|
92
|
+
data[m] = model.send(m.to_sym)
|
93
|
+
end
|
94
|
+
|
95
|
+
include_methods = include.is_a?(Array) ? include : include.keys
|
96
|
+
include_methods.each do |m|
|
97
|
+
unless data.include?(m)
|
98
|
+
raise "no block given to calculate the attributes from model" unless block
|
99
|
+
models_or_model = model.send(m)
|
100
|
+
if models_or_model.is_a?(Array) && models_or_model.first.is_a?(String)
|
101
|
+
data[m] = models_or_model
|
102
|
+
elsif models_or_model.respond_to?(:collect)
|
103
|
+
data[m] = models_or_model.collect { |i| block.call(i) }
|
104
|
+
else
|
105
|
+
val = model.send(m)
|
106
|
+
case val
|
107
|
+
when Fixnum
|
108
|
+
data[m] = val
|
109
|
+
when String
|
110
|
+
data[m] = val
|
111
|
+
when TrueClass
|
112
|
+
data[m] = val
|
113
|
+
else
|
114
|
+
data[m]= block.call(val)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
methods ||= []
|
121
|
+
result = {}
|
122
|
+
data.each do |k,v|
|
123
|
+
case v
|
124
|
+
when Hash
|
125
|
+
if include.include?(k.to_s)
|
126
|
+
case include
|
127
|
+
when Array
|
128
|
+
result[k.to_s] = filter_data(model.send(k), v, &block)
|
129
|
+
when Hash
|
130
|
+
result[k.to_s] = filter_data(model.send(k), v, include[k.to_s], &block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
when Array
|
134
|
+
if include.include?(k.to_s)
|
135
|
+
models = model.send(k)
|
136
|
+
j = -1
|
137
|
+
case include
|
138
|
+
when Array
|
139
|
+
result[k.to_s] = v.collect do |i|
|
140
|
+
j += 1
|
141
|
+
if i.is_a?(Array) || i.is_a?(Hash)
|
142
|
+
filter_data(models[j], i, &block)
|
143
|
+
else
|
144
|
+
i
|
145
|
+
end
|
146
|
+
end
|
147
|
+
when Hash
|
148
|
+
opts = include[k.to_s]
|
149
|
+
result[k.to_s] = v.collect do |i|
|
150
|
+
j += 1
|
151
|
+
ndata = i.is_a?(Hash)? i : block.call(i)
|
152
|
+
filter_data(models[j], ndata, opts, &block)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
if methods.include?(k.to_s) || (only && only.include?(k.to_s)) || (only.nil? && !except.include?(k.to_s))
|
158
|
+
if @map && ser = @map[v.class.to_s]
|
159
|
+
result[k.to_s] = ser.call(v)
|
160
|
+
else
|
161
|
+
result[k.to_s] = v
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
result
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|