ixtlan-babel 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,62 +1,171 @@
1
- #
2
- # Copyright (C) 2013 Christian Meier
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
- # this software and associated documentation files (the "Software"), to deal in
6
- # the Software without restriction, including without limitation the rights to
7
- # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
- # the Software, and to permit persons to whom the Software is furnished to do so,
9
- # subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in all
12
- # copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
- # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
- # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
- #
21
- require 'ixtlan/babel/abstract_filter'
22
1
  module Ixtlan
23
2
  module Babel
24
- class HashFilter < AbstractFilter
3
+
4
+ module HashFilter
5
+
6
+ def self.included( model )
7
+ model.extend( ClassMethods )
8
+ end
25
9
 
26
10
  def filter( data )
27
- if data
28
- filter_data( data,
29
- Context.new( options ) )
11
+ @attributes = do_filter( data, self.class.attributes )
12
+ end
13
+
14
+ def hidden( data )
15
+ @hidden = do_filter( data, self.class.hiddens )
16
+ end
17
+
18
+ def attributes
19
+ @attributes
20
+ end
21
+ alias :params :attributes
22
+
23
+ def new_model
24
+ if @class
25
+ @class.new( @attributes )
30
26
  end
31
27
  end
32
28
 
33
- private
29
+ def respond_to? name
30
+ key = name.to_sym
31
+ self.class.hiddens.key?( key ) || self.class.attributes.key?( key ) || super
32
+ end
34
33
 
35
- def filter_array( array, options )
36
- array.collect do |item|
37
- if item.is_a?( Array ) || item.is_a?( Hash )
38
- filter_data( item, options )
34
+ def method_missing( name, *args )
35
+ key = name.to_sym
36
+ if self.class.hiddens.key?( key )
37
+ to_attribute( key,
38
+ @hidden[ key ] || @hidden[ key.to_s ],
39
+ self.class.hiddens )
40
+ elsif self.class.attributes.key?( key )
41
+ to_attribute( key,
42
+ @attributes[ key ] || @attributes[ key.to_s ],
43
+ self.class.attributes )
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def to_attribute( key, v, ref )
50
+ case v
51
+ when Hash
52
+ ref[ key ].replace( v )
53
+ else
54
+ v
55
+ end
56
+ end
57
+
58
+ def do_filter( data, ref )
59
+ data.reject do |k,v|
60
+ case v
61
+ when Hash
62
+ if babel = ref[ k.to_sym ]
63
+ v.replace babel.replace( v ).attributes
64
+ false
65
+ else
66
+ true
67
+ end
68
+ when ::Array
69
+ if babel = ref[ k.to_sym ]
70
+ v.each do |vv|
71
+ vv.replace babel.replace( vv ).attributes
72
+ end
73
+ false
74
+ else
75
+ true
76
+ end
39
77
  else
40
- serialize( item )
78
+ not ref.member?( k.to_sym )
41
79
  end
42
80
  end
43
81
  end
82
+
83
+ def initialize( clazz = nil )
84
+ super()
85
+ replace( {} )
86
+ @class = clazz
87
+ end
88
+
89
+ def replace( data )
90
+ data = deep_dup( data )
91
+ filter( data )
92
+ hidden( data )
93
+ self
94
+ end
95
+
96
+ private
44
97
 
45
- def filter_data( data, context )
46
- result = {}
98
+ def deep_dup( data )
99
+ data = data.dup
47
100
  data.each do |k,v|
48
- k = k.to_s
49
- case v
50
- when Hash
51
- result[ k ] = filter_data( v, context[ k ] ) if context.include?( k )
101
+ if v.is_a? Hash
102
+ data[ k ] = deep_dup( v )
103
+ end
104
+ end
105
+ end
106
+
107
+ module ClassMethods
108
+
109
+ def attribute( name, type = nil )
110
+ attributes[ name.to_sym ] = new_instance( type )
111
+ end
112
+
113
+ def hidden( name, type = nil )
114
+ hiddens[ name.to_sym ] = new_instance( type )
115
+ end
116
+
117
+ def attributes( *args )
118
+ if args.size == 0
119
+ @attributes ||= (superclass.attributes.dup rescue nil) || {}
120
+ else
121
+ args.each { |a| attribute( a ) }
122
+ end
123
+ end
124
+
125
+ def hiddens( *args )
126
+ if args.size == 0
127
+ @hiddens ||= (superclass.hiddens.dup rescue nil) || {}
128
+ else
129
+ args.each { |a| hidden( a ) }
130
+ end
131
+ end
132
+
133
+ def new_instance( type )
134
+ case type
52
135
  when Array
53
- result[ k ] = filter_array( v, context[ k ] ) if context.include?( k )
136
+ type[ 0 ].new
137
+ when NilClass
138
+ nil
54
139
  else
55
- result[ k ] = serialize( v ) if context.allowed?( k )
140
+ type.new
56
141
  end
57
142
  end
58
- result
59
143
  end
60
144
  end
61
145
  end
62
146
  end
147
+ # class ModifiedBy
148
+ # include Babel::HashFilter
149
+
150
+ # attribute :id
151
+ # end
152
+ # User = ModifiedBy
153
+
154
+ # class A
155
+ # include Babel::HashFilter
156
+
157
+ # attribute :id
158
+ # attribute :created_at, Array[User]
159
+ # hidden :updated_at
160
+ # attribute :modified_by, ModifiedBy
161
+
162
+ # end
163
+
164
+ # data = {:a=>123, :id => 432, :created_at => [ {:id => 12, :name => 'asd' } ], :updated_at => 'asd', :modified_by => { :id => 342 } }
165
+ # a = A.new.replace(data )
166
+ # p a.attributes
167
+ # p a
168
+ # p a.updated_at
169
+ # p a.id
170
+ # p a.modified_by
171
+ # p a.modified_by.id
@@ -1,73 +1,135 @@
1
- #
2
- # Copyright (C) 2013 Christian Meier
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
- # this software and associated documentation files (the "Software"), to deal in
6
- # the Software without restriction, including without limitation the rights to
7
- # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
- # the Software, and to permit persons to whom the Software is furnished to do so,
9
- # subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in all
12
- # copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
- # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
- # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
- #
21
- require 'ixtlan/babel/abstract_filter'
22
1
  module Ixtlan
23
2
  module Babel
24
- class ModelFilter < AbstractFilter
25
-
26
- def filter( model, &block )
27
- if model
28
- data = block.call( model )
29
- filter_data( model, data,
30
- Context.new( options ),
31
- &block )
32
- end
3
+
4
+ module ModelFilter
5
+
6
+ def self.included( model )
7
+ model.extend( ClassMethods )
33
8
  end
34
9
 
35
- private
10
+ def respond_to? name
11
+ @model.respond_to? name
12
+ end
36
13
 
37
- def filter_array( models, options, &block )
38
- models.collect do |i|
39
- if i.respond_to? :attributes
40
- filter_data(i, block.call(i), options, &block)
41
- else
42
- serialize( i )
14
+ def method_missing( name, *args, &block )
15
+ @model.send( name, *args, &block )
16
+ end
17
+
18
+ def to_json
19
+ to_data.to_json
20
+ end
21
+
22
+ def to_yaml
23
+ to_data.to_yaml
24
+ end
25
+
26
+ def to_data
27
+ case @model
28
+ when Array
29
+ @model.collect do |m|
30
+ replace( m ).to_hash
43
31
  end
32
+ else
33
+ to_hash
44
34
  end
45
35
  end
46
36
 
47
- def setup_data(model, data, context)
48
- context.methods.each do |m|
49
- unless data.include?(m)
50
- data[ m ] = model.send( m.to_sym )
51
- end
37
+ def serializers=( map )
38
+ @map = map
39
+ end
40
+
41
+ def serialize( data )
42
+ if @map && ser = @map[ data.class.to_s ]
43
+ ser.call( data )
44
+ else
45
+ data
52
46
  end
53
47
  end
54
48
 
55
- def filter_data(model, data, context, &block)
56
- setup_data(model, data, context)
57
-
49
+ def to_hash
58
50
  result = {}
59
- data.each do |k,v|
60
- k = k.to_s
61
- if v.respond_to? :attributes
62
- result[ k ] = filter_data( v, block.call(v), context[ k ], &block ) if context.include?( k )
63
- elsif v.is_a? Array
64
- result[ k ] = filter_array( v, context[ k ], &block ) if context.include?( k )
51
+ self.class.attributes.each do |k,v|
52
+ if v
53
+ filter = v.is_a?( Array ) ? v[ 0 ] : v
54
+ filter.serializers = @map
55
+ model = @model.send( k )
56
+ result[ k ] = filter.replace( model ).to_data if model
65
57
  else
66
- result[ k ] = serialize( v ) if context.allowed?( k ) && ! v.respond_to?( :attributes )
58
+ result[ k ] = serialize( @model.send( k ) )
67
59
  end
68
60
  end
69
61
  result
70
62
  end
63
+
64
+ def initialize( model = nil, map = nil )
65
+ super()
66
+ @map = map
67
+ replace( model )
68
+ end
69
+
70
+ def replace( model )
71
+ @model = model
72
+ self
73
+ end
74
+
75
+ module ClassMethods
76
+
77
+ def attribute( name, type = nil )
78
+ attributes[ name.to_sym ] = new_instance( type )
79
+ end
80
+
81
+ def attributes
82
+ @attributes ||= (superclass.attributes.dup rescue nil) || {}
83
+ end
84
+
85
+ def new_instance( type )
86
+ case type
87
+ when Array
88
+ [ type[ 0 ].new ]
89
+ when NilClass
90
+ nil
91
+ else
92
+ type.new
93
+ end
94
+ end
95
+ end
71
96
  end
72
97
  end
73
98
  end
99
+
100
+ # class User
101
+ # include Babel::ModelFilter
102
+
103
+ # attribute :id
104
+ # end
105
+ # class A
106
+ # include Babel::ModelFilter
107
+
108
+ # attribute :id
109
+ # attribute :created_at, Array[User]
110
+ # attribute :updated_at
111
+ # attribute :modified_by, User
112
+
113
+ # end
114
+
115
+ # class B
116
+ # attr_accessor :id, :created_at, :updated_at, :name, :modified_by
117
+ # end
118
+
119
+ # b = B.new
120
+ # b.id = 123
121
+ # require 'date'
122
+ # b.updated_at = DateTime.now
123
+ # b.name ='bla'
124
+ # b.modified_by = B.new
125
+ # b.modified_by.id = 432
126
+ # b.modified_by.name = 'asd432'
127
+ # b.created_at = [ b, b.modified_by]
128
+
129
+ # require_relative 'factory'
130
+ # a = A.new( b, Babel::Factory::DEFAULT_MAP )
131
+
132
+ # p a
133
+ # p a.to_hash
134
+
135
+ # p a.id
@@ -0,0 +1,103 @@
1
+ module Ixtlan
2
+ module Babel
3
+
4
+ module ModelSerializer
5
+
6
+ attr_reader :model
7
+
8
+ def self.included( model )
9
+ model.extend( ClassMethods )
10
+ end
11
+
12
+ def respond_to? name
13
+ @model.respond_to? name
14
+ end
15
+
16
+ def method_missing( name, *args, &block )
17
+ @model.send( name, *args, &block )
18
+ end
19
+
20
+ def to_json
21
+ to_data.to_json
22
+ end
23
+
24
+ def to_yaml
25
+ to_data.to_yaml
26
+ end
27
+
28
+ def to_data
29
+ if @model.respond_to?( :collect ) and not @model.is_a?( Hash )
30
+ @model.collect do |m|
31
+ replace( m ).to_hash
32
+ end
33
+ else
34
+ to_hash
35
+ end
36
+ end
37
+
38
+ def serializers=( map )
39
+ @map = map
40
+ end
41
+
42
+ def serialize( data )
43
+ if @map && ser = @map[ data.class.to_s ]
44
+ ser.call(data)
45
+ else
46
+ data
47
+ end
48
+ end
49
+
50
+ def to_hash
51
+ result = {}
52
+ self.class.attributes.each do |k,v|
53
+ if v
54
+ filter = v.is_a?( Array ) ? v[ 0 ] : v
55
+ filter.serializers = @map
56
+ model = @model.send( k )
57
+ result[ k ] = filter.replace( model ).to_data if model
58
+ else
59
+ result[ k ] = serialize( @model.send( k ) )
60
+ end
61
+ end
62
+ result
63
+ end
64
+
65
+ def initialize( model = nil, map = nil )
66
+ super()
67
+ @map = map
68
+ replace( model )
69
+ end
70
+
71
+ def replace( model )
72
+ @model = model
73
+ self
74
+ end
75
+
76
+ module ClassMethods
77
+
78
+ def attribute( name, type = nil )
79
+ attributes[ name.to_sym ] = new_instance( type )
80
+ end
81
+
82
+ def attributes( *args )
83
+ if args.size == 0
84
+ @attributes ||= (superclass.attributes.dup rescue nil) || {}
85
+ else
86
+ args.each { |a| attribute( a ) }
87
+ end
88
+ end
89
+
90
+ def new_instance( type )
91
+ case type
92
+ when Array
93
+ [ type[ 0 ].new ]
94
+ when NilClass
95
+ nil
96
+ else
97
+ type.new
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end