simple_ams 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,24 +1,30 @@
1
1
  require "simple_ams"
2
2
 
3
3
  class SimpleAMS::Document
4
- attr_reader :options, :serializer, :resource
4
+ attr_reader :options, :embedded_options, :serializer, :resource
5
5
 
6
- def initialize(options = SimpleAMS::Options.new)
6
+ def initialize(options, embedded_options = nil)
7
7
  @options = options
8
+ @embedded_options = embedded_options
8
9
  @serializer = options.serializer
9
10
  @resource = options.resource
10
11
  end
11
12
 
12
13
  def primary_id
13
- options.primary_id
14
+ @primary_id ||= self.class::PrimaryId.new(options)
14
15
  end
15
16
 
16
17
  def fields
18
+ return @fields if defined?(@fields)
19
+ return @fields = [] if options.fields.empty?
17
20
  return @fields ||= self.class::Fields.new(options)
18
21
  end
19
22
 
20
23
  def relations
21
- return @relations ||= self.class::Relations.new(options)
24
+ return @relations if defined?(@relations)
25
+ return @relations ||= self.class::Relations.new(
26
+ options, options.relations
27
+ )
22
28
  end
23
29
 
24
30
  def name
@@ -34,13 +40,29 @@ class SimpleAMS::Document
34
40
  end
35
41
 
36
42
  def links
43
+ return @links if defined?(@links)
44
+ return @links = {} if options.links.empty?
37
45
  return @links ||= self.class::Links.new(options)
38
46
  end
39
47
 
40
48
  def metas
49
+ return @metas if defined?(@metas)
50
+ return @metas = {} if options.metas.empty?
41
51
  return @metas ||= self.class::Metas.new(options)
42
52
  end
43
53
 
54
+ def forms
55
+ return @forms if defined?(@forms)
56
+ return @forms = {} if options.forms.empty?
57
+ return @forms ||= self.class::Forms.new(options)
58
+ end
59
+
60
+ def generics
61
+ return @generics if defined?(@generics)
62
+ return @generics = {} if options.generics.empty?
63
+ return @generics ||= self.class::Generics.new(options)
64
+ end
65
+
44
66
  def folder?
45
67
  self.is_a?(self.class::Folder)
46
68
  end
@@ -49,18 +71,37 @@ class SimpleAMS::Document
49
71
  !folder?
50
72
  end
51
73
 
74
+ def embedded
75
+ return nil unless embedded_options
76
+
77
+ @embedded ||= SimpleAMS::Document.new(embedded_options)
78
+ end
79
+
52
80
  class Folder < self
53
- attr_reader :collection
81
+ include Enumerable
82
+ attr_reader :members
54
83
 
55
- def initialize(options)
84
+ def initialize(options, embedded_options = nil)
56
85
  @_options = options
86
+ @embedded_options = embedded_options
57
87
  @options = @_options.collection_options
58
88
 
59
- @collection = options.collection
89
+ @members = options.collection
90
+ end
91
+
92
+ def each(&block)
93
+ return enum_for(:each) unless block_given?
94
+
95
+ members.each do |resource|
96
+ yield SimpleAMS::Document.new(options_for(resource))
97
+ end
98
+
99
+ self
60
100
  end
61
101
 
102
+ #do we really need this method ?
62
103
  def documents
63
- @documents = collection.map do |resource|
104
+ @members.map do |resource|
64
105
  SimpleAMS::Document.new(options_for(resource))
65
106
  end
66
107
  end
@@ -72,15 +113,26 @@ class SimpleAMS::Document
72
113
  private
73
114
  attr_reader :_options
74
115
 
75
- #TODO: OBS! here we have extra cost for nothing
76
- #can't we just pass the resource_option with different resource?
77
116
  def options_for(resource)
78
- SimpleAMS::Options.new(resource, {
79
- injected_options: resource_options.injected_options.merge({
80
- serializer: serializer_for(resource)
81
- }),
82
- allowed_options: resource_options.allowed_options
83
- })
117
+ if resource_options.serializer_class.respond_to?(:call)
118
+ SimpleAMS::Options.new(resource, {
119
+ injected_options: resource_options.injected_options.merge({
120
+ serializer: serializer_for(resource)
121
+ }),
122
+ allowed_options: serializer_for(resource).options
123
+ })
124
+ else
125
+ resource_options.with_resource(resource)
126
+ end
127
+ =begin
128
+ SimpleAMS::Options.new(resource, {
129
+ injected_options: resource_options.injected_options.merge({
130
+ serializer: serializer_for(resource)
131
+ }),
132
+ allowed_options: serializer_for(resource).options
133
+ })
134
+ end
135
+ =end
84
136
  end
85
137
 
86
138
  def serializer_for(resource)
@@ -16,16 +16,27 @@ module SimpleAMS::DSL
16
16
  includes: includes,
17
17
  links: links,
18
18
  metas: metas,
19
+ forms: forms,
20
+ generics: generics,
19
21
  }
20
22
  end
21
23
  end
22
24
 
23
- host_class.const_set('Collection', _klass)
25
+ host_class.const_set('Collection_', _klass)
24
26
  end
25
27
 
26
28
  module ClassMethods
27
29
  #TODO: Shouldn't we call here super to presever user's behavior ?
28
30
  def inherited(subclass)
31
+ #TODO: why this breaks collection type?
32
+ subclass.with_options(
33
+ options.merge(
34
+ #TODO: maybe add another group of elements under dsl?
35
+ #this could be DSL::Type.new(type).explicit?
36
+ type.last[:_explicit] ? {} : {type: nil}
37
+ )
38
+ )
39
+
29
40
  _klass = Class.new(Object).extend(ClassMethods)
30
41
  _klass.instance_eval do
31
42
  def options
@@ -38,11 +49,20 @@ module SimpleAMS::DSL
38
49
  includes: includes,
39
50
  links: links,
40
51
  metas: metas,
52
+ forms: forms,
53
+ generics: generics,
41
54
  }
42
55
  end
43
56
  end
44
57
 
45
- subclass.const_set('Collection', _klass)
58
+ _klass.with_options(
59
+ self::Collection_.options.merge(
60
+ #TODO: maybe add another group of elements under dsl?
61
+ #this could be DSL::Type.new(type).explicit?
62
+ type.last[:_explicit] ? {} : {type: nil}
63
+ )
64
+ )
65
+ subclass.const_set('Collection_', _klass)
46
66
  end
47
67
 
48
68
  def default_options
@@ -55,11 +75,11 @@ module SimpleAMS::DSL
55
75
 
56
76
  #TODO: Add tests !!
57
77
  def _default_type_name
58
- if self.to_s.end_with?('::Collection')
78
+ if self.to_s.end_with?('::Collection_')
59
79
  _name = self.to_s.gsub(
60
80
  'Serializer',''
61
81
  ).gsub(
62
- '::Collection', ''
82
+ '::Collection_', ''
63
83
  ).downcase.split('::').last
64
84
 
65
85
  return "#{_name}_collection".to_sym
@@ -71,11 +91,21 @@ module SimpleAMS::DSL
71
91
  @_options = options
72
92
  meths = SimpleAMS::DSL::ClassMethods.instance_methods(false)
73
93
  @_options.each do |key, value|
74
- if key.to_sym == :collection
75
- self.send(:collection){}.with_options(value)
94
+ if key == :relations
95
+ (value || []).each{|rel_value|
96
+ append_relationship(rel_value)
97
+ }
98
+ elsif key.to_sym == :collection
99
+ #TODO: Add proc option maybe?
100
+ if value.is_a?(Hash)
101
+ collection{}.with_options(value)
102
+ end
76
103
  elsif meths.include?(key)
77
- self.send(key, value) if value.is_a?(Array)
78
- self.send(key, value)
104
+ if (value.is_a?(Array) && value.first.is_a?(Array)) || value.is_a?(Hash)
105
+ self.send(key, value)
106
+ else
107
+ self.send(key, *value)
108
+ end
79
109
  else
80
110
  #TODO: Add a proper logger
81
111
  puts "SimpeAMS: #{key} is not recognized, ignoring (from #{self.to_s})"
@@ -85,26 +115,16 @@ module SimpleAMS::DSL
85
115
  return @_options
86
116
  end
87
117
 
88
- #same for other ValueHashes
89
- def adapter(name = nil, options = {})
90
- @_adapter ||= default_options[:adapter]
91
- return @_adapter if name.nil?
92
-
93
- @_adapter = [name, options]
118
+ def adapter(value = nil, options = {})
119
+ @_adapter = _value_hash_for(@_adapter, value, options, :adapter)
94
120
  end
95
121
 
96
122
  def primary_id(value = nil, options = {})
97
- @_primary_id ||= default_options[:primary_id]
98
- return @_primary_id if value.nil?
99
-
100
- @_primary_id = [value, options]
123
+ @_primary_id = _value_hash_for(@_primary_id, value, options, :primary_id)
101
124
  end
102
125
 
103
126
  def type(value = nil, options = {})
104
- @_type ||= default_options[:type]
105
- return @_type if value.nil?
106
-
107
- @_type = [value, options]
127
+ @_type = _value_hash_for(@_type, value, options.merge(_explicit: true), :type)
108
128
  end
109
129
 
110
130
  def attributes(*args)
@@ -116,26 +136,50 @@ module SimpleAMS::DSL
116
136
  alias attribute attributes
117
137
  alias fields attributes
118
138
 
119
- def has_many(name, options = {})
120
- append_relationship([__method__, name, options])
139
+ def attributes=(*args)
140
+ @_attributes = []
141
+
142
+ attributes(args)
143
+ end
144
+
145
+ def has_many(name, options = {}, &block)
146
+ append_relationship(
147
+ [__method__, name, options, embedded_class_for(name, options, block)]
148
+ )
121
149
  end
122
150
 
123
- def has_one(name, options = {})
124
- append_relationship([__method__, name, options])
151
+ def has_one(name, options = {}, &block)
152
+ append_relationship(
153
+ [__method__, name, options, embedded_class_for(name, options, block)]
154
+ )
125
155
  end
126
156
 
127
- def belongs_to(name, options = {})
128
- append_relationship([__method__, name, options])
157
+ def belongs_to(name, options = {}, &block)
158
+ append_relationship(
159
+ [__method__, name, options, embedded_class_for(name, options, block)]
160
+ )
129
161
  end
130
162
 
131
163
  def relations
132
164
  @_relations || []
133
165
  end
134
166
 
167
+ def embedded_class_for(name, options, block)
168
+ embedded = Class.new(self)
169
+ klass_name = "Embedded#{name.to_s.capitalize}Options_"
170
+ self.const_set(klass_name, embedded)
171
+ embedded.with_options(
172
+ default_options.merge(options.select{|k| k != :serializer})
173
+ )
174
+ embedded.instance_exec(&block) if block
175
+
176
+ return embedded
177
+ end
178
+
135
179
  #TODO: there is no memoization here, hence we ignore includes manually set !!
136
180
  #Consider fixing it by employing an observer that will clean the instance var
137
181
  #each time @_relations is updated
138
- def includes(_ = [])
182
+ def includes(*args)
139
183
  relations.map{|rel| rel[1] }
140
184
  end
141
185
 
@@ -147,28 +191,53 @@ module SimpleAMS::DSL
147
191
  append_meta([name, value, options])
148
192
  end
149
193
 
194
+ def form(name, value, options = {})
195
+ append_form([name, value, options])
196
+ end
197
+
198
+ def generic(name, value, options = {})
199
+ append_generic([name, value, options])
200
+ end
201
+
150
202
  def links(links = [])
203
+ return @_links ||= [] if links.empty?
151
204
  links.map{|key, value| append_link([key, value].flatten(1))} if links.is_a?(Hash)
152
205
 
153
206
  @_links ||= links
154
207
  end
155
208
 
156
209
  def metas(metas = [])
210
+ return @_metas ||= [] if metas.empty?
211
+
157
212
  metas.map{|key, value| append_meta([key, value].flatten(1))} if metas.is_a?(Hash)
158
213
 
159
- @_metas || []
214
+ @_metas ||= metas
215
+ end
216
+
217
+ def forms(forms = [])
218
+ return @_forms ||= [] if forms.empty?
219
+ forms.map{|key, value| append_form([key, value].flatten(1))} if forms.is_a?(Hash)
220
+
221
+ @_forms ||= forms
222
+ end
223
+
224
+ def generics(generics = [])
225
+ return @_generics ||= [] if generics.empty?
226
+ generics.map{|key, value| append_generic([key, value].flatten(1))} if generics.is_a?(Hash)
227
+
228
+ @_generics ||= generics
160
229
  end
161
230
 
162
231
  def collection(name = nil, &block)
163
232
  if block
164
- self::Collection.class_eval do
233
+ self::Collection_.class_eval do
165
234
  instance_exec(&block)
166
235
  end
167
236
  end
168
237
 
169
- self::Collection.type(name) if name
238
+ self::Collection_.type(name) if name
170
239
 
171
- return self::Collection
240
+ return self::Collection_
172
241
  end
173
242
 
174
243
  def options
@@ -181,11 +250,24 @@ module SimpleAMS::DSL
181
250
  includes: includes,
182
251
  links: links,
183
252
  metas: metas,
253
+ forms: forms,
254
+ generics: generics,
184
255
  collection: collection
185
256
  }
186
257
  end
187
258
 
259
+ def simple_ams?
260
+ true
261
+ end
262
+
188
263
  private
264
+ def _value_hash_for(current, value, options, name)
265
+ _type = current || default_options[name]
266
+ return _type if value.nil?
267
+
268
+ return [value, options]
269
+ end
270
+
189
271
  def append_relationship(rel)
190
272
  @_relations ||= []
191
273
 
@@ -210,6 +292,18 @@ module SimpleAMS::DSL
210
292
  @_metas << meta
211
293
  end
212
294
 
295
+ def append_form(form)
296
+ @_forms ||= []
297
+
298
+ @_forms << form
299
+ end
300
+
301
+ def append_generic(generic)
302
+ @_generics ||= []
303
+
304
+ @_generics << generic
305
+ end
306
+
213
307
  def empty_options
214
308
  end
215
309
  end
@@ -5,14 +5,15 @@ class SimpleAMS::Options
5
5
  module NameValueHash
6
6
  attr_reader :name, :value, :options
7
7
 
8
- def initialize(name, value, options = {}, resource:)
8
+ def initialize(name, value, options = {}, resource:, serializer:)
9
9
  @name = name.is_a?(String) ? name.to_sym : name
10
- if value.is_a?(Proc)
11
- _value = value.call(resource)
12
- @value = _value.first
10
+ if value.is_a?(Proc) #TODO: maybe we should do duck typing instead?
11
+ _value = value.call(resource, serializer)
13
12
  if _value.is_a?(Array) && _value.length > 1
13
+ @value = _value.first
14
14
  @options = (_value.last || {}).merge(options || {})
15
15
  else
16
+ @value = _value
16
17
  @options = options || {}
17
18
  end
18
19
  else
@@ -5,9 +5,20 @@ class SimpleAMS::Options
5
5
  module ValueHash
6
6
  attr_reader :value, :options
7
7
 
8
- def initialize(value, options = {})
9
- @value = value.is_a?(String) ? value.to_sym : value
10
- @options = options.kind_of?(Hash) ? options || {} : options
8
+ def initialize(value, options = {}, resource:, serializer:)
9
+ if value.is_a?(Proc) #TODO: maybe we should do duck typing instead?
10
+ _value = value.call(resource, serializer)
11
+ if _value.is_a?(Array) && _value.length > 1
12
+ @value = _value.first
13
+ @options = (_value.last || {}).merge(options || {})
14
+ else
15
+ @value = _value
16
+ @options = options || {}
17
+ end
18
+ else
19
+ @value = value.is_a?(String) ? value.to_sym : value
20
+ @options = options || {}
21
+ end
11
22
  end
12
23
 
13
24
  alias_method :name, :value
@@ -0,0 +1,12 @@
1
+ require "simple_ams"
2
+
3
+ class SimpleAMS::Options
4
+ class Forms < Array
5
+ include SimpleAMS::Options::Concerns::Filterable
6
+
7
+ class Form
8
+ include SimpleAMS::Options::Concerns::NameValueHash
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,13 @@
1
+ require "simple_ams"
2
+
3
+ class SimpleAMS::Options
4
+ class Generics < Array
5
+ include SimpleAMS::Options::Concerns::Filterable
6
+
7
+ class Option
8
+ include SimpleAMS::Options::Concerns::NameValueHash
9
+ end
10
+ end
11
+ end
12
+
13
+
@@ -4,7 +4,6 @@ class SimpleAMS::Options
4
4
  class Metas < Array
5
5
  include SimpleAMS::Options::Concerns::Filterable
6
6
 
7
- #TODO: should it check empty value ?
8
7
  class Meta
9
8
  include SimpleAMS::Options::Concerns::NameValueHash
10
9
  end
@@ -0,0 +1,52 @@
1
+ require "simple_ams"
2
+
3
+ class SimpleAMS::Options
4
+ class Relations < Array
5
+ attr_reader :relations, :includes
6
+
7
+ def initialize(relations, includes = nil)
8
+ @relations = relations
9
+ @includes = includes
10
+
11
+ super(relations.map{|rel| Relations::Relation.new(*rel)})
12
+ end
13
+
14
+ def available
15
+ return @available ||= self if includes.nil?
16
+ return @available ||= [] if includes.empty?
17
+
18
+ @available ||= self.select{
19
+ |relation| includes.include?(relation.name)
20
+ }
21
+ end
22
+
23
+ class Relation
24
+ attr_reader :type, :name, :options, :embedded
25
+ def initialize(type, name, options = {}, embedded)
26
+ @type = type.to_sym
27
+ @name = name.is_a?(String) ? name.to_sym : name
28
+ @options = options
29
+ @embedded = embedded
30
+
31
+ @many = type == :has_many ? true : false
32
+ end
33
+
34
+ alias relation name
35
+
36
+ def raw
37
+ [type, name, options, embedded]
38
+ end
39
+
40
+ def collection?
41
+ @many
42
+ end
43
+
44
+ def single?
45
+ !collection?
46
+ end
47
+
48
+ private
49
+ attr_writer :type, :name, :options
50
+ end
51
+ end
52
+ end