datamappify 0.60.0 → 0.70.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +120 -3
  5. data/datamappify.gemspec +3 -3
  6. data/lib/datamappify/data/criteria/active_record/count.rb +0 -2
  7. data/lib/datamappify/data/criteria/active_record/criteria.rb +10 -0
  8. data/lib/datamappify/data/criteria/active_record/criteria_method.rb +65 -0
  9. data/lib/datamappify/data/criteria/active_record/find.rb +10 -2
  10. data/lib/datamappify/data/criteria/active_record/find_by_key.rb +4 -2
  11. data/lib/datamappify/data/criteria/active_record/limit.rb +10 -0
  12. data/lib/datamappify/data/criteria/active_record/match.rb +40 -0
  13. data/lib/datamappify/data/criteria/active_record/order.rb +41 -0
  14. data/lib/datamappify/data/criteria/active_record/rollback.rb +13 -0
  15. data/lib/datamappify/data/criteria/active_record/save.rb +1 -4
  16. data/lib/datamappify/data/criteria/active_record/save_by_key.rb +0 -1
  17. data/lib/datamappify/data/criteria/active_record/where.rb +18 -0
  18. data/lib/datamappify/data/criteria/common.rb +5 -2
  19. data/lib/datamappify/data/criteria/concerns/update_primary_record.rb +2 -0
  20. data/lib/datamappify/data/criteria/relational/concerns/find.rb +35 -0
  21. data/lib/datamappify/data/criteria/relational/concerns/find_by_key.rb +19 -0
  22. data/lib/datamappify/data/criteria/relational/criteria.rb +29 -0
  23. data/lib/datamappify/data/criteria/relational/{find_multiple.rb → criteria_base_method.rb} +2 -28
  24. data/lib/datamappify/data/criteria/relational/criteria_method.rb +44 -0
  25. data/lib/datamappify/data/criteria/relational/limit.rb +13 -0
  26. data/lib/datamappify/data/criteria/relational/save.rb +12 -0
  27. data/lib/datamappify/data/criteria/sequel/count.rb +0 -2
  28. data/lib/datamappify/data/criteria/sequel/criteria.rb +10 -0
  29. data/lib/datamappify/data/criteria/sequel/criteria_method.rb +51 -0
  30. data/lib/datamappify/data/criteria/sequel/find.rb +10 -2
  31. data/lib/datamappify/data/criteria/sequel/find_by_key.rb +4 -2
  32. data/lib/datamappify/data/criteria/sequel/limit.rb +10 -0
  33. data/lib/datamappify/data/criteria/sequel/match.rb +22 -0
  34. data/lib/datamappify/data/criteria/sequel/order.rb +22 -0
  35. data/lib/datamappify/data/criteria/sequel/rollback.rb +13 -0
  36. data/lib/datamappify/data/criteria/sequel/save.rb +1 -4
  37. data/lib/datamappify/data/criteria/sequel/save_by_key.rb +0 -1
  38. data/lib/datamappify/data/criteria/sequel/where.rb +18 -0
  39. data/lib/datamappify/data/errors.rb +8 -9
  40. data/lib/datamappify/data/mapper/attribute.rb +13 -33
  41. data/lib/datamappify/data/mapper.rb +13 -2
  42. data/lib/datamappify/data/provider/active_record.rb +8 -6
  43. data/lib/datamappify/data/provider/common_provider.rb +2 -2
  44. data/lib/datamappify/data/provider/sequel.rb +9 -6
  45. data/lib/datamappify/entity/association/reference.rb +63 -0
  46. data/lib/datamappify/entity/association/validation.rb +59 -0
  47. data/lib/datamappify/entity/association.rb +44 -0
  48. data/lib/datamappify/entity/{active_model/compatibility.rb → compatibility/active_model.rb} +2 -2
  49. data/lib/datamappify/entity/compatibility/active_record.rb +22 -0
  50. data/lib/datamappify/entity/compatibility/association/active_record.rb +43 -0
  51. data/lib/datamappify/entity/composable/attributes.rb +8 -16
  52. data/lib/datamappify/entity.rb +8 -6
  53. data/lib/datamappify/lazy/attributes_handler.rb +1 -1
  54. data/lib/datamappify/lazy.rb +0 -1
  55. data/lib/datamappify/repository/mapping_dsl.rb +9 -0
  56. data/lib/datamappify/repository/query_method/criteria.rb +21 -0
  57. data/lib/datamappify/repository/query_method/destroy.rb +1 -1
  58. data/lib/datamappify/repository/query_method/exists.rb +1 -1
  59. data/lib/datamappify/repository/query_method/find.rb +1 -9
  60. data/lib/datamappify/repository/query_method/method/multi_result_blender.rb +24 -0
  61. data/lib/datamappify/repository/query_method/method/reference_handler.rb +60 -0
  62. data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +19 -4
  63. data/lib/datamappify/repository/query_method/method.rb +58 -17
  64. data/lib/datamappify/repository/query_method/save.rb +4 -4
  65. data/lib/datamappify/repository/query_method/where_or_match.rb +24 -0
  66. data/lib/datamappify/repository/query_methods.rb +21 -5
  67. data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
  68. data/lib/datamappify/version.rb +1 -1
  69. data/spec/entity/{relation_spec.rb → association/reference_spec.rb} +2 -2
  70. data/spec/repository/associations/has_many_spec.rb +259 -0
  71. data/spec/repository/dirty_tracking_spec.rb +68 -57
  72. data/spec/repository/finders/all_spec.rb +29 -0
  73. data/spec/repository/finders/criteria_spec.rb +61 -0
  74. data/spec/repository/finders/different_providers_spec.rb +81 -0
  75. data/spec/repository/finders/find_spec.rb +21 -0
  76. data/spec/repository/finders/match_spec.rb +28 -0
  77. data/spec/repository/finders/where_spec.rb +18 -0
  78. data/spec/repository/reverse_mapped_spec.rb +20 -4
  79. data/spec/support/entities/group.rb +4 -0
  80. data/spec/support/entities/super_user.rb +9 -0
  81. data/spec/support/entities/user.rb +5 -0
  82. data/spec/support/entities/user_info.rb +5 -0
  83. data/spec/support/repositories/active_record/group_repository.rb +4 -0
  84. data/spec/support/repositories/active_record/super_user_repository.rb +23 -0
  85. data/spec/support/repositories/active_record/user_repository.rb +6 -4
  86. data/spec/support/repositories/sequel/group_repository.rb +4 -0
  87. data/spec/support/repositories/sequel/super_user_repository.rb +23 -0
  88. data/spec/support/repositories/sequel/user_repository.rb +6 -4
  89. data/spec/support/shared/contexts.rb +8 -1
  90. data/spec/support/shared/examples/repository/finders/where_or_match.rb +130 -0
  91. data/spec/support/tables/active_record.rb +20 -10
  92. data/spec/support/tables/sequel.rb +20 -7
  93. data/spec/unit/entity/{relation_spec.rb → association/reference_spec.rb} +5 -5
  94. data/spec/unit/repository/query_method_spec.rb +9 -2
  95. metadata +74 -42
  96. data/lib/datamappify/data/criteria/active_record/find_multiple.rb +0 -40
  97. data/lib/datamappify/data/criteria/relational/find.rb +0 -25
  98. data/lib/datamappify/data/criteria/relational/find_by_key.rb +0 -18
  99. data/lib/datamappify/data/criteria/sequel/find_multiple.rb +0 -43
  100. data/lib/datamappify/entity/relation.rb +0 -61
  101. data/lib/datamappify/repository/query_method/find_multiple.rb +0 -28
  102. data/spec/repository/finders_spec.rb +0 -211
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7fa8132e32a3dec1dd746263e0df0eff57b5448
4
- data.tar.gz: 7536658c8651e4f89cb6ff9021dcfe7107bc59ca
3
+ metadata.gz: 42a1f0451b17ea3e731eab36334ca301d2f82c54
4
+ data.tar.gz: fe1e60e36f3c5885829e0b3e109e1c6ae799572d
5
5
  SHA512:
6
- metadata.gz: 51787e716646495a2ccff4474e783805ad8e81d1a1de59f3600c0b2b9a3a89e509eb56016fd0f9728ee8310a14fb8fbde2e72f28b9346c193a8a7a2247b6716c
7
- data.tar.gz: 421115bd3c70db49fefe9a136ba9caedce4357d6e921e37c53533d350481a6f71e8304346c77389171451b6930ff01c4a6b998cb0fb24f0afa08c61363bee866
6
+ metadata.gz: 32641a2d06c6f2eb8281422edee1e292bb414c932266fcf04b0fd4fa7b59ddb75ae370c4d30295fd08d4b84f5f555c8208efe0a43bdfe1847d3b7051976f9b81
7
+ data.tar.gz: 12f10eda25ac22872ca671ef411531709448d547822bad740ca822ac306c2958cebf788cd6749c64ec28300c7919356abddf70a6d86b424b518646c0c649c1c1
data/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
3
  - 2.0.0
5
4
  env:
6
5
  - COV=2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## master
2
2
 
3
+ ## 0.70.0.beta1 [2013-09-26]
4
+
5
+ - Fixed dirty tracking for entities returned from `where` and `all`.
6
+ - Fixed a bug where in certain situations duplicated data records are created upon updating an entity.
7
+ - Fixed a bug where reversed mapping option `:via` generates wrong join queries.
8
+ - New API: Added `match` for repository.
9
+ - Added preliminary support for composed search criteria.
10
+ - Added `where` as a composable criteria.
11
+ - Added `match` as a composable criteria.
12
+ - Added `limit` as a composable criteria.
13
+ - Added `order` as a composable criteria.
14
+ - Added experimental support for associations.
15
+ - Added `reference_key` option to `map_attribute`.
16
+ - Added support for ActiveRecord-style attribute-nesting, e.g. `form.fields_for`.
17
+ - Added validation aggregation for associated entities.
18
+ - Added the ability to destroy associated items.
19
+ - Performance optimisations.
20
+ - Refactored the way Datamappify walks attributes to allow query optimisations.
21
+ - Optimised ActiveRecord queries for entities.
22
+
3
23
  ## 0.60.0 [2013-07-23]
4
24
 
5
25
  - New API: Introduced `Datamappify.config` for configuring default behaviours.
data/README.md CHANGED
@@ -27,17 +27,22 @@ Datamappify consists of three components:
27
27
 
28
28
  Below is a high level and somewhat simplified overview of Datamappify's architecture.
29
29
 
30
- ![](http://i.imgur.com/I9GpLds.png)
30
+ ![](http://i.imgur.com/BvtEO1u.png)
31
31
 
32
32
  Note: Datamappify is NOT affiliated with the [Datamapper](https://github.com/datamapper/) project.
33
33
 
34
34
  ### Built-in ORMs for Persistence
35
35
 
36
- You may implement your own [data provider and criterias](lib/datamappify/data), but Datamappify comes with build-in support for the following ORMS:
36
+ You may implement your own [data provider and criteria](lib/datamappify/data), but Datamappify comes with build-in support for the following ORMS:
37
37
 
38
38
  - ActiveRecord
39
39
  - Sequel
40
40
 
41
+ ## Requirements
42
+
43
+ - ruby 2.0+
44
+ - ActiveModel 4.0+
45
+
41
46
  ## Installation
42
47
 
43
48
  Add this line to your application's Gemfile:
@@ -205,6 +210,12 @@ class UserRepository
205
210
  #
206
211
  # this is useful for mapping form fields (similar to ActiveRecord's nested attributes)
207
212
  map_attribute :hobby_name, :to => 'Hobby#name', :via => :hobby_id
213
+
214
+ # by default, Datamappify maps attributes using an inferred reference (foreign) key,
215
+ # for example, the first mapping below will look for the `user_id` key in `Bio`,
216
+ # the second mapping below will look for the `person_id` key in `Bio` instead
217
+ map_attribute :bio, :to => 'Bio#body'
218
+ map_attribute :bio, :to => 'Bio#body', :reference_key => :person_id
208
219
  end
209
220
  ```
210
221
 
@@ -284,10 +295,41 @@ users = UserRepository.all
284
295
 
285
296
  Returns an array of entities.
286
297
 
298
+ ##### Simple
299
+
287
300
  ```ruby
288
301
  users = UserRepository.where(:first_name => 'Fred', :driver_license => 'AABBCCDD')
289
302
  ```
290
303
 
304
+ ##### Match
305
+
306
+ ```ruby
307
+ users = UserRepository.match(:first_name => 'Fre%', :driver_license => '%bbcc%')
308
+ ```
309
+
310
+ ##### Advanced
311
+
312
+ You may compose search criteria via the `criteria` method.
313
+
314
+ ```ruby
315
+ users = UserRepository.criteria(
316
+ :where => {
317
+ :first_name => 'Fred'
318
+ },
319
+ :order => {
320
+ :last_name => :asc
321
+ },
322
+ :limit => 10
323
+ )
324
+ ```
325
+
326
+ Currently implemented criteria options:
327
+
328
+ - where(Hash)
329
+ - match(Hash)
330
+ - order(Hash)
331
+ - limit(Integer)
332
+
291
333
  _Note: it does not currently support searching attributes from different data providers._
292
334
 
293
335
  #### Saving/updating entities
@@ -381,6 +423,76 @@ end
381
423
 
382
424
  Note: Returning either `nil` or `false` from the callback will cancel all subsequent callbacks (and the action itself, if it's a `before_` callback).
383
425
 
426
+ ### Association
427
+
428
+ Datamappify also supports entity association. It is experimental and it currently supports the following association types:
429
+
430
+ - belongs_to
431
+ - has_many
432
+
433
+ Set up your entities and repositories:
434
+
435
+ ```ruby
436
+ # entities
437
+
438
+ class User
439
+ include Datamappify::Entity
440
+
441
+ has_many :posts, :via => Post
442
+ end
443
+
444
+ class Post
445
+ include Datamappify::Entity
446
+
447
+ belongs_to :user
448
+ end
449
+
450
+ # repositories
451
+
452
+ class UserRepository
453
+ include Datamappify::Repository
454
+
455
+ for_entity User
456
+
457
+ references :posts, :via => PostRepository
458
+ end
459
+
460
+ class PostRepository
461
+ include Datamappify::Repository
462
+
463
+ for_entity Post
464
+ end
465
+ ```
466
+
467
+ Usage examples:
468
+
469
+ ```ruby
470
+ new_post = Post.new(post_attributes)
471
+ another_new_post = Post.new(post_attributes)
472
+ user = UserRepository.find(1)
473
+ user.posts = [new_post, another_new_post]
474
+
475
+ persisted_user = UserRepository.save!(user)
476
+
477
+ persisted_user.posts #=> an array of associated posts
478
+ ```
479
+
480
+ ### Nested attributes in forms
481
+
482
+ Like ActiveRecord and ActionView, Datamappify also supports nested attributes via `fields_for` or `simple_fields_for`.
483
+
484
+ ```ruby
485
+ # slim template
486
+
487
+ = simple_form_for @post do |f|
488
+ = f.input :title
489
+ = f.input :body
490
+
491
+ = f.simple_fields_for :comment do |fp|
492
+ = fp.input :author_name
493
+ = fp.input :comment_body
494
+ ```
495
+
384
496
  ### Default configuration
385
497
 
386
498
  You may configure Datamappify's default behaviour. In Rails you would put it in an initializer file.
@@ -391,6 +503,11 @@ Datamappify.config do |c|
391
503
  end
392
504
  ```
393
505
 
506
+ ## API Documentation
507
+
508
+ - [Rubygem release version](http://rubydoc.info/gems/datamappify/frames)
509
+ - [Github master version](http://rubydoc.info/github/fredwu/datamappify/master/frames)
510
+
394
511
  ## More Reading
395
512
 
396
513
  You may check out this [article](http://fredwu.me/post/54009567748/) for more examples.
@@ -415,7 +532,7 @@ Refer to [CHANGELOG](CHANGELOG.md).
415
532
 
416
533
  - [Fred Wu](http://fredwu.me/) - author.
417
534
  - [James Ladd](http://jamesladdcode.com/) for reviewing the code and giving advice on architectural decisions.
418
- - [Locomote](http://www.locomote.com.au/) - where Datamappify is built and being tested in product development.
535
+ - [Locomote](http://www.locomote.com.au/) - where Datamappify is built and used in our products.
419
536
  - And with these [awesome contributors](https://github.com/fredwu/datamappify/contributors)!
420
537
 
421
538
  ## License
data/datamappify.gemspec CHANGED
@@ -18,8 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "virtus", ">= 1.0.0.beta0", "<= 2.0"
22
- spec.add_dependency "activemodel", ">= 4.0.0.rc1", "< 5"
21
+ spec.add_dependency "virtus", ">= 1.0.0.beta6", "< 2.0"
22
+ spec.add_dependency "activemodel", "~> 4.0.0"
23
23
  spec.add_dependency "hooks", "~> 0.3.0"
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.3"
@@ -33,6 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "coveralls"
34
34
  spec.add_development_dependency "sqlite3"
35
35
  spec.add_development_dependency "sequel"
36
- spec.add_development_dependency "activerecord", ">= 4.0.0.rc1", "< 5"
36
+ spec.add_development_dependency "activerecord", "~> 4.0.0"
37
37
  spec.add_development_dependency "database_cleaner", "~> 1.0.1"
38
38
  end
@@ -1,5 +1,3 @@
1
- require 'datamappify/data/criteria/relational/count'
2
-
3
1
  module Datamappify
4
2
  module Data
5
3
  module Criteria
@@ -0,0 +1,10 @@
1
+ module Datamappify
2
+ module Data
3
+ module Criteria
4
+ module ActiveRecord
5
+ class Criteria < Relational::Criteria
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,65 @@
1
+ require 'datamappify/data/criteria/relational/criteria_method'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module ActiveRecord
7
+ class CriteriaMethod < Relational::CriteriaMethod
8
+ private
9
+
10
+ # @param scope [ActiveRecord::Relation]
11
+ #
12
+ # @return [ActiveRecord::Relation]
13
+ def records_scope(scope = nil)
14
+ (scope || source_class).includes(associated.map(&:association_key))
15
+ .references(associated.map(&:association_key))
16
+ end
17
+
18
+ # @return [Array<Attribute>]
19
+ def associated
20
+ attributes.select(&:secondary_attribute?)
21
+ end
22
+
23
+ # @param primaries [Array<Attribute>]
24
+ #
25
+ # @param secondaries [Array<Attribute>]
26
+ #
27
+ # @return [Hash]
28
+ def structured_criteria
29
+ _criteria = {}
30
+
31
+ primaries.each do |primary|
32
+ _criteria[primary.source_attribute_key] = primary.value
33
+ end
34
+
35
+ secondaries.each do |secondary|
36
+ table_alias = detect_table_alias(secondary)
37
+
38
+ _criteria[table_alias] ||= {}
39
+ _criteria[table_alias][secondary.source_attribute_key] = secondary.value
40
+ end
41
+
42
+ _criteria
43
+ end
44
+
45
+ # @see https://github.com/rails/rails/issues/12224
46
+ #
47
+ # @param [Attribute]
48
+ #
49
+ # @return [Symbol]
50
+ def detect_table_alias(attribute)
51
+ reflection = records_scope.send(:construct_join_dependency_for_association_find).join_parts.detect do |join|
52
+ join.try(:reflection).try(:name) == attribute.association_key
53
+ end
54
+
55
+ if reflection
56
+ reflection.tables[0].try(:right) || attribute.source_table
57
+ else
58
+ attribute.source_table
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,10 +1,18 @@
1
- require 'datamappify/data/criteria/relational/find'
1
+ require 'datamappify/data/criteria/relational/concerns/find'
2
+ require 'datamappify/data/criteria/active_record/criteria_method'
2
3
 
3
4
  module Datamappify
4
5
  module Data
5
6
  module Criteria
6
7
  module ActiveRecord
7
- class Find < Relational::Find
8
+ class Find < CriteriaMethod
9
+ include Relational::Concerns::Find
10
+
11
+ private
12
+
13
+ def record
14
+ records_scope.where(criteria).first
15
+ end
8
16
  end
9
17
  end
10
18
  end
@@ -1,10 +1,12 @@
1
- require 'datamappify/data/criteria/relational/find_by_key'
1
+ require 'datamappify/data/criteria/relational/concerns/find_by_key'
2
+ require 'datamappify/data/criteria/active_record/find'
2
3
 
3
4
  module Datamappify
4
5
  module Data
5
6
  module Criteria
6
7
  module ActiveRecord
7
- class FindByKey < Relational::FindByKey
8
+ class FindByKey < Find
9
+ include Relational::Concerns::FindByKey
8
10
  end
9
11
  end
10
12
  end
@@ -0,0 +1,10 @@
1
+ module Datamappify
2
+ module Data
3
+ module Criteria
4
+ module ActiveRecord
5
+ class Limit < Relational::Limit
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,40 @@
1
+ require 'datamappify/data/criteria/active_record/criteria_method'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module ActiveRecord
7
+ class Match < CriteriaMethod
8
+ # @param scope [ActiveRecord::Relation]
9
+ #
10
+ # @return [ActiveRecord::Relation]
11
+ def records(scope = nil)
12
+ records_scope(scope).where(string_criteria, *criteria.values)
13
+ end
14
+
15
+ private
16
+
17
+ # @return [String]
18
+ def string_criteria
19
+ structured_criteria.map do |column, value|
20
+ "lower(#{column}) LIKE lower(?)"
21
+ end.join(' AND ')
22
+ end
23
+
24
+ # @return [Hash]
25
+ def structured_criteria
26
+ Hash[*super.flat_map do |table, values|
27
+ if values.is_a?(Hash)
28
+ values.flat_map do |column, value|
29
+ ["#{table}.#{column}", value]
30
+ end
31
+ else
32
+ ["#{records_scope.table.name}.#{table}", values]
33
+ end
34
+ end]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ require 'datamappify/data/criteria/active_record/criteria_method'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module ActiveRecord
7
+ class Order < CriteriaMethod
8
+ def initialize(*args)
9
+ super
10
+
11
+ validate_order_args
12
+ end
13
+
14
+ # @param scope [ActiveRecord::Relation]
15
+ #
16
+ # @return [ActiveRecord::Relation]
17
+ def records(scope = nil)
18
+ structured_criteria.inject(records_scope(scope)) do |scope, (table, values)|
19
+ if values.is_a?(Hash)
20
+ values.inject(scope) do |s, (column, value)|
21
+ s.order("#{table}.#{column} #{value}")
22
+ end
23
+ else
24
+ scope.order(table => values)
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # @return [void]
32
+ def validate_order_args
33
+ unless (criteria.values - [:asc, :desc]).empty?
34
+ raise ArgumentError.new('Order direction should be :asc or :desc')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ module Datamappify
2
+ module Data
3
+ module Criteria
4
+ module ActiveRecord
5
+ class Rollback < Common
6
+ def perform
7
+ raise ::ActiveRecord::Rollback
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,3 @@
1
- require 'datamappify/data/criteria/relational/save'
2
1
  require 'datamappify/data/criteria/concerns/update_primary_record'
3
2
 
4
3
  module Datamappify
@@ -6,7 +5,7 @@ module Datamappify
6
5
  module Criteria
7
6
  module ActiveRecord
8
7
  class Save < Relational::Save
9
- include Concerns::UpdatePrimaryRecord
8
+ prepend Concerns::UpdatePrimaryRecord
10
9
 
11
10
  private
12
11
 
@@ -17,8 +16,6 @@ module Datamappify
17
16
 
18
17
  def save(record)
19
18
  record.update_attributes attributes_and_values
20
-
21
- super
22
19
  end
23
20
  end
24
21
  end
@@ -1,4 +1,3 @@
1
- require 'datamappify/data/criteria/relational/save_by_key'
2
1
  require 'datamappify/data/criteria/active_record/save'
3
2
 
4
3
  module Datamappify
@@ -0,0 +1,18 @@
1
+ require 'datamappify/data/criteria/active_record/criteria_method'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module ActiveRecord
7
+ class Where < CriteriaMethod
8
+ # @param scope [ActiveRecord::Relation]
9
+ #
10
+ # @return [ActiveRecord::Relation]
11
+ def records(scope = nil)
12
+ records_scope(scope).where(structured_criteria)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -32,6 +32,9 @@ module Datamappify
32
32
  @source_class = source_class
33
33
  @entity, @criteria, @attributes, @options = *args
34
34
  @block = block
35
+
36
+ @criteria ||= {}
37
+ @options ||= {}
35
38
  end
36
39
 
37
40
  # Performs the action (defined by child method classes) with callbacks
@@ -76,7 +79,7 @@ module Datamappify
76
79
  #
77
80
  # @return [Symbol]
78
81
  def key_name
79
- primary_record? ? :id : any_attribute.primary_reference_key
82
+ primary_record? || options[:via] ? :id : any_attribute.reference_key
80
83
  end
81
84
 
82
85
  # The value of {#key_name}
@@ -111,7 +114,7 @@ module Datamappify
111
114
  #
112
115
  # @return [Boolean]
113
116
  def ignore?
114
- attributes_and_values.empty?
117
+ attributes_and_values.empty? && !primary_record?
115
118
  end
116
119
 
117
120
  # Stores the attribute value in {Mapper::Attribute} for later use
@@ -6,6 +6,8 @@ module Datamappify
6
6
  private
7
7
 
8
8
  def save(record)
9
+ super
10
+
9
11
  if options && options[:via] && options[:primary_record]
10
12
  update_primary_record_with(record)
11
13
  end
@@ -0,0 +1,35 @@
1
+ module Datamappify
2
+ module Data
3
+ module Criteria
4
+ module Relational
5
+ module Concerns
6
+ module Find
7
+ def perform
8
+ record = records_scope.where(criteria).first
9
+
10
+ update_entity_with(record) if record
11
+
12
+ record
13
+ end
14
+
15
+ private
16
+
17
+ def update_entity_with(record)
18
+ attributes.each do |attribute|
19
+ if attribute.secondary_attribute? || attribute.reverse_mapped?
20
+ update_entity_attribute_with(attribute, record.send(attribute.association_key))
21
+ else
22
+ update_entity_attribute_with(attribute, record)
23
+ end
24
+ end
25
+ end
26
+
27
+ def update_entity_attribute_with(attribute, record)
28
+ entity.send("#{attribute.name}=", record.send(attribute.source_attribute_name)) if record
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ require 'datamappify/data/criteria/relational/concerns/set_criteria'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module Relational
7
+ module Concerns
8
+ module FindByKey
9
+ include SetCriteria
10
+
11
+ def initialize(source_class, entity, attributes, options = {}, &block)
12
+ super(source_class, entity, nil, attributes, options, &block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ require 'datamappify/data/criteria/relational/criteria_base_method'
2
+
3
+ module Datamappify
4
+ module Data
5
+ module Criteria
6
+ module Relational
7
+ class Criteria < CriteriaBaseMethod
8
+ # @return
9
+ # a collection represents the records, e.g. `ActiveRecord::Relation` or `Sequel::DataSet`
10
+ def records
11
+ criteria.inject(nil) do |scope, (name, method_criteria)|
12
+ criteria_class(name).new(
13
+ source_class, entity, method_criteria, attributes, options
14
+ ).records(scope)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ # @return [Class]
21
+ # the criteria class, e.g. `Where` or `Limit`
22
+ def criteria_class(name)
23
+ self.class.name.deconstantize.constantize.const_get(name.to_s.classify)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end