active_mappers 1.2.4 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 492d45c2bd3a72e786b65fa4305dfec8c2847208e4f92b8c1ed790414b4b45fa
4
- data.tar.gz: 2db83741a820fc9a797228e2f026d191df807e7c7f0006211e46ec5ede34e63b
3
+ metadata.gz: 2ec2a77d6b91e05de5d86ab9a43b37d5fd7245085d696ccd9c7fb5aa900a42a3
4
+ data.tar.gz: b930be8ee3ac6adf404cfe9c0bad1107b9eb3287912c347b940438f2bab95af4
5
5
  SHA512:
6
- metadata.gz: 0d4b8f0683eb36a445903f1752be3041661f93ec0a6b9fd2a755794f10208a127d9dd94e68c1dc718bc1fa9b97cf422efb910e333e9aec4ded1482a9075659b8
7
- data.tar.gz: 38a26b2aea1672f3a4f95740a31397fe3e49123e2f189b9fb596dde97dfcd1ab9ade069bb5d1d0cc8b12c55f97bd772cd97c798f358b23a3488f2f12027c85e2
6
+ metadata.gz: 9d29e59cc9231aab42657fbc2eb8c8e8106ed3a5e61752763efa3454de2775f4a7189886ebf46f626ea7b74b6d70c93bb3cf6c930dc23a77b7ffdb10cdfeaa8b
7
+ data.tar.gz: 774c84aea5e4b793ea0ce59ea8160ad2b626fe1d3d53392bc7d4909eff89c9629b7e69c1859ae8b6dc1198407f671554250430d26333d1ee8fbe26c4b657afe0
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ - 2.4.0
5
+ - 2.5.0
6
+
7
+ script: echo "Running tests against $(ruby -v) ..."
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gemspec
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ active_mappers (1.3.0)
5
+ activesupport (>= 4.2)
6
+ method_source (~> 0.9.2)
7
+ mocha (>= 1.8.0)
8
+ ruby2ruby (> 2.4.0)
9
+ ruby_parser (~> 3.1)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ activesupport (5.2.2)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 0.7, < 2)
17
+ minitest (~> 5.1)
18
+ tzinfo (~> 1.1)
19
+ concurrent-ruby (1.1.5)
20
+ i18n (1.6.0)
21
+ concurrent-ruby (~> 1.0)
22
+ metaclass (0.0.4)
23
+ method_source (0.9.2)
24
+ minitest (5.11.3)
25
+ mocha (1.8.0)
26
+ metaclass (~> 0.0.1)
27
+ rake (12.3.1)
28
+ ruby2ruby (2.4.2)
29
+ ruby_parser (~> 3.1)
30
+ sexp_processor (~> 4.6)
31
+ ruby_parser (3.13.0)
32
+ sexp_processor (~> 4.9)
33
+ sexp_processor (4.12.0)
34
+ thread_safe (0.3.6)
35
+ tzinfo (1.2.5)
36
+ thread_safe (~> 0.1)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ active_mappers!
43
+ rake
44
+
45
+ BUNDLED WITH
46
+ 1.17.2
@@ -0,0 +1,431 @@
1
+ # ActiveMappers
2
+
3
+ [![Build Status](https://travis-ci.org/FidMe/active_mappers.svg?branch=master)](https://travis-ci.org/FidMe/active_mappers)
4
+ [![Gem Version](https://badge.fury.io/rb/active_mappers.svg)](https://badge.fury.io/rb/active_mappers)
5
+
6
+ If you have ever done Rails API development, you must have considered using a layer to abstract and centralize your JSON objects construction.
7
+
8
+ There are multiple solutions out on the market, here is a quick overview of each :
9
+
10
+ | Solution | Pros | Cons |
11
+ | ------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------- |
12
+ | JBuilder | Simple, easy, integrates with the default View layer | Very slow, dedicated to JSON |
13
+ | Active Model Serializers | Simple, easy to declare | Can be hard to customize, slow, project is abandonned |
14
+ | fast_json_api | As simple as AMS, fast | Hard to customize, JSONAPI standard is required |
15
+ | ActiveMappers | Blazing fast, Easy to declare/customize, works with any format output (JSON, Hash) | Limited number of options (for now) |
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'active_mappers'
23
+ ```
24
+
25
+ Execute
26
+
27
+ ```bash
28
+ $ bundle install
29
+ ```
30
+
31
+ Then, depending on your usage you may want to create an `app/mappers` folder in your Rails application.
32
+
33
+ You will put all your mappers inside of it.
34
+
35
+ ## Usage
36
+
37
+ ```ruby
38
+ UserMapper.with(user)
39
+ # =>
40
+ # {
41
+ # user: {
42
+ # id: '123',
43
+ # email: 'mvilleneuve@snapp.fr',
44
+ # profile: {
45
+ # first_name: 'Michael',
46
+ # last_name: 'Villeneuve',
47
+ # }
48
+ # }
49
+ # }
50
+ ```
51
+
52
+ ### Setup (optional)
53
+
54
+ You may want to customize some parts of ActiveMappers behavior.
55
+
56
+ If you want to, create an initializer in your project :
57
+
58
+ ```ruby
59
+ # config/initializers/active_mappers.rb
60
+ ActiveMappers::Setup.configure do |config|
61
+ config.camelcase_keys = false
62
+ config.ignored_namespaces = [:admin, :back_office]
63
+ end
64
+ ```
65
+
66
+ Here is the list of configurable options
67
+
68
+ | Option | Type | Default | Description |
69
+ | ----------------------- | --------- | --------- | ------------------------------------------------------------------ |
70
+ | `camelcase_keys` | `boolean` | `true` | Should keys name be camelcase. Fallback to snake when set to false |
71
+ | `ignored_namespaces` | `Array` | `[]` | Namespaces to ignore when generating json root key name |
72
+ | `root_keys_transformer` | `Proc` | See below | Custom way to change a mapper class name into a JSON root key |
73
+
74
+ **Root Keys Transformer**
75
+
76
+ A root key transform is used to transform the mapper class name into a JSON root key name.
77
+
78
+ For example this mapper class :
79
+
80
+ ```ruby
81
+ class User::ProfileInformationMapper < ActiveMappers::Base
82
+ end
83
+ ```
84
+
85
+ Will automatically resolve to the following json :
86
+
87
+ ```json
88
+ {
89
+ "user/profileInformation": {}
90
+ }
91
+ ```
92
+
93
+ To customize this behavior you can do the following :
94
+
95
+ ```ruby
96
+ config.root_keys_transformer = proc do |key|
97
+ # Return any key transform based on the key which is the class name of your mapper
98
+
99
+ # The below line transforms User::ProfileInformationMapper to user/profile_informations
100
+ key.gsub('Mapper', '').tableize
101
+ end
102
+ ```
103
+
104
+ ### Creating a mapper
105
+
106
+ **Declaring your attributes**
107
+
108
+ Most basic usage, just declare the attributes you want to display.
109
+
110
+ ```ruby
111
+ class UserMapper < ActiveMappers::Base # You must extend ActiveMappers::Base in order to use the DSL
112
+ attributes :id, :email
113
+ end
114
+ ```
115
+
116
+ **Delegating your attributes**
117
+
118
+ Say you have a model with the following structure :
119
+
120
+ ```ruby
121
+ {
122
+ email: 'mvilleneuve@snapp.fr',
123
+ profile: {
124
+ first_name: 'Michael',
125
+ }
126
+ }
127
+ ```
128
+
129
+ And you want to generate this structure :
130
+
131
+ ```ruby
132
+ {
133
+ email: 'mvilleneuve@snapp.fr',
134
+ first_name: 'Michael',
135
+ last_name: 'Villeneuve',
136
+ }
137
+ ```
138
+
139
+ To implement this, you must use the `delegate` feature :
140
+
141
+ ```ruby
142
+ class UserMapper < ActiveMappers::Base
143
+ delegate :first_name, :last_name, to: :profile
144
+ end
145
+ ```
146
+
147
+ **Declaring relationship**
148
+
149
+ You can declare any type of relationship (`has_one`, `belongs_to`, `has_many`, etc) and the mapper that matches it will automatically be fetched and used.
150
+
151
+ For example if a `User` has a `belongs_to` relationship with an `Account` you can write :
152
+
153
+ ```ruby
154
+ class UserMapper < ActiveMappers::Base
155
+ attributes :email
156
+ relation :account # Will automatically resolve to AccountMapper
157
+ end
158
+
159
+ class AccountMapper < ActiveMappers::Base
160
+ attributes :first_name, :last_name
161
+ end
162
+ ```
163
+
164
+ It will generate something like
165
+
166
+ ```ruby
167
+ {
168
+ user: {
169
+ email: 'mvilleneuve@snapp.fr',
170
+ account: {
171
+ first_name: 'Michael',
172
+ last_name: 'Villeneuve'
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ It also works with namespaced resources.
179
+
180
+ If you need you can specify more options :
181
+
182
+ ```ruby
183
+ class UserMapper < ActiveMappers::Base
184
+ relation :account, AccountMapper, scope: :admin
185
+ end
186
+
187
+
188
+
189
+ **Declaring polymorphic relationships**
190
+
191
+ Consider the following polymorphic relation :
192
+
193
+ ```ruby
194
+ class Post
195
+ belongs_to :author, polymorphic: true
196
+ end
197
+
198
+ class AdminUser
199
+ has_many :posts, class_name: 'Post', as: :author
200
+ end
201
+
202
+ class NormalUser
203
+ has_many :posts, class_name: 'Post', as: :author
204
+ end
205
+ ```
206
+
207
+ In order to use the `author` polymorphic attribute in your `PostMapper` you need to declare the following :
208
+
209
+ ```ruby
210
+ class PostMapper < ActiveMappers::Base
211
+ polymorphic :author
212
+ end
213
+ ```
214
+
215
+ And of course, you must implement the associated mappers :
216
+
217
+ ```ruby
218
+ class AdminUserMapper
219
+ attributes :id, :name
220
+ end
221
+
222
+ class NormalUserMapper
223
+ attributes :id, :name
224
+ end
225
+ ```
226
+
227
+ Then, based of the `XXX_type` column, the mapper will automatically resolve to either `AdminUserMapper` or `NormalUserMapper`
228
+
229
+ **Rendering a collection of different classes**
230
+
231
+ Say you want to render many resources with a single Mapper
232
+
233
+ ```ruby
234
+ collection = Bird.all + Fish.all + Insect.all
235
+
236
+ render json: AnimalMapper.with(collection)
237
+
238
+ class AnimalMapper < ActiveMappers::Base
239
+ acts_as_polymorphic
240
+ end
241
+
242
+ class BirdMapper < ActiveMappers::Base
243
+ attributes :name, :wings_count
244
+ end
245
+
246
+ class FishMapper < ActiveMappers::Base
247
+ attributes :name, :fins_count
248
+ end
249
+
250
+ class InsectMapper < ActiveMappers::Base
251
+ attributes :name, :has_venom
252
+ end
253
+ ```
254
+
255
+ Will generate the following :
256
+
257
+ ```ruby
258
+ {
259
+ animals: [
260
+ { name: 'Michael', wings_count: 2 },
261
+ { name: 'Emeric', fins_count: 1 },
262
+ { name: 'Arthur', has_venom: true },
263
+ ]
264
+ }
265
+ ```
266
+
267
+ Again, just like the above polymorphic declaration, the mapper will automatically resolve to the corresponding one.
268
+
269
+ **Custom Attributes**
270
+
271
+ If you need to implement custom attributes you can always use the `each` statement.
272
+
273
+ ```ruby
274
+ class UserMapper < ActiveMappers::Base
275
+ attributes :email, :id
276
+
277
+ each do |user|
278
+ {
279
+ custom_attribute: "Hi, I'm a custom attribute",
280
+ another_custom_attribute: Time.now
281
+ }
282
+ end
283
+ end
284
+ ```
285
+
286
+ Will generate the following:
287
+
288
+ ```ruby
289
+ {
290
+ user: {
291
+ id: '12345',
292
+ email: 'mvilleneuve@snapp.fr',
293
+ custom_attribute: "Hi, I'm a custom attribute",
294
+ another_custom_attribute: "2018-09-26 17:49:59 +0200"
295
+ }
296
+ }
297
+ ```
298
+
299
+ You can declare any number of `each` in a single mapper.
300
+ Actually, `each` is used to implement every above features.
301
+
302
+ **Scope**
303
+
304
+ ActiveMappers does not yet support inheritance. However we provide an even better alternative named `scope`.
305
+
306
+ Whenever you feel the need to declare more or less attributes based on who called the mapper, you may want to consider using scope.
307
+
308
+ A very usual use case would be to have a different way to map a resource depending on wether you are an administrator or not.
309
+ Instead of declaring a whole new mapper just to add/remove attributes, you can do the following :
310
+
311
+ ```ruby
312
+ class UserMapper < ActiveMappers::Base
313
+ attributes :pseudo
314
+
315
+ scope :admin
316
+ attributes :id
317
+ end
318
+
319
+ scope :owner
320
+ attributes :email
321
+ end
322
+ end
323
+
324
+ # This declaration gives you 3 ways to call the mapper
325
+
326
+ # By an administrator
327
+ UserMapper.with(User.first, scope: :admin)
328
+ # => { pseudo: 'michael33', id: '1234' }
329
+
330
+ # By anyone
331
+ UserMapper.with(User.first)
332
+ # => { pseudo: 'michael33' }
333
+
334
+ # Or by the corresponding user that will gain access to personal informations
335
+ UserMapper.with(User.first, scope: :owner)
336
+ # => { pseudo: 'michael33', email: 'mvilleneuve@fidme.com' }
337
+ ```
338
+
339
+
340
+ ## Using a mapper
341
+
342
+ Even though there are many ways to declare a mapper, there is only one way to use it
343
+
344
+ ```ruby
345
+ UserMapper.with(User.first)
346
+
347
+ # Or use it with a collection
348
+ UserMapper.with(User.all)
349
+ ```
350
+
351
+ In a Rails controller :
352
+
353
+ ```ruby
354
+ def index
355
+ render json: UserMapper.with(User.all)
356
+ end
357
+ ```
358
+
359
+ ### JSON Root
360
+
361
+ You can choose to use ActiveMappers with or without a JSON root.
362
+
363
+ By default, root will be enabled, meaning a UserMapper, will generate a JSON prefixed by :
364
+
365
+ ```ruby
366
+ {
367
+ user: {}
368
+ }
369
+ ```
370
+
371
+ **Custom Root**
372
+
373
+ If you want to customize the root name, you can use
374
+
375
+ ```ruby
376
+ UserMapper.with(user, root: :hello)
377
+ ```
378
+
379
+ which will generate :
380
+
381
+ ```ruby
382
+ {
383
+ hello: {}
384
+ }
385
+ ```
386
+
387
+ **Rootless**
388
+
389
+ If you do not want to set any root, use :
390
+
391
+ ```ruby
392
+ UserMapper.with(user, rootless: true)
393
+ ```
394
+
395
+ ## Adding your own features to Active Mapper DSL
396
+
397
+ If you want to add specific features to the DSL you can reopen `::ActiveMappers::Base` class and add your own methods.
398
+ The most convenient way to do that is in your Active Mapper initializer following this pattern:
399
+
400
+ ```ruby
401
+ ActiveMappers::Setup.configure do |config|
402
+ ...
403
+ end
404
+
405
+ module ActiveMappers
406
+ class Base
407
+ include Rails.application.routes.url_helpers
408
+
409
+ def self.my_capitalize_dsl_feature(*params)
410
+ each do |resource| #your mapped resource(s)
411
+ h = {}
412
+ params.each do |param|
413
+ h[param] = resource.try(param)&.capitalize #your treatment
414
+ end
415
+ h #the returned hash will be merged to the mapper result.
416
+ end
417
+ end
418
+ end
419
+ end
420
+ ```
421
+ and then:
422
+
423
+ ```ruby
424
+ class UserMapper < ActiveMappers::Base
425
+ my_capitalize_dsl_feature :civility
426
+ end
427
+ ```
428
+
429
+ ## Anything is missing ?
430
+
431
+ File an issue
@@ -0,0 +1,9 @@
1
+
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'active_mappers'
3
+ s.version = '1.3.0'
4
+ s.date = '2019-05-10'
5
+ s.summary = 'Slick, fast view layer for you Rails API.'
6
+ s.description = 'Fast, simple, declarative way to design your API\'s view layer'
7
+ s.authors = ['Michaël Villeneuve']
8
+ s.homepage = 'https://github.com/fidme/active_mappers'
9
+ s.email = 'contact@michaelvilleneuve.fr'
10
+ s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
11
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
12
+ end
13
+
14
+ s.license = 'MIT'
15
+ s.add_runtime_dependency('activesupport', ['>= 4.2'])
16
+ s.add_runtime_dependency('method_source', ['~> 0.9.2'])
17
+ s.add_runtime_dependency('mocha', ['>= 1.8.0'])
18
+ s.add_runtime_dependency('ruby2ruby', ['> 2.4.0'])
19
+ s.add_runtime_dependency('ruby_parser', ['~> 3.1'])
20
+ end
@@ -1,14 +1,23 @@
1
+ require 'method_source'
2
+ require 'ruby2ruby'
3
+ require 'ruby_parser'
4
+
1
5
  require 'active_support'
2
6
  require 'active_support/core_ext/object/try'
7
+ require 'active_support/core_ext/hash/except'
3
8
  require 'active_support/core_ext/string/inflections'
4
9
  require_relative 'core_ext/hash'
10
+ require_relative 'active_mappers/handlers/inheritance'
5
11
  require_relative 'active_mappers/key_transformer'
6
12
 
13
+
7
14
  module ActiveMappers
8
15
  class Base
9
16
  @@renderers = {}
10
- @@initial_renderers = {}
11
- @@scopes = {}
17
+
18
+ def self.inherited(subclass)
19
+ Handlers::Inheritance.new(subclass, self).handle
20
+ end
12
21
 
13
22
  def self.attributes(*params)
14
23
  each do |resource|
@@ -66,36 +75,28 @@ module ActiveMappers
66
75
  end
67
76
 
68
77
  def self.with(args, options = {})
69
- evaluate_scopes(options[:scope])
70
-
78
+ return evaluate_scopes(args, options) unless options[:scope].nil?
79
+
71
80
  response = if options[:rootless]
72
81
  args.respond_to?(:each) ? all(args) : one(args)
73
82
  else
74
83
  render_with_root(args, options)
75
84
  end
76
85
  response
77
- ensure
78
- reset_renderers_before_scopes if !options[:scope].nil?
79
86
  end
80
87
 
81
- def self.evaluate_scopes(scope_name)
82
- return if scope_name.nil?
83
- @@initial_renderers[name] = @@initial_renderers[name] ? @@initial_renderers[name] : [] + (@@renderers[name] || [])
84
-
85
- found_scope = (@@scopes[name] || []).detect { |s| s[:name] === scope_name }
86
- raise "ActiveMappers [#{name}] Scope named #{scope_name} has not been declared or is not a block" if found_scope.nil? || found_scope[:lambda].nil? || !found_scope[:lambda].respond_to?(:call)
87
-
88
- found_scope[:lambda].call
88
+ def self.evaluate_scopes(args, options)
89
+ class_to_call = "::#{name}Scope#{options[:scope].capitalize}".constantize rescue raise("ActiveMappers [#{name}] No scope named #{options[:scope]} found")
90
+ return class_to_call.with(args, options.except(:scope))
89
91
  end
90
92
 
91
93
  def self.scope(*params, &block)
92
94
  raise "ActiveMappers [#{name}] scope must be a bloc" if block.nil? || !block.respond_to?(:call)
93
95
 
96
+
94
97
  params.each do |param|
95
- @@scopes[name] = (@@scopes[name] || []) << {
96
- name: param,
97
- lambda: block,
98
- }
98
+ block_content = Ruby2Ruby.new.process(RubyParser.new.process(block.source).to_a.last)
99
+ eval("class ::#{name}Scope#{param.capitalize} < ::#{name} ; #{block_content}; end")
99
100
  end
100
101
  end
101
102
 
@@ -123,10 +124,5 @@ module ActiveMappers
123
124
 
124
125
  KeyTransformer.format_keys(renderers)
125
126
  end
126
-
127
- def self.reset_renderers_before_scopes
128
- return if !@@initial_renderers || !@@initial_renderers[name]
129
- @@renderers[name] = @@initial_renderers[name].dup
130
- end
131
127
  end
132
128
  end
@@ -0,0 +1,26 @@
1
+ module ActiveMappers
2
+ module Handlers
3
+ class Inheritance
4
+ def initialize(subclass, klass)
5
+ @subclass = subclass
6
+ @klass = klass
7
+ end
8
+
9
+ def handle
10
+ return nil if regular_inheritance?
11
+
12
+ @klass.class_variables.each do |var_name|
13
+ dsl_values = @subclass.class_variable_get(var_name)
14
+
15
+ dsl_values[@subclass.name] = dsl_values[@klass.name].dup
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def regular_inheritance?
22
+ @klass == ActiveMappers::Base
23
+ end
24
+ end
25
+ end
26
+ end
@@ -32,6 +32,7 @@ module ActiveMappers
32
32
  Setup.ignored_namespaces.each do |namespace|
33
33
  @name.gsub!("#{namespace.to_s.capitalize}::", '')
34
34
  end
35
+ @name = @name.split('MapperScope')[0]
35
36
  self
36
37
  end
37
38
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_mappers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michaël Villeneuve
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: method_source
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.2
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: mocha
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,13 +52,48 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: 1.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby2ruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.4.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.4.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby_parser
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
41
83
  description: Fast, simple, declarative way to design your API's view layer
42
84
  email: contact@michaelvilleneuve.fr
43
85
  executables: []
44
86
  extensions: []
45
87
  extra_rdoc_files: []
46
88
  files:
89
+ - ".travis.yml"
90
+ - Gemfile
91
+ - Gemfile.lock
92
+ - README.md
93
+ - Rakefile
94
+ - active_mappers.gemspec
47
95
  - lib/active_mappers.rb
96
+ - lib/active_mappers/handlers/inheritance.rb
48
97
  - lib/active_mappers/key_transformer.rb
49
98
  - lib/active_mappers/setup.rb
50
99
  - lib/core_ext/hash.rb