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
data/lib/ixtlan/babel/factory.rb
CHANGED
@@ -2,6 +2,22 @@ module Ixtlan
|
|
2
2
|
module Babel
|
3
3
|
class Factory
|
4
4
|
|
5
|
+
NANOSECONDS_IN_DAY = Rational(1, 86400*10**9)
|
6
|
+
|
7
|
+
TIME_TO_S = Proc.new do |t|
|
8
|
+
t.strftime('%Y-%m-%dT%H:%M:%S.') + ("%06d" % t.usec) + t.strftime('%z')
|
9
|
+
end
|
10
|
+
|
11
|
+
DATE_TIME_TO_S = Proc.new do |dt|
|
12
|
+
dt.strftime('%Y-%m-%dT%H:%M:%S.') + ("%06d" % (dt.sec_fraction / NANOSECONDS_IN_DAY / 1000)) + dt.strftime('%z')
|
13
|
+
end
|
14
|
+
|
15
|
+
DEFAULT_MAP = {
|
16
|
+
'DateTime' => DATE_TIME_TO_S,
|
17
|
+
'ActiveSupport::TimeWithZone' => TIME_TO_S,
|
18
|
+
'Time' => TIME_TO_S
|
19
|
+
}
|
20
|
+
|
5
21
|
class EmptyArraySerializer < Array
|
6
22
|
def use(arg)
|
7
23
|
self
|
@@ -9,16 +25,7 @@ module Ixtlan
|
|
9
25
|
end
|
10
26
|
|
11
27
|
def initialize(custom_serializers = {})
|
12
|
-
@map =
|
13
|
-
add('DateTime') do |dt|
|
14
|
-
dt.strftime('%Y-%m-%dT%H:%M:%S.') + ("%06d" % (dt.sec_fraction / Date::NANOSECONDS_IN_DAY / 1000)) + dt.strftime('%z')
|
15
|
-
end
|
16
|
-
add('ActiveSupport::TimeWithZone') do |tz|
|
17
|
-
tz.strftime('%Y-%m-%dT%H:%M:%S.') + ("%06d" % tz.usec) + tz.strftime('%z')
|
18
|
-
end
|
19
|
-
add('Time') do |t|
|
20
|
-
t.strftime('%Y-%m-%dT%H:%M:%S.') + ("%06d" % t.usec) + t.strftime('%z')
|
21
|
-
end
|
28
|
+
@map = DEFAULT_MAP.dup
|
22
29
|
@map.merge!(custom_serializers)
|
23
30
|
end
|
24
31
|
|
@@ -29,7 +36,7 @@ module Ixtlan
|
|
29
36
|
def new(resource)
|
30
37
|
if resource.respond_to?(:model)
|
31
38
|
model = resource.model
|
32
|
-
elsif resource.
|
39
|
+
elsif resource.respond_to? :collect
|
33
40
|
if resource.empty?
|
34
41
|
return EmptyArraySerializer.new
|
35
42
|
else
|
@@ -39,10 +46,18 @@ module Ixtlan
|
|
39
46
|
else
|
40
47
|
model = resource.class
|
41
48
|
end
|
42
|
-
ser =
|
49
|
+
ser = const_retrieve("#{model}Serializer").new(resource)
|
43
50
|
ser.add_custom_serializers(@map)
|
44
51
|
ser
|
45
52
|
end
|
53
|
+
|
54
|
+
def const_retrieve( const )
|
55
|
+
obj = Object
|
56
|
+
const.split(/::/).each do |part|
|
57
|
+
obj = obj.const_get( part )
|
58
|
+
end
|
59
|
+
obj
|
60
|
+
end
|
46
61
|
end
|
47
62
|
end
|
48
63
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'ixtlan/babel/context'
|
2
|
+
module Ixtlan
|
3
|
+
module Babel
|
4
|
+
class FilterConfig
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def context
|
9
|
+
@context ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def context_options( context_or_options )
|
13
|
+
if context_or_options
|
14
|
+
case context_or_options
|
15
|
+
when Symbol
|
16
|
+
if opts = context[ context_or_options ]
|
17
|
+
opts.dup
|
18
|
+
end
|
19
|
+
when Hash
|
20
|
+
context_or_options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
def default_context_key( single = :single,
|
28
|
+
collection = :collection )
|
29
|
+
@single, @collection = single, collection
|
30
|
+
end
|
31
|
+
|
32
|
+
def []=( key, options )
|
33
|
+
context[ key.to_sym ] = options if key
|
34
|
+
end
|
35
|
+
|
36
|
+
def []( key )
|
37
|
+
context[ key.to_sym ] if key
|
38
|
+
end
|
39
|
+
|
40
|
+
def single_options( context_or_options )
|
41
|
+
context_options( context_or_options ) || context[default_context_key[0]] || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def collection_options( context_or_options )
|
45
|
+
context_options( context_or_options ) || context[default_context_key[1]] || {}
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_accessor :root
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'ixtlan/babel/context'
|
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
|
+
Context.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, context )
|
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, context[ k ] ) if context.include?( k )
|
101
|
+
when Array
|
102
|
+
result[ k ] = filter_array( v, context[ k ] ) if context.include?( k )
|
103
|
+
else
|
104
|
+
result[ k ] = serialize( v ) if context.allowed?( k )
|
105
|
+
end
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,144 +1,38 @@
|
|
1
|
+
require 'ixtlan/babel/abstract_filter'
|
1
2
|
module Ixtlan
|
2
3
|
module Babel
|
3
|
-
class HashFilter
|
4
|
+
class HashFilter < AbstractFilter
|
4
5
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
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(default = nil)
|
22
|
-
@default = default if default
|
23
|
-
@default
|
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
|
6
|
+
def filter( data )
|
7
|
+
if data
|
8
|
+
filter_data( data,
|
9
|
+
Context.new( options ) )
|
46
10
|
end
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
NO_MODEL = Object.new
|
51
|
-
def NO_MODEL.send(*args)
|
52
|
-
self
|
53
|
-
end
|
54
|
-
def NO_MODEL.[](*args)
|
55
|
-
self
|
56
|
-
end
|
57
|
-
|
58
|
-
def filter(hash = {}, model = NO_MODEL, &block)
|
59
|
-
filter_data(model, hash, options, &block) if hash
|
60
|
-
end
|
61
|
-
|
62
|
-
def options
|
63
|
-
@options || context[default_context_key] || {}
|
64
11
|
end
|
65
12
|
|
66
13
|
private
|
67
14
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
include =
|
74
|
-
case options[:include]
|
75
|
-
when Array
|
76
|
-
options[:include].collect { |i| i.to_s }
|
77
|
-
when Hash
|
78
|
-
Hash[options[:include].collect {|k,v| [k.to_s, v]}]
|
15
|
+
def filter_array( array, options )
|
16
|
+
array.collect do |item|
|
17
|
+
if item.is_a?( Array ) || item.is_a?( Hash )
|
18
|
+
filter_data( item, options )
|
79
19
|
else
|
80
|
-
|
81
|
-
end
|
82
|
-
if model != NO_MODEL
|
83
|
-
methods = (options[:methods] || []).collect { |e| e.to_s }
|
84
|
-
methods.each do |m|
|
85
|
-
data[m] = model.send(m.to_sym)
|
86
|
-
end
|
87
|
-
|
88
|
-
include_methods = include.is_a?(Array) ? include : include.keys
|
89
|
-
include_methods.each do |m|
|
90
|
-
unless data.include?(m)
|
91
|
-
raise "no block given to calculate the attributes from model" unless block
|
92
|
-
models_or_model = model.send(m)
|
93
|
-
if models_or_model.respond_to?(:collect)
|
94
|
-
data[m] = models_or_model.collect { |i| block.call(i) }
|
95
|
-
else
|
96
|
-
data[m]= block.call(model.send(m))
|
97
|
-
end
|
98
|
-
end
|
20
|
+
item
|
99
21
|
end
|
100
22
|
end
|
101
|
-
|
23
|
+
end
|
102
24
|
|
25
|
+
def filter_data( data, context )
|
103
26
|
result = {}
|
104
27
|
data.each do |k,v|
|
28
|
+
k = k.to_s
|
105
29
|
case v
|
106
30
|
when Hash
|
107
|
-
if
|
108
|
-
case include
|
109
|
-
when Array
|
110
|
-
result[k.to_s] = filter_data(model.send(k), v, &block)
|
111
|
-
when Hash
|
112
|
-
result[k.to_s] = filter_data(model.send(k), v, include[k.to_s], &block)
|
113
|
-
end
|
114
|
-
end
|
31
|
+
result[ k ] = filter_data( v, context[ k ] ) if context.include?( k )
|
115
32
|
when Array
|
116
|
-
if
|
117
|
-
models = model.send(k)
|
118
|
-
j = -1
|
119
|
-
case include
|
120
|
-
when Array
|
121
|
-
result[k.to_s] = v.collect do |i|
|
122
|
-
j += 1
|
123
|
-
if i.is_a?(Array) || i.is_a?(Hash)
|
124
|
-
filter_data(models[j], i, &block)
|
125
|
-
else
|
126
|
-
i
|
127
|
-
end
|
128
|
-
end
|
129
|
-
when Hash
|
130
|
-
opts = include[k]
|
131
|
-
result[k.to_s] = v.collect { |i| j += 1; filter_data(models[j], i, opts, &block) }
|
132
|
-
end
|
133
|
-
end
|
33
|
+
result[ k ] = filter_array( v, context[ k ] ) if context.include?( k )
|
134
34
|
else
|
135
|
-
|
136
|
-
if @map && ser = @map[v.class.to_s]
|
137
|
-
result[k.to_s] = ser.call(v)
|
138
|
-
else
|
139
|
-
result[k.to_s] = v
|
140
|
-
end
|
141
|
-
end
|
35
|
+
result[ k ] = serialize( v ) if context.allowed?( k )
|
142
36
|
end
|
143
37
|
end
|
144
38
|
result
|
@@ -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
|