datamappify 0.60.0 → 0.70.0.beta1

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