simple_ams 0.1.0 → 0.1.1

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.
@@ -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