simple_ams 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,299 @@
1
+ require "simple_ams"
2
+
3
+ module SimpleAMS
4
+ class Options
5
+ class Collection < self; end
6
+
7
+ attr_reader :resource, :allowed_options, :injected_options
8
+
9
+ #injected_options is always a Hash object
10
+ def initialize(resource, injected_options: {}, allowed_options: nil)
11
+ @resource = resource
12
+ @injected_options = injected_options || {}
13
+ @_internal = @injected_options[:_internal] || {}
14
+ @allowed_options = allowed_options || fetch_allowed_options
15
+ end
16
+ alias collection resource
17
+
18
+ def relation_options_for(relation_name)
19
+ return _relation_options[relation_name] || {}
20
+ end
21
+
22
+ def primary_id
23
+ return @primary_id if defined?(@primary_id)
24
+
25
+ _options = injected_options.fetch(:primary_id, nil)
26
+ _options = allowed_options.fetch(:primary_id, nil) unless _options
27
+
28
+ return @primary_id ||= PrimaryId.new(*_options)
29
+ end
30
+
31
+ def type
32
+ return @type if defined?(@type)
33
+
34
+ _options = injected_options.fetch(:type, nil)
35
+ _options = allowed_options.fetch(:type, nil) unless _options
36
+
37
+ return @type ||= Type.new(*_options)
38
+ end
39
+
40
+ #that's handful
41
+ def name
42
+ @name ||= injected_options[:name] || allowed_options[:name] || type.name
43
+ end
44
+
45
+ #TODO: optimize for nested fields?
46
+ def fields
47
+ return @fields if defined?(@fields)
48
+
49
+ injected = injected_options.fetch(:fields, nil)
50
+
51
+ if injected.nil?
52
+ return @fields = Fields.new(allowed_options.fetch(:fields).uniq)
53
+ else
54
+ return @fields = Fields.new(options_for(
55
+ injected: Fields.new(injected_options.fetch(:fields, nil)),
56
+ allowed: Fields.new(allowed_options.fetch(:fields).uniq)
57
+ ).uniq)
58
+ end
59
+ end
60
+
61
+ def includes
62
+ return @includes if defined?(@includes)
63
+
64
+ injected = injected_options.fetch(:includes, nil)
65
+
66
+ if injected.nil?
67
+ return @includes = Includes.new(allowed_options.fetch(:includes).uniq)
68
+ else
69
+ return @includes = Includes.new(options_for(
70
+ injected: Includes.new(injected_options.fetch(:includes, nil)),
71
+ allowed: Includes.new(allowed_options.fetch(:includes).uniq)
72
+ ).uniq)
73
+ end
74
+ end
75
+
76
+ #TODO: correctly loop over injected relations, although should be a rarely used feature
77
+ def relations
78
+ return @relations if defined?(@relations) #||= options_for(
79
+
80
+ relations = injected_options.fetch(:relations, nil)
81
+ relations = allowed_options.fetch(:relations, []) if relations.nil?
82
+
83
+ return @relations = relations.map{|rel| Relation.new(*rel)}.select{
84
+ |relation| includes.include?(relation.name)
85
+ }
86
+ end
87
+
88
+ #TODO: add method-based links, should boost performance
89
+ def links
90
+ return @links if defined?(@links)
91
+
92
+ injected = injected_options.fetch(:links, nil)
93
+ if injected
94
+ injected = Links.new(
95
+ injected.map{|l| Links::Link.new(*l.flatten, resource: resource)}
96
+ )
97
+ end
98
+
99
+ allowed = Links.new(
100
+ allowed_options.fetch(:links).map{|l| Links::Link.new(*l, resource: resource)}
101
+ )
102
+
103
+ return @links = Links.new(options_for(
104
+ #TODO: correctly loop over injected properties
105
+ injected: injected,
106
+ allowed: allowed,
107
+ ).uniq{|link| link.name})
108
+ end
109
+
110
+ #TODO: add method-based metas, should boost performance
111
+ def metas
112
+ return @metas if defined?(@metas)
113
+
114
+ injected = injected_options.fetch(:metas, nil)
115
+ if injected
116
+ injected = Metas.new(
117
+ injected.map{|l| Metas::Meta.new(*l.flatten, resource: resource)}
118
+ )
119
+ end
120
+ allowed = Metas.new(
121
+ allowed_options.fetch(:metas).map{|l| Metas::Meta.new(*l, resource: resource)}
122
+ )
123
+
124
+ return @metas = Metas.new(options_for(
125
+ #TODO: correctly loop over injected properties
126
+ injected: injected,
127
+ allowed: allowed,
128
+ ).uniq{|meta| meta.name})
129
+ end
130
+
131
+ #TODO: handle case of proc
132
+ def serializer
133
+ return @serializer if defined?(@serializer)
134
+
135
+ _serializer = injected_options.fetch(:serializer, serializer_class)
136
+
137
+ return @serializer = _serializer.new.extend(
138
+ SimpleAMS::Methy.of(
139
+ expose.merge({
140
+ object: resource
141
+ })
142
+ )
143
+ )
144
+ end
145
+
146
+ def adapter
147
+ return @adapter if defined?(@adapter)
148
+
149
+ @adapter = Adapter.new(*injected_options.fetch(:adapter, [nil]))
150
+ @adapter = Adapter.new(*allowed_options.fetch(:adapter, [nil])) if @adapter.value.nil?
151
+ @adapter = Adapter.new(SimpleAMS::Adapters::AMS) if @adapter.value.nil?
152
+
153
+ return @adapter
154
+ end
155
+
156
+ # the following should be the same for all (nested) serializers of the same document
157
+ def expose
158
+ @expose ||= injected_options.fetch(:expose, {})
159
+ end
160
+
161
+ def as_hash
162
+ {
163
+ adapter: adapter.raw,
164
+ primary_id: primary_id.raw,
165
+ type: type.raw,
166
+ name: name,
167
+ fields: fields.raw,
168
+ serializer: serializer_class,
169
+ #relations: relations.raw, #TODO: why have I commented that out ?
170
+ includes: includes.raw,
171
+ links: links.raw,
172
+ metas: metas.raw,
173
+ expose: expose,
174
+ _internal: _internal
175
+ }
176
+ end
177
+
178
+ def collection_options
179
+ return @collection_options if defined?(@collection_options)
180
+
181
+ #TODO: Do we need that merge ?
182
+ _injected_options = @injected_options.fetch(:collection, {}).merge({
183
+ serializer: collection_serializer_class
184
+ })
185
+ _allowed_options = @allowed_options.fetch(:collection).options
186
+
187
+ return @collection_options = self.class::Collection.new(
188
+ resource,
189
+ injected_options: _injected_options,
190
+ allowed_options: _allowed_options
191
+ )
192
+ end
193
+
194
+ def serializer_class
195
+ return @serializer_class if defined?(@serializer_class)
196
+
197
+ @serializer_class = injected_options.fetch(:serializer, nil)
198
+
199
+ return @serializer_class if @serializer_class
200
+
201
+ return @serializer_class = infer_serializer
202
+ end
203
+
204
+ #TODO: maybe have that inside :collection? (isomorphism)
205
+ def collection_serializer_class
206
+ return @collection_serializer_class if defined?(@collection_serializer_class)
207
+
208
+ if serializer_class.is_a?(Proc)
209
+ @collection_serializer_class = injected_options[:collection_serializer]
210
+ if @collection_serializer_class.nil?
211
+ raise "In case of a proc serializer, you need to specify a collection_serializer"
212
+ end
213
+ else
214
+ @collection_serializer_class = serializer_class
215
+ end
216
+
217
+ return @collection_serializer_class
218
+ end
219
+
220
+ private
221
+ attr_reader :_internal
222
+ =begin
223
+ def is_collection?
224
+ _internal[:is_collection] == true
225
+ end
226
+ =end
227
+
228
+ def options_for(allowed:, injected:)
229
+ if not injected.nil?
230
+ allowed = injected.class.new(
231
+ injected.map{|s| s.is_a?(Hash) ? s.keys.first : s}
232
+ ) & allowed
233
+ end
234
+
235
+ return allowed
236
+ end
237
+
238
+ def _relation_options
239
+ return @_relation_options if defined?(@_relation_options)
240
+
241
+ @_relation_options = relations.inject({}){|memo, relation|
242
+ includes_value = (injected_options[:includes] || {}).select{|incl|
243
+ incl.is_a?(Hash)
244
+ }.find{|incl_hash|
245
+ incl_hash.keys.first.to_s == relation.name.to_s
246
+ }
247
+ if includes_value
248
+ includes_value = includes_value[relation.name]
249
+ else
250
+ #it's important here to return empty array if nothing is found..
251
+ includes_value = []
252
+ end
253
+
254
+ fields_value = (injected_options[:fields] || {}).select{|field|
255
+ field.is_a?(Hash)
256
+ }.find{|field_hash|
257
+ field_hash.keys.first.to_s == relation.name.to_s
258
+ }
259
+
260
+ #.. while here just nil will work (pick default fields from serializer)
261
+ fields_value = fields_value[relation.name] if fields_value
262
+
263
+ memo[relation.name] = {
264
+ includes: includes_value,
265
+ fields: fields_value
266
+ }
267
+ memo
268
+ }
269
+ end
270
+
271
+ #TODO: raise exception if both are nil!
272
+ def fetch_allowed_options
273
+ _serializer_class = self.serializer_class
274
+ if _serializer_class.is_a?(Proc)
275
+ _serializer_class = self.collection_serializer_class
276
+ end
277
+
278
+ _allowed_options = _serializer_class&.options
279
+
280
+ return _allowed_options
281
+ end
282
+
283
+ def infer_serializer
284
+ namespace = _internal[:module] ? "#{_internal[:module]}::" : ""
285
+ resource_klass = resource.kind_of?(Array) ? resource.first.class : resource.class
286
+ if resource_klass == NilClass
287
+ return EmptySerializer
288
+ else
289
+ return Object.const_get("#{namespace}#{resource_klass.to_s}Serializer")
290
+ end
291
+ rescue NameError => _
292
+ raise "Could not infer serializer for #{resource.class}, maybe specify it? (tried #{namespace}#{resource_klass.to_s}Serializer)"
293
+ end
294
+
295
+ class EmptySerializer
296
+ include SimpleAMS::DSL
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,59 @@
1
+ require "simple_ams"
2
+
3
+ module SimpleAMS
4
+ class Renderer
5
+ def initialize(resource, options = {})
6
+ @resource = resource
7
+ @options = SimpleAMS::Options.new(resource, injected_options: options)
8
+ end
9
+
10
+ #resource decorator ?
11
+ def document
12
+ @document ||= SimpleAMS::Document.new(options)
13
+ end
14
+
15
+ def name
16
+ @options.name
17
+ end
18
+
19
+ def as_json
20
+ options.adapter.klass.new(document, options.adapter.options).as_json
21
+ end
22
+
23
+ def to_json
24
+ as_json.to_json
25
+ end
26
+
27
+ class Collection
28
+ def initialize(collection, options = {})
29
+ @collection = collection
30
+ @options = SimpleAMS::Options.new(collection, {
31
+ injected_options: options.merge(_internal: is_collection)
32
+ })
33
+ end
34
+
35
+ def folder
36
+ @folder ||= SimpleAMS::Document::Folder.new(options)
37
+ end
38
+
39
+ def as_json
40
+ options.adapter.klass::Collection.new(folder, options.adapter.options).as_json
41
+ end
42
+
43
+ def to_json
44
+ as_json.to_json
45
+ end
46
+
47
+ private
48
+ attr_reader :collection, :options
49
+
50
+ def is_collection
51
+ { is_collection: true }
52
+ end
53
+ end
54
+
55
+ private
56
+ attr_reader :resource, :options
57
+ end
58
+
59
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleAMS
2
+ VERSION = "0.1.0"
3
+ end
data/lib/simple_ams.rb ADDED
@@ -0,0 +1,29 @@
1
+ require "delegate"
2
+ require "simple_ams/version"
3
+ require "simple_ams/methy"
4
+ require "simple_ams/document"
5
+ require "simple_ams/dsl"
6
+ require "simple_ams/adapters"
7
+ require "simple_ams/adapters/ams"
8
+ require "simple_ams/renderer"
9
+
10
+ require "simple_ams/options"
11
+ require "simple_ams/options/concerns/filterable"
12
+ require "simple_ams/options/concerns/name_value_hash"
13
+ require "simple_ams/options/concerns/value_hash"
14
+ require "simple_ams/options/adapter"
15
+ require "simple_ams/options/fields"
16
+ require "simple_ams/options/includes"
17
+ require "simple_ams/options/links"
18
+ require "simple_ams/options/metas"
19
+ require "simple_ams/options/primary_id"
20
+ require "simple_ams/options/type"
21
+ require "simple_ams/options/relation"
22
+
23
+ require "simple_ams/document/fields"
24
+ require "simple_ams/document/relations"
25
+ require "simple_ams/document/links"
26
+ require "simple_ams/document/metas"
27
+
28
+ module SimpleAMS
29
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "simple_ams/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "simple_ams"
8
+ spec.version = SimpleAMS::VERSION
9
+ spec.authors = ["Filippos Vasilakis"]
10
+ spec.email = ["vasilakisfil@gmail.com"]
11
+
12
+ spec.summary = %q{ActiveModel Serializers, simplified.}
13
+ spec.description = %q{ActiveModel Serializers, simplified.}
14
+ spec.homepage = "https://github.com/vasilakisfil/simple-ams"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.15"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_development_dependency "faker", "~> 1.8.5"
27
+ spec.add_development_dependency "pry", "~> 0.11.3"
28
+ spec.add_development_dependency "simplecov"
29
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_ams
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Filippos Vasilakis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-08-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faker
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.8.5
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.8.5
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.11.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.11.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: ActiveModel Serializers, simplified.
98
+ email:
99
+ - vasilakisfil@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - lib/simple_ams.rb
113
+ - lib/simple_ams/adapters.rb
114
+ - lib/simple_ams/adapters/ams.rb
115
+ - lib/simple_ams/document.rb
116
+ - lib/simple_ams/document/fields.rb
117
+ - lib/simple_ams/document/links.rb
118
+ - lib/simple_ams/document/metas.rb
119
+ - lib/simple_ams/document/relations.rb
120
+ - lib/simple_ams/dsl.rb
121
+ - lib/simple_ams/methy.rb
122
+ - lib/simple_ams/options.rb
123
+ - lib/simple_ams/options/adapter.rb
124
+ - lib/simple_ams/options/concerns/filterable.rb
125
+ - lib/simple_ams/options/concerns/name_value_hash.rb
126
+ - lib/simple_ams/options/concerns/value_hash.rb
127
+ - lib/simple_ams/options/fields.rb
128
+ - lib/simple_ams/options/includes.rb
129
+ - lib/simple_ams/options/links.rb
130
+ - lib/simple_ams/options/metas.rb
131
+ - lib/simple_ams/options/primary_id.rb
132
+ - lib/simple_ams/options/relation.rb
133
+ - lib/simple_ams/options/type.rb
134
+ - lib/simple_ams/renderer.rb
135
+ - lib/simple_ams/version.rb
136
+ - simple_ams.gemspec
137
+ homepage: https://github.com/vasilakisfil/simple-ams
138
+ licenses: []
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.6.12
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: ActiveModel Serializers, simplified.
160
+ test_files: []