active_mappers 1.2.4 → 1.3.0

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