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
@@ -0,0 +1,294 @@
1
+ require 'spec_helper'
2
+
3
+ describe Encore::Serializer do
4
+ let(:serializer) { Encore::Serializer::Instance }
5
+ let(:objects) { User.all }
6
+ let(:serialized) { serializer.new(objects, include: include).as_json }
7
+
8
+ let(:run_migrations!) do
9
+ run_migration do
10
+ create_table(:users, force: true) do |t|
11
+ t.string :name, default: nil
12
+ t.integer :project_id
13
+ end
14
+ create_table(:projects, force: true) do |t|
15
+ t.string :name, default: nil
16
+ t.integer :user_id
17
+ end
18
+ end
19
+ end
20
+
21
+ before do
22
+ run_migrations!
23
+ spawn_objects!
24
+ create_records!
25
+ end
26
+
27
+ context 'can access' do
28
+ context 'default' do
29
+ let(:spawn_objects!) do
30
+ spawn_model('User') do
31
+ belongs_to :project
32
+ belongs_to :irrelevant_reflection
33
+ end
34
+ spawn_serializer('UserSerializer') do
35
+ attributes :name, :links
36
+
37
+ def can_include
38
+ %i(project)
39
+ end
40
+ end
41
+ spawn_model('Project') do
42
+ has_many :users
43
+ end
44
+ spawn_serializer('ProjectSerializer') do
45
+ attributes :name
46
+ end
47
+
48
+ end
49
+ let(:create_records!) do
50
+ User.create name: 'Allan', project_id: 1
51
+ User.create name: 'Doe', project_id: 2
52
+ end
53
+ let(:include) { '' }
54
+
55
+ it { expect(serialized[:users][0][:links][:irrelevant_reflection]).to eq(nil) }
56
+ end
57
+
58
+ context 'not loaded with access' do
59
+ let(:spawn_objects!) do
60
+ spawn_model('User') do
61
+ belongs_to :project
62
+ belongs_to :irrelevant_reflection
63
+ end
64
+ spawn_serializer('UserSerializer') do
65
+ attributes :name, :links
66
+
67
+ def can_include
68
+ %i(project)
69
+ end
70
+ end
71
+ spawn_model('Project') do
72
+ has_many :users
73
+ end
74
+ spawn_serializer('ProjectSerializer') do
75
+ attributes :name
76
+ end
77
+
78
+ end
79
+ let(:create_records!) do
80
+ User.create name: 'Allan', project_id: 1
81
+ User.create name: 'Doe', project_id: 2
82
+ end
83
+ let(:include) { '' }
84
+
85
+ it { expect(serialized[:users][0][:links][:project].present?).to eq(true) }
86
+ end
87
+
88
+ context 'not loaded without access' do
89
+ let(:spawn_objects!) do
90
+ spawn_model('User') do
91
+ belongs_to :project
92
+ belongs_to :irrelevant_reflection
93
+ end
94
+ spawn_serializer('UserSerializer') do
95
+ attributes :name, :links
96
+
97
+ def can_include
98
+ %i(project)
99
+ end
100
+
101
+ def can_access
102
+ []
103
+ end
104
+ end
105
+ spawn_model('Project') do
106
+ has_many :users
107
+ end
108
+ spawn_serializer('ProjectSerializer') do
109
+ attributes :name
110
+ end
111
+
112
+ end
113
+ let(:create_records!) do
114
+ User.create name: 'Allan', project_id: 1
115
+ User.create name: 'Doe', project_id: 2
116
+ end
117
+ let(:include) { '' }
118
+
119
+ it { expect(serialized[:users][0][:links][:project].present?).to eq(false) }
120
+ end
121
+
122
+ context 'loaded without access' do
123
+ let(:spawn_objects!) do
124
+ spawn_model('User') do
125
+ belongs_to :project
126
+ belongs_to :irrelevant_reflection
127
+ end
128
+ spawn_serializer('UserSerializer') do
129
+ attributes :name, :links
130
+
131
+ def can_include
132
+ %i(project)
133
+ end
134
+
135
+ def can_access
136
+ []
137
+ end
138
+ end
139
+ spawn_model('Project') do
140
+ has_many :users
141
+ end
142
+ spawn_serializer('ProjectSerializer') do
143
+ attributes :name
144
+ end
145
+
146
+ end
147
+ let(:create_records!) do
148
+ User.create name: 'Allan', project_id: 1
149
+ User.create name: 'Doe', project_id: 2
150
+ end
151
+ let(:include) { 'project' }
152
+
153
+ it { expect(serialized[:users][0][:links][:project].present?).to eq(true) }
154
+ end
155
+ end
156
+
157
+ context 'belongs_to' do
158
+ let(:spawn_objects!) do
159
+ spawn_model('User') do
160
+ belongs_to :project
161
+ end
162
+ spawn_serializer('UserSerializer') do
163
+ attributes :name, :links
164
+
165
+ def can_include
166
+ %i(project)
167
+ end
168
+ end
169
+ spawn_model('Project') do
170
+ has_many :users
171
+ end
172
+ spawn_serializer('ProjectSerializer') do
173
+ attributes :name
174
+ end
175
+
176
+ end
177
+ let(:create_records!) do
178
+ User.create name: 'Allan', project_id: 1
179
+ User.create name: 'Doe', project_id: 2
180
+ end
181
+
182
+ context 'not included' do
183
+ let(:include) { '' }
184
+ let(:expected_project) do
185
+ {
186
+ href: '/projects/1',
187
+ id: '1',
188
+ type: 'projects'
189
+ }
190
+ end
191
+
192
+ it { expect(serialized[:users][0][:links][:project]).to eq(expected_project) }
193
+ end
194
+
195
+ context 'included' do
196
+ let(:include) { 'project' }
197
+ let(:expected_project) { '1' }
198
+
199
+ it { expect(serialized[:users][0][:links][:project]).to eq(expected_project) }
200
+ end
201
+ end
202
+
203
+ context 'has_many' do
204
+ let(:spawn_objects!) do
205
+ spawn_model('User') do
206
+ has_many :projects
207
+ end
208
+ spawn_serializer('UserSerializer') do
209
+ attributes :name, :links
210
+
211
+ def can_include
212
+ %i(projects)
213
+ end
214
+ end
215
+ spawn_model('Project') do
216
+ belongs_to :user
217
+ end
218
+ spawn_serializer('ProjectSerializer') do
219
+ attributes :name
220
+ end
221
+ end
222
+
223
+ let(:create_records!) do
224
+ User.create name: 'Allan'
225
+ User.create name: 'Doe'
226
+ Project.create name: 'p1', user_id: 1
227
+ Project.create name: 'p2', user_id: 1
228
+ end
229
+
230
+ context 'not included' do
231
+ let(:include) { '' }
232
+ let(:expected_project) do
233
+ {
234
+ href: '/projects?user_id=1',
235
+ type: 'projects'
236
+ }
237
+ end
238
+
239
+ it { expect(serialized[:users][0][:links][:projects]).to eq(expected_project) }
240
+ end
241
+
242
+ context 'included' do
243
+ let(:include) { 'projects' }
244
+ let(:expected_project) { %w(1 2) }
245
+
246
+ it { expect(serialized[:users][0][:links][:projects]).to eq(expected_project) }
247
+ end
248
+ end
249
+
250
+ context 'has_one' do
251
+ let(:spawn_objects!) do
252
+ spawn_model('User') do
253
+ has_one :project
254
+ end
255
+ spawn_serializer('UserSerializer') do
256
+ attributes :name, :links
257
+
258
+ def can_include
259
+ %i(project)
260
+ end
261
+ end
262
+ spawn_model('Project')
263
+ spawn_serializer('ProjectSerializer') do
264
+ attributes :name
265
+ end
266
+ end
267
+
268
+ let(:create_records!) do
269
+ User.create name: 'Allan'
270
+ User.create name: 'Doe'
271
+ Project.create name: 'p1', user_id: 1
272
+ Project.create name: 'p2', user_id: 2
273
+ end
274
+
275
+ context 'not included' do
276
+ let(:include) { '' }
277
+ let(:expected_project) do
278
+ {
279
+ href: '/users/1/project',
280
+ type: 'projects'
281
+ }
282
+ end
283
+
284
+ it { expect(serialized[:users][0][:links][:project]).to eq(expected_project) }
285
+ end
286
+
287
+ context 'included' do
288
+ let(:include) { 'project' }
289
+ let(:expected_project) { '1' }
290
+
291
+ it { expect(serialized[:users][0][:links][:project]).to eq(expected_project) }
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Encore::Serializer do
4
+ let(:serializer) { Encore::Serializer::Instance }
5
+ let(:objects) { User.all }
6
+ let(:serialized) { serializer.new(objects, page: page).as_json }
7
+
8
+ let(:run_migrations!) do
9
+ run_migration do
10
+ create_table(:users, force: true) do |t|
11
+ t.string :name, default: nil
12
+ t.string :discarded_attribute, default: nil
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:spawn_objects!) do
18
+ spawn_model('User')
19
+ spawn_serializer('UserSerializer') do
20
+ attributes :name
21
+ end
22
+ end
23
+
24
+ let(:create_records!) do
25
+ User.create name: 'Allan'
26
+ User.create name: 'Doe'
27
+ User.create name: 'Ding'
28
+ User.create name: 'Bob'
29
+ end
30
+
31
+ let(:paging_config) do
32
+ double(
33
+ 'Kaminari',
34
+ page_method_name: 'page',
35
+ default_per_page: 1,
36
+ max_per_page: 1,
37
+ max_pages: 10
38
+ )
39
+ end
40
+
41
+ before do
42
+ expect(Kaminari).to receive(:config).and_return(paging_config).at_least(:once)
43
+ run_migrations!
44
+ spawn_objects!
45
+ create_records!
46
+ end
47
+
48
+ context 'page 1' do
49
+ let(:page) { 1 }
50
+
51
+ it { expect(serialized[:meta][:users][:page]).to eq(1) }
52
+ it { expect(serialized[:meta][:users][:count]).to eq(4) }
53
+ it { expect(serialized[:meta][:users][:page_count]).to eq(4) }
54
+ it { expect(serialized[:meta][:users][:next_page]).to eq(2) }
55
+ end
56
+
57
+ context 'page 2' do
58
+ let(:page) { 2 }
59
+
60
+ it { expect(serialized[:meta][:users][:page]).to eq(2) }
61
+ it { expect(serialized[:meta][:users][:count]).to eq(4) }
62
+ it { expect(serialized[:meta][:users][:page_count]).to eq(4) }
63
+ it { expect(serialized[:meta][:users][:previous_page]).to eq(1) }
64
+ it { expect(serialized[:meta][:users][:next_page]).to eq(3) }
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Encore::Serializer do
4
+ let(:serializer) { Encore::Serializer::Instance }
5
+ let(:objects) { User.all }
6
+ let(:serialized) { serializer.new(objects).as_json }
7
+
8
+ let(:run_migrations!) do
9
+ run_migration do
10
+ create_table(:users, force: true) do |t|
11
+ t.string :name, default: nil
12
+ t.string :discarded_attribute, default: nil
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:spawn_objects!) do
18
+ spawn_model('User')
19
+ spawn_serializer('UserSerializer') do
20
+ attributes :name
21
+ end
22
+ end
23
+
24
+ let(:create_records!) do
25
+ User.create name: 'Allan', discarded_attribute: 'THIS IS MY PASSWORD'
26
+ User.create name: 'Doe'
27
+ end
28
+
29
+ before do
30
+ run_migrations!
31
+ spawn_objects!
32
+ create_records!
33
+ end
34
+
35
+ it { expect(serialized[:users].count).to eq(2) }
36
+ it { expect(serialized[:users]).to eq([{ name: 'Allan' }, { name: 'Doe' }]) }
37
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Encore::Serializer do
4
+ let(:serializer) { Encore::Serializer::Instance }
5
+ let(:objects) { User.all }
6
+ let(:serialized) { serializer.new(objects, skip_paging: true).as_json }
7
+
8
+ let(:run_migrations!) do
9
+ run_migration do
10
+ create_table(:users, force: true) do |t|
11
+ t.string :name, default: nil
12
+ t.string :discarded_attribute, default: nil
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:spawn_objects!) do
18
+ spawn_model('User')
19
+ spawn_serializer('UserSerializer') do
20
+ attributes :name
21
+ end
22
+ end
23
+
24
+ let(:create_records!) do
25
+ User.create name: 'Allan'
26
+ User.create name: 'Doe'
27
+ User.create name: 'Ding'
28
+ User.create name: 'Bob'
29
+ end
30
+
31
+ before do
32
+ expect(User).to receive(:page).never
33
+ run_migrations!
34
+ spawn_objects!
35
+ create_records!
36
+ end
37
+
38
+ it { expect(serialized[:meta]).to eq({}) }
39
+ it { expect(serialized[:users].count).to eq(4) }
40
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Encore do
4
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,38 +1,25 @@
1
- $:.unshift File.expand_path('../lib', __FILE__)
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
2
 
3
- # Test dependencies
4
3
  require 'rspec'
5
4
  require 'sqlite3'
6
5
 
7
- # Data structure
8
- require 'active_record'
9
- require 'ostruct'
10
-
11
- # Encore
12
6
  require 'encore'
13
7
 
14
8
  # Require our macros and extensions
15
- Dir[File.expand_path('../../spec/support/macros/*.rb', __FILE__)].map(&method(:require))
16
-
17
- # Emulate our railtie's behavior
18
- require 'encore/railtie'
19
- Encore::Helpers::ControllerHelper.inject_into_action_controller
9
+ Dir[File.expand_path('../../spec/support/macros/**/*.rb', __FILE__)].map(&method(:require))
20
10
 
21
11
  RSpec.configure do |config|
22
12
  # Include our macros
23
13
  config.include DatabaseMacros
24
14
  config.include ModelMacros
25
15
 
26
- # Shared examples aliases
27
- config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
28
-
29
- config.before(:each) do
30
- # Create the SQLite database
31
- setup_database
16
+ config.before :each do
17
+ # Setup the database
18
+ setup_database(adapter: 'sqlite3', database: 'encore_test')
32
19
  end
33
20
 
34
- config.after(:each) do
35
- # Make sure we remove our test database file
36
- cleanup_database
21
+ config.before :suite do
22
+ # Enforce available locales (otherwise generating error messages throws a warning)
23
+ I18n.enforce_available_locales = true
37
24
  end
38
25
  end
@@ -0,0 +1,9 @@
1
+ class DatabaseAdapter
2
+ def initialize(opts = {})
3
+ @database = opts[:database]
4
+ end
5
+
6
+ def establish_connection!
7
+ ActiveRecord::Base.establish_connection(database_configuration)
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'database_adapter'
2
+
3
+ class Sqlite3Adapter < DatabaseAdapter
4
+ def database_configuration
5
+ {
6
+ adapter: 'sqlite3',
7
+ database: ':memory:'
8
+ }
9
+ end
10
+
11
+ def reset_database!
12
+ ActiveRecord::Base.connection.execute("select 'drop table ' || name || ';' from sqlite_master where type = 'table';")
13
+ end
14
+ end
@@ -5,29 +5,18 @@ module DatabaseMacros
5
5
  klass = Class.new(ActiveRecord::Migration)
6
6
 
7
7
  # Create a new `up` that executes the argument
8
- klass.send(:define_method, :up) { self.instance_exec(&block) }
8
+ klass.send(:define_method, :up) { instance_exec(&block) }
9
9
 
10
10
  # Create a new instance of it and execute its `up` method
11
11
  klass.new.up
12
12
  end
13
13
 
14
- def self.database_file
15
- @database_file || File.expand_path('../test.db', __FILE__)
16
- end
17
-
18
- def setup_database
19
- # Make sure the test database file is gone
20
- cleanup_database
21
-
22
- # Establish the connection
23
- SQLite3::Database.new FileUtils.touch(DatabaseMacros.database_file).first
24
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: DatabaseMacros.database_file)
14
+ def setup_database(opts = {})
15
+ adapter = "#{opts[:adapter].capitalize}Adapter".constantize.new(database: opts[:database])
16
+ adapter.establish_connection!
17
+ adapter.reset_database!
25
18
 
26
19
  # Silence everything
27
20
  ActiveRecord::Base.logger = ActiveRecord::Migration.verbose = false
28
21
  end
29
-
30
- def cleanup_database
31
- FileUtils.rm(DatabaseMacros.database_file) if File.exists?(DatabaseMacros.database_file)
32
- end
33
22
  end
@@ -1,37 +1,22 @@
1
1
  module ModelMacros
2
- # Create a new Encore entity
3
- def spawn_entity(klass_name, &block)
4
- spawn_klass klass_name, Object do
5
- include Encore::Entity
6
- instance_exec(&block) if block
7
- end
8
- end
9
-
10
2
  # Create a new ActiveRecord model
11
3
  def spawn_model(klass_name, &block)
12
- spawn_klass klass_name, ActiveRecord::Base do
13
- instance_exec(&block) if block
14
- end
15
- end
16
-
17
- # Create a new OpenStruct struct
18
- def spawn_struct(klass_name, &block)
19
- spawn_klass klass_name, OpenStruct do
4
+ spawn_object klass_name, ActiveRecord::Base do
20
5
  instance_exec(&block) if block
21
6
  end
22
7
  end
23
8
 
24
- # Create a new ActionPack controller
25
- def spawn_controller(klass_name, &block)
26
- spawn_klass klass_name, ActionController::Base do
9
+ # Create a new Encore::Serializer object
10
+ def spawn_serializer(klass_name, &block)
11
+ spawn_object klass_name, Encore::Serializer::Base do
27
12
  instance_exec(&block) if block
28
13
  end
29
14
  end
30
15
 
31
- protected
16
+ protected
32
17
 
33
- # Create a new class
34
- def spawn_klass(klass_name, parent_klass, &block)
18
+ # Create a new model class
19
+ def spawn_object(klass_name, parent_klass, &block)
35
20
  Object.instance_eval { remove_const klass_name } if Object.const_defined?(klass_name)
36
21
  Object.const_set(klass_name, Class.new(parent_klass))
37
22
  Object.const_get(klass_name).class_eval(&block) if block_given?