encore 0.0.3 → 0.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -16
  3. data/.rubocop.yml +53 -0
  4. data/.travis.yml +10 -2
  5. data/LICENSE.md +1 -1
  6. data/README.md +105 -153
  7. data/Rakefile +10 -0
  8. data/encore.gemspec +16 -13
  9. data/gemfiles/{Gemfile.activerecord-3.2.x → Gemfile.activerecord-4.1} +1 -1
  10. data/lib/encore/config.rb +2 -0
  11. data/lib/encore/persister/errors_parser.rb +17 -0
  12. data/lib/encore/persister/instance.rb +85 -0
  13. data/lib/encore/persister/key_mapping.rb +15 -0
  14. data/lib/encore/persister/links_parser.rb +57 -0
  15. data/lib/encore/persister/param_injection.rb +15 -0
  16. data/lib/encore/serializer/base.rb +48 -0
  17. data/lib/encore/serializer/eager_loading_manager.rb +11 -0
  18. data/lib/encore/serializer/instance.rb +56 -0
  19. data/lib/encore/serializer/linked_resource_manager.rb +19 -0
  20. data/lib/encore/serializer/links_reflection_includer.rb +50 -0
  21. data/lib/encore/serializer/main_resource_links_manager/reflection_belongs_to.rb +13 -0
  22. data/lib/encore/serializer/main_resource_links_manager/reflection_has_many.rb +13 -0
  23. data/lib/encore/serializer/main_resource_links_manager/reflection_has_one.rb +13 -0
  24. data/lib/encore/serializer/main_resource_links_manager.rb +45 -0
  25. data/lib/encore/serializer/main_resource_manager.rb +13 -0
  26. data/lib/encore/serializer/meta_manager.rb +29 -0
  27. data/lib/encore/serializer/options_parser.rb +51 -0
  28. data/lib/encore/serializer/utils.rb +12 -0
  29. data/lib/encore/version.rb +1 -1
  30. data/lib/encore.rb +16 -8
  31. data/spec/encore/persister/belongs_to_links_spec.rb +46 -0
  32. data/spec/encore/persister/create_resources_spec.rb +55 -0
  33. data/spec/encore/persister/errors_resources_spec.rb +55 -0
  34. data/spec/encore/persister/has_many_links_spec.rb +49 -0
  35. data/spec/encore/persister/has_one_links_spec.rb +49 -0
  36. data/spec/encore/persister/key_mappings_spec.rb +66 -0
  37. data/spec/encore/persister/param_injection_spec.rb +46 -0
  38. data/spec/encore/persister/update_resources_spec.rb +37 -0
  39. data/spec/encore/serializer/linked/always_include_resources_spec.rb +85 -0
  40. data/spec/encore/serializer/linked/can_include_resources_spec.rb +69 -0
  41. data/spec/encore/serializer/linked/linked_resources_spec.rb +103 -0
  42. data/spec/encore/serializer/links_resource_spec.rb +294 -0
  43. data/spec/encore/serializer/main_meta_spec.rb +66 -0
  44. data/spec/encore/serializer/main_resource_spec.rb +37 -0
  45. data/spec/encore/serializer/no_paging_spec.rb +40 -0
  46. data/spec/encore_spec.rb +4 -0
  47. data/spec/spec_helper.rb +8 -21
  48. data/spec/support/macros/database/database_adapter.rb +9 -0
  49. data/spec/support/macros/database/sqlite3_adapter.rb +14 -0
  50. data/spec/support/macros/database_macros.rb +5 -16
  51. data/spec/support/macros/model_macros.rb +7 -22
  52. metadata +143 -72
  53. data/lib/encore/association/has_many_association.rb +0 -12
  54. data/lib/encore/association/has_one_association.rb +0 -12
  55. data/lib/encore/association.rb +0 -39
  56. data/lib/encore/attribute.rb +0 -42
  57. data/lib/encore/entity/input/associations.rb +0 -27
  58. data/lib/encore/entity/input/errors.rb +0 -8
  59. data/lib/encore/entity/input/exposed_attributes.rb +0 -37
  60. data/lib/encore/entity/input.rb +0 -73
  61. data/lib/encore/entity/output/json.rb +0 -14
  62. data/lib/encore/entity/output.rb +0 -13
  63. data/lib/encore/entity.rb +0 -44
  64. data/lib/encore/helpers/controller_helper.rb +0 -37
  65. data/lib/encore/helpers.rb +0 -1
  66. data/lib/encore/railtie.rb +0 -11
  67. data/spec/encore/entity/input/associations_spec.rb +0 -48
  68. data/spec/encore/entity/input/exposed_attributes_spec.rb +0 -46
  69. data/spec/encore/entity/input_spec.rb +0 -104
  70. data/spec/encore/entity/output/json_spec.rb +0 -76
  71. data/spec/encore/entity/output_spec.rb +0 -5
  72. data/spec/encore/entity_spec.rb +0 -31
  73. data/spec/encore/helpers/controller_helper_spec.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c05497c6b6640c47a20eafba79c3eb344cc523a6
4
- data.tar.gz: 1e727f1662fdf0927f940e0a4d46fa5054073d88
3
+ metadata.gz: 58e8c5a2cb80f79ec4fdf6afec91a6d67dd1108c
4
+ data.tar.gz: f45565ef1a3ae7732237728783146b8a008d4f8e
5
5
  SHA512:
6
- metadata.gz: 2b0c3d9175e9665cb709d725cf48873f2c16a4b9fb4100af1dafc07f087a9a9a075fbb8492822624a186386f4b5a0e7f25c560fdfd3b02dee4efd43e89820157
7
- data.tar.gz: 46d9c310cdaed3ea98051dea98f3a463137468f512abadfd62019553e74dd4c35e0c88d9531f14d47c289b358b516d82ed2e208b6f1059ba87fb3d1a5796c7f2
6
+ metadata.gz: cd79df1408e83a0792e7723c47fd0d7466140492ca7f10ce36e95902c3e42b992472aa96e27021e07ed9724290ab828983055e9119f79cf7b8caf4be49e2ef79
7
+ data.tar.gz: 44b77c571699964d6b3c0b5b6593d660a9e62eac44bc2d9de8e3be72315a57a7fb264a39fd83fc94151171493e53d06be94271241254049c8a5cc68e85441a7a
data/.gitignore CHANGED
@@ -1,17 +1 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
1
  Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/.rubocop.yml ADDED
@@ -0,0 +1,53 @@
1
+ AllCops:
2
+ Include:
3
+ - encore.gemspec
4
+
5
+ Documentation:
6
+ Enabled: false
7
+
8
+ Encoding:
9
+ Enabled: false
10
+
11
+ LineLength:
12
+ Max: 200
13
+
14
+ ClassLength:
15
+ Max: 200
16
+
17
+ AccessModifierIndentation:
18
+ EnforcedStyle: outdent
19
+
20
+ IfUnlessModifier:
21
+ Enabled: false
22
+
23
+ CaseIndentation:
24
+ IndentWhenRelativeTo: case
25
+ IndentOneStep: true
26
+
27
+ MethodLength:
28
+ CountComments: false
29
+ Max: 20
30
+
31
+ SignalException:
32
+ Enabled: false
33
+
34
+ ColonMethodCall:
35
+ Enabled: false
36
+
37
+ AsciiComments:
38
+ Enabled: false
39
+
40
+ Lambda:
41
+ Enabled: false
42
+
43
+ RegexpLiteral:
44
+ Enabled: false
45
+
46
+ AssignmentInCondition:
47
+ Enabled: false
48
+
49
+ ClassAndModuleChildren:
50
+ Enabled: false
51
+
52
+ GuardClause:
53
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,11 +1,19 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
+ - 2.1.1
4
5
  - 2.0.0
5
- - 1.9.3
6
6
 
7
7
  gemfile:
8
8
  - gemfiles/Gemfile.activerecord-4.0
9
- - gemfiles/Gemfile.activerecord-3.2.x
9
+ - gemfiles/Gemfile.activerecord-4.1
10
10
 
11
11
  script: "echo 'DO IT' && bundle exec rake spec"
12
+
13
+ notifications:
14
+ hipchat:
15
+ rooms:
16
+ secure: "lLUK7HuJzZiae6dSB35I/ZlTI8guiRteImGQBhj1DKe533HPGZQHnhwAdML2YY6nLzhfw1uaXHNgGDCLTkylHK2ChLROHZicoX6mCLPqspwkB2GCx4K+1neltReeOIIi5ZIg2fz6wAV9rdSSaIBpIUg1717nhbOCP3ouIf0j3X0="
17
+ template:
18
+ - '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} (<a href="%{build_url}">Build</a>/<a href="%{compare_url}">Changes</a>)'
19
+ format: 'html'
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013, Mirego
1
+ Copyright (c) 2013-2014, Mirego
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
data/README.md CHANGED
@@ -3,15 +3,10 @@
3
3
  <img src="http://i.imgur.com/erXBozp.png" alt="Encore" />
4
4
  </a>
5
5
  <br />
6
- Encore is a Ruby framework to help dealing with entities.
6
+ Encore provides serializers and persisters to build <a href="http://jsonapi.org">JSON API</a>-compliant Web services with Ruby on Rails.
7
7
  <br /><br />
8
- <a href="https://rubygems.org/gems/encore"><img src="https://badge.fury.io/rb/encore.png" /></a>
9
- <a href="https://codeclimate.com/github/mirego/encore"><img src="https://codeclimate.com/github/mirego/encore.png" /></a>
10
- <a href="https://travis-ci.org/mirego/encore"><img src="https://travis-ci.org/mirego/encore.png?branch=master" /></a>
11
8
  </p>
12
9
 
13
- ---
14
-
15
10
  ## Installation
16
11
 
17
12
  Add this line to your application's Gemfile:
@@ -32,196 +27,153 @@ Or install it yourself as:
32
27
  $ gem install encore
33
28
  ```
34
29
 
35
- ## Usage
36
-
37
- Let’s say we have the `UserEntity` entity class bound to the `User` model:
30
+ ## Disclaimer
38
31
 
39
- ```ruby
40
- class User < ActiveRecord::Base
41
- end
32
+ Encore is under heavy development at the moment, do not use this gem in production unless you know exactly what you're doing. **Breaking changes** are still being committed.
42
33
 
43
- class UserEntity
44
- include Encore::Entity
45
-
46
- expose :name
47
- expose :email
48
- expose :created_at, readonly: true
49
- expose :updated_at, readonly: true
50
- end
51
- ```
34
+ ## Basic serializer usage
52
35
 
53
- We can create a new `User` resource with attributes:
36
+ ### Configure your serializer
54
37
 
55
38
  ```ruby
56
- @entity = UserEntity.new
57
- @entity.assign_attributes email: 'remi@example.com', name: 'Rémi Prévost'
58
- @entity.save
59
- ```
60
-
61
- When assigning attributes to an entity, we can pass either an `:update` or a `:partial_update` context.
62
-
63
- With the (default) `:partial_update` context, Encore will assign new attributes and ignore the other exposed attributes. This makes sense in a `PATCH` HTTP request context.
64
-
65
- ```ruby
66
- new_attributes = { email: 'remi+new@example.com' }
67
- @entity.assign_attributes new_attributes
68
- @entity.object.email # => "remi+new@example.com"
69
- @entity.object.name # => "Rémi Prévost"
70
- ```
71
-
72
- However, with the `:update` context, Encore will assign new attributes and set all non-provided exposed attributes to `nil`. This
73
- makes sense in a `PUT` HTTP request context.
74
-
75
- ```ruby
76
- new_attributes = { email: 'remi+new-again@example.com' }
77
- @entity.assign_attributes new_attributes, context: :update
78
- @entity.object.email # => "remi+new-again@example.com"
79
- @entity.object.name # => nil
80
- ```
81
-
82
- If we try to assign a value to a non-exposed or readonly attribute, Encore will raise an error.
83
-
84
- ```ruby
85
- @entity.assign_attributes email: 'remi+new@example.com', created_at: Time.now
86
- # => raises #<Encore::Entity::Input::InvalidAttributeError: The #<Encore::Attribute UserEntity@created_at> attribute is not exposed.>
39
+ class CommentSerializer < Encore::Serializer::Base
40
+ attributes :id, :body, :links
41
+
42
+ # By default, root_key will be the pluralized model
43
+ # name. If you want to set a custom root_key, you can
44
+ # do that:
45
+ def self.root_key
46
+ :user_comments
47
+ end
48
+ end
87
49
  ```
88
50
 
89
- ### Associations
51
+ The `links` attribute is generated by Encore and will return the included associations. Read more in the [Inclusion](#inclusion) section.
90
52
 
91
- You can also expose associations.
53
+ ### Returning serialized model
92
54
 
93
55
  ```ruby
94
- class User < ActiveRecord::Base
95
- belongs_to :organization
96
- end
56
+ class CommentsController < ApplicationController
57
+ before_action :fetch_comments, only: %i(index)
97
58
 
98
- class Organization < ActiveRecord::Base
99
- has_many :users
100
- end
59
+ def index
60
+ render json: Encore::Serializer::Instance.new(@comments)
61
+ end
101
62
 
102
- class UserEntity
103
- include Encore::Entity
63
+ protected
104
64
 
105
- expose :name
106
- expose_one :organization
65
+ def fetch_comments
66
+ @comments = Comment.all
67
+ end
107
68
  end
108
69
  ```
109
70
 
110
- Assigning new value for associations doesn’t save them right away.
111
-
112
- ```ruby
113
- @user = User.first # => #<User id=1 organization_id=1>
114
- @entity = UserEntity.new(@user)
115
- @entity.assign_attributes organization: 2
116
- @entity.object.organization_id # => 1
71
+ Will result in the following JSON output:
72
+
73
+ ```json
74
+ {
75
+ "comments": [
76
+ {
77
+ "id": "1",
78
+ "body": "First!",
79
+ "links": {}
80
+ }
81
+ ],
82
+ "links": {},
83
+ "linked": {},
84
+ "meta": {
85
+ "comments": {
86
+ "page": 1,
87
+ "count": 1,
88
+ "page_count": 1,
89
+ "previous_page": null,
90
+ "next_page": null
91
+ }
92
+ }
117
93
  ```
118
94
 
119
- Calling `save` on the entity saves them.
120
-
121
- ```ruby
122
- @entity.save
123
- @entity.object.organization_id # => 2
124
- ```
95
+ ### Inclusion
125
96
 
126
- ## Typical setup with Ruby on Rails
127
-
128
- _This is work-in-progress. There’s still missing stuff._
129
-
130
- ### Model
97
+ Encore can handle model associations. For example, let's include the `author` in the code sample above.
131
98
 
132
99
  ```ruby
133
- # app/models/user.rb
134
- class User < ActiveRecord::Base
135
- end
100
+ Encore::Serializer::Instance.new(@comments, include: 'author')
136
101
  ```
137
102
 
138
- ### Entity
103
+ Since we don't want all associations to be exposed, we also need to allow the serializer to include the association. To do so, we need to update the `CommentSerializer`.
139
104
 
140
105
  ```ruby
141
- # app/entities/user_entity.rb
142
- class UserEntity
143
- include Encore::Entity
144
-
145
- expose :name
146
- expose :email
147
- expose :created_at, readonly: true
148
- expose :updated_at, readonly: true
149
- end
150
- ```
151
-
152
- ### Routes
106
+ class CommentSerializer < Encore::Serializer::Base
107
+ # ...
153
108
 
154
- ```ruby
155
- # config/routes.rb
156
- Rails::Application.routes.draw do
157
- resources :users do
158
- # This makes Rails route PUT and PATCH requests to two separate actions
159
- patch on: :member, action: :partial_update
109
+ def self.can_include
110
+ [:author]
160
111
  end
161
112
  end
162
113
  ```
163
114
 
164
- ### Controller
165
-
166
- ```ruby
167
- # app/controllers/users_controller.rb
168
- class UsersController < ApplicationController
169
- before_action :fetch_user, only: [:update, :partial_update]
170
-
171
- # POST /users
172
- def create
173
- @entity = UserEntity.new
174
- @entity.assign_attributes(params[:user], context: :create)
175
-
176
- process! @entity
177
-
178
- # Here, `process!` is a shortcut for:
179
- #
180
- # if @entity.save
181
- # render json: @entity, status: 201
182
- # else
183
- # render json: { errors: @entity.errors }, status: 422
184
- # end
185
- end
186
-
187
- # PUT /users/:id
188
- def update
189
- @entity = UserEntity.new(@user)
190
- @entity.assign_attributes(params[:user], context: :update)
191
-
192
- process! @entity
193
- end
194
-
195
- # PATCH /users/:id
196
- def partial_update
197
- @entity = UserEntity.new(@user)
198
- @entity.assign_attributes(params[:user], context: :partial_update)
115
+ This will result in the following JSON output:
116
+
117
+ ```json
118
+ {
119
+ "comments": [
120
+ {
121
+ "id": "1",
122
+ "body": "First!",
123
+ "links": {
124
+ "author": "1"
125
+ }
126
+ }
127
+ ],
128
+ "links": {
129
+ "comments.author": {
130
+ "href": "/authors?ids={author.id}",
131
+ "type": "user"
132
+ }
133
+ },
134
+ "linked": {
135
+ "authors": [
136
+ {
137
+ "id": "1",
138
+ "name": "John Doe"
139
+ }
140
+ ]
141
+ },
142
+ "meta": {
143
+ "comments": {
144
+ "page": 1,
145
+ "count": 1,
146
+ "page_count": 1,
147
+ "previous_page": null,
148
+ "next_page": null
149
+ }
150
+ }
151
+ ```
199
152
 
200
- process! @entity
201
- end
153
+ If you want the `author` the **always** be included when you request a `comment`, update the `CommentSerializer` this way:
202
154
 
203
- protected
155
+ ```ruby
156
+ class CommentSerializer < Encore::Serializer::Base
157
+ attributes :id, :body, :links
204
158
 
205
- def fetch_user
206
- @user = User.find(params[:id])
159
+ def self.always_include
160
+ [:author]
207
161
  end
208
162
  end
209
163
  ```
210
164
 
211
- ## Todo
165
+ ### Pagination
212
166
 
213
- Please keep in mind that this gem is far from finished and totally not ready to use in production.
214
- This is something we’ve been wanting to build for a long time and now we’re finally taking the time
215
- do it right.
167
+ Lot more work to do here :)
216
168
 
217
169
  ## License
218
170
 
219
- `Encore` is © 2013 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/encore/blob/master/LICENSE.md) file.
171
+ `Encore` is © 2013-2014 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/encore/blob/master/LICENSE.md) file.
220
172
 
221
- The nut logo is based on [this lovely icon](http://thenounproject.com/noun/hazelnuts/#icon-No3618) by [Alessandro Suraci](http://thenounproject.com/alessandro.suraci/), from The Noun Project. Used under a [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/) license.
173
+ The hazelnut logo is based on [this lovely icon](http://thenounproject.com/term/hazelnuts/3618/) by [Alessandro Suraci](http://thenounproject.com/alessandro.suraci/), from The Noun Project. Used under a [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/) license.
222
174
 
223
175
  ## About Mirego
224
176
 
225
- Mirego is a team of passionate people who believe that work is a place where you can innovate and have fun. We proudly build mobile applications for [iPhone](http://mirego.com/en/iphone-app-development/ "iPhone application development"), [iPad](http://mirego.com/en/ipad-app-development/ "iPad application development"), [Android](http://mirego.com/en/android-app-development/ "Android application development"), [Blackberry](http://mirego.com/en/blackberry-app-development/ "Blackberry application development"), [Windows Phone](http://mirego.com/en/windows-phone-app-development/ "Windows Phone application development") and [Windows 8](http://mirego.com/en/windows-8-app-development/ "Windows 8 application development") in beautiful Quebec City.
177
+ [Mirego](http://mirego.com) is a team of passionate people who believe that work is a place where you can innovate and have fun. We're a team of [talented people](http://life.mirego.com) who imagine and build beautiful Web and mobile applications. We come together to share ideas and [change the world](http://mirego.org).
226
178
 
227
- We also love [open-source software](http://open.mirego.com/) and we try to extract as much code as possible from our projects to give back to the community.
179
+ We also [love open-source software](http://open.mirego.com) and we try to give back to the community as much as we can.
data/Rakefile CHANGED
@@ -9,3 +9,13 @@ desc 'Run all specs'
9
9
  RSpec::Core::RakeTask.new(:spec) do |task|
10
10
  task.pattern = 'spec/**/*_spec.rb'
11
11
  end
12
+
13
+ desc 'Start an IRB session with the gem'
14
+ task :console do
15
+ $:.unshift File.expand_path('..', __FILE__)
16
+ require 'encore'
17
+ require 'irb'
18
+
19
+ ARGV.clear
20
+ IRB.start
21
+ end
data/encore.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'encore/version'
@@ -6,23 +7,25 @@ require 'encore/version'
6
7
  Gem::Specification.new do |spec|
7
8
  spec.name = 'encore'
8
9
  spec.version = Encore::VERSION
9
- spec.authors = ['Rémi Prévost']
10
- spec.email = ['rprevost@mirego.com']
11
- spec.description = 'Encore is a Ruby framework to help dealing with entities.'
10
+ spec.authors = ['Simon Prévost']
11
+ spec.email = ['sprevost@mirego.com']
12
+ spec.description = 'Encore provides serializers and persisters to build JSON API-compliant Web services with Ruby on Rails.'
12
13
  spec.summary = spec.description
13
14
  spec.homepage = 'https://github.com/mirego/encore'
14
- spec.license = "BSD 3-Clause"
15
+ spec.license = 'BSD 3-Clause'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
19
18
  spec.require_paths = ['lib']
20
19
 
21
- spec.add_development_dependency 'bundler'
22
- spec.add_development_dependency 'rake'
23
- spec.add_development_dependency 'rspec'
24
- spec.add_development_dependency 'sqlite3'
25
- spec.add_development_dependency 'rails', '>= 3.0.0'
20
+ spec.add_dependency 'activemodel', '>= 4.0.0'
21
+ spec.add_dependency 'activerecord', '>= 4.0.0'
22
+ spec.add_dependency 'active_model_serializers', '~> 0.8.0'
23
+ spec.add_dependency 'kaminari', '~> 0.15.1'
26
24
 
27
- spec.add_dependency 'activesupport', '>= 3.0.0'
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake', '~> 10.3'
27
+ spec.add_development_dependency 'rspec', '3.0.0beta2'
28
+ spec.add_development_dependency 'sqlite3', '>= 1.3.8', '< 1.4'
29
+ spec.add_development_dependency 'rubocop', '0.22'
30
+ spec.add_development_dependency 'phare'
28
31
  end
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec path: '../'
4
4
 
5
- gem 'activerecord', '~> 3.2.0'
5
+ gem 'activerecord', '~> 4.1.0'
@@ -0,0 +1,2 @@
1
+ ActiveModel::Serializer.root = false
2
+ Kaminari::Hooks.init
@@ -0,0 +1,17 @@
1
+ module Encore
2
+ module Persister
3
+ module ErrorsParser
4
+ extend ActiveSupport::Concern
5
+
6
+ def parse_errors(record, index)
7
+ record.errors.messages.map do |field, values|
8
+ {
9
+ field: field.to_s,
10
+ types: values,
11
+ path: "#{record.class.name.underscore}/#{index}/#{field}"
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ require 'encore/persister/param_injection'
2
+ require 'encore/persister/errors_parser'
3
+ require 'encore/persister/key_mapping'
4
+ require 'encore/persister/links_parser'
5
+
6
+ module Encore
7
+ module Persister
8
+ class Instance
9
+ include LinksParser
10
+ include ErrorsParser
11
+
12
+ attr_reader :errors
13
+
14
+ def initialize(model, payload, options = {})
15
+ @model = model
16
+ @payload = payload
17
+ @options = options
18
+ @errors = []
19
+ @ids = Set.new
20
+ end
21
+
22
+ def persist!
23
+ @model.transaction do
24
+ procces_payload!(action)
25
+
26
+ if @errors.any? || @ids.empty?
27
+ raise ActiveRecord::Rollback
28
+ end
29
+
30
+ true
31
+ end
32
+ end
33
+
34
+ def active_records
35
+ active_record_class.where(id: @ids.to_a.compact)
36
+ end
37
+
38
+ private
39
+
40
+ def procces_payload!(action)
41
+ payload = key_mapping(@payload)
42
+ payload = param_injection(payload)
43
+
44
+ payload.each_with_index do |args, i|
45
+ args = parse_links(args)
46
+ record = send("#{action}_record", args)
47
+
48
+ next unless record.present?
49
+
50
+ @ids += fetch_id(record)
51
+ @errors += parse_errors(record, i)
52
+ end
53
+ end
54
+
55
+ def active_record_class
56
+ @model.is_a?(Class) ? @model : @model.class
57
+ end
58
+
59
+ def action
60
+ @model.is_a?(Class) ? :create : :update
61
+ end
62
+
63
+ def create_record(args)
64
+ @model.create args
65
+ end
66
+
67
+ def update_record(args)
68
+ @model.update_attributes(args)
69
+ @model
70
+ end
71
+
72
+ def fetch_id(record)
73
+ [record.id]
74
+ end
75
+
76
+ def key_mapping(payload)
77
+ @model.active_model_serializer ? KeyMapping.map_keys(payload, @model.active_model_serializer) : payload
78
+ end
79
+
80
+ def param_injection(payload)
81
+ ParamInjection.inject(payload, @options[:inject_params])
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,15 @@
1
+ module Encore
2
+ module Persister
3
+ module KeyMapping
4
+ def self.map_keys(array_params, serializer)
5
+ array_params.map do |params|
6
+ mappings = serializer.key_mappings
7
+
8
+ params.keys.each { |k| params[mappings[k]] = params.delete(k) if mappings[k] }
9
+
10
+ params
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end