encore 0.0.3 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
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