datamappify 0.70.0.beta1 → 0.70.0.beta2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +55 -3
- data/datamappify.gemspec +4 -2
- data/lib/datamappify/data/criteria/active_record/limit.rb +7 -1
- data/lib/datamappify/data/criteria/active_record/save.rb +7 -1
- data/lib/datamappify/data/criteria/common.rb +4 -4
- data/lib/datamappify/data/criteria/relational/count.rb +4 -2
- data/lib/datamappify/data/criteria/sequel/limit.rb +4 -1
- data/lib/datamappify/data/criteria/sequel/save.rb +7 -1
- data/lib/datamappify/entity/association.rb +11 -0
- data/lib/datamappify/entity/association/validation.rb +1 -1
- data/lib/datamappify/entity/compatibility/active_record.rb +13 -0
- data/lib/datamappify/entity/compatibility/association/active_record.rb +19 -3
- data/lib/datamappify/entity/composable/attributes.rb +1 -1
- data/lib/datamappify/extensions/kaminari/collection_methods.rb +16 -0
- data/lib/datamappify/extensions/kaminari/criteria_processor.rb +36 -0
- data/lib/datamappify/lazy/attributes_handler.rb +2 -2
- data/lib/datamappify/repository.rb +9 -0
- data/lib/datamappify/repository/query_method/callbacks.rb +33 -6
- data/lib/datamappify/repository/query_method/count.rb +3 -1
- data/lib/datamappify/repository/query_method/method.rb +8 -5
- data/lib/datamappify/repository/query_method/method/reference_handler.rb +27 -4
- data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +3 -1
- data/lib/datamappify/repository/query_method/save.rb +1 -6
- data/lib/datamappify/repository/query_method/update.rb +4 -0
- data/lib/datamappify/repository/query_methods.rb +42 -11
- data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
- data/lib/datamappify/version.rb +1 -1
- data/spec/extensions/kaminari_spec.rb +27 -0
- data/spec/repository/associations/has_many_spec.rb +9 -42
- data/spec/repository/associations/has_one_spec.rb +185 -0
- data/spec/repository/callbacks_spec.rb +55 -14
- data/spec/repository/dirty_tracking_spec.rb +0 -1
- data/spec/repository/finders/criteria_spec.rb +1 -0
- data/spec/repository/persistence_spec.rb +21 -0
- data/spec/repository_spec.rb +18 -2
- data/spec/spec_helper.rb +0 -2
- data/spec/support/entities/group.rb +2 -1
- data/spec/support/repositories/active_record/group_repository.rb +2 -1
- data/spec/support/repositories/hero_user_repository.rb +12 -0
- data/spec/support/repositories/sequel/group_repository.rb +2 -1
- data/spec/support/shared/contexts.rb +1 -1
- data/spec/support/shared/examples/data/association_data_records.rb +28 -0
- data/spec/unit/entity/association/reference_spec.rb +4 -4
- data/spec/unit/entity/compatibility/active_record_spec.rb +19 -0
- metadata +46 -17
- data/lib/datamappify/data/criteria/relational/limit.rb +0 -13
- data/spec/support/monkey_patches/database_cleaner.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8b5c9791648cd0a93b4a98014a63d37830b40bd
|
4
|
+
data.tar.gz: 24be82a4d65be38dd1097fdeed79d1176e25b66a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3dea093938a3ad30b10fb5b84456d7716c6376e2a4a653f472783b6268311bdd9e47cf8b227263b76eded63e48b94ad48cea35e9ca5aeb118a8c17d6f5af4f4
|
7
|
+
data.tar.gz: 631134bfc10968a87b3c9c219c56107a3c030e3481d12bf8c736ed98d9a87a182024386fbc43e83b0e5dc03151b25fc7bf6abd36027ab58ed25df97e2ce0fd7f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## master
|
2
2
|
|
3
|
+
## 0.70.0.beta2 [2013-10-22]
|
4
|
+
|
5
|
+
- Proper dirty tracking in record updating, fixes an issue of setting an attribute to `nil`.
|
6
|
+
- Added `has_one` association.
|
7
|
+
- Added `before_load`, `after_load`, `before_find` and `after_find` callbacks.
|
8
|
+
- Added `before_init` and `after_init` callbacks.
|
9
|
+
- Added `offset` argument to the `limit` criteria.
|
10
|
+
- Added support for Kaminari pagination.
|
11
|
+
|
3
12
|
## 0.70.0.beta1 [2013-09-26]
|
4
13
|
|
5
14
|
- Fixed dirty tracking for entities returned from `where` and `all`.
|
data/README.md
CHANGED
@@ -319,7 +319,7 @@ users = UserRepository.criteria(
|
|
319
319
|
:order => {
|
320
320
|
:last_name => :asc
|
321
321
|
},
|
322
|
-
:limit => 10
|
322
|
+
:limit => [10, 20]
|
323
323
|
)
|
324
324
|
```
|
325
325
|
|
@@ -328,7 +328,7 @@ Currently implemented criteria options:
|
|
328
328
|
- where(Hash)
|
329
329
|
- match(Hash)
|
330
330
|
- order(Hash)
|
331
|
-
- limit(Integer)
|
331
|
+
- limit(Array<limit(Integer), offset(Integer)>)
|
332
332
|
|
333
333
|
_Note: it does not currently support searching attributes from different data providers._
|
334
334
|
|
@@ -380,14 +380,30 @@ Note that due to the attributes mapping, any data found in mapped records are no
|
|
380
380
|
UserRepository.destroy(user)
|
381
381
|
```
|
382
382
|
|
383
|
+
#### Initialising an entity
|
384
|
+
|
385
|
+
Accepts an entity class and returns a new entity.
|
386
|
+
|
387
|
+
This is useful for using `before_init` and `after_init` callbacks to set up the entity.
|
388
|
+
|
389
|
+
```ruby
|
390
|
+
UserRepository.init(user_class) #=> user
|
391
|
+
```
|
392
|
+
|
383
393
|
#### Callbacks
|
384
394
|
|
385
395
|
Datamappify supports the following callbacks via [Hooks](https://github.com/apotonick/hooks):
|
386
396
|
|
397
|
+
- before_init
|
398
|
+
- before_load
|
399
|
+
- before_find
|
387
400
|
- before_create
|
388
401
|
- before_update
|
389
402
|
- before_save
|
390
403
|
- before_destroy
|
404
|
+
- after_init
|
405
|
+
- after_load
|
406
|
+
- after_find
|
391
407
|
- after_create
|
392
408
|
- after_update
|
393
409
|
- after_save
|
@@ -427,7 +443,8 @@ Note: Returning either `nil` or `false` from the callback will cancel all subseq
|
|
427
443
|
|
428
444
|
Datamappify also supports entity association. It is experimental and it currently supports the following association types:
|
429
445
|
|
430
|
-
- belongs_to
|
446
|
+
- belongs_to (partially implemented)
|
447
|
+
- has_one
|
431
448
|
- has_many
|
432
449
|
|
433
450
|
Set up your entities and repositories:
|
@@ -438,9 +455,16 @@ Set up your entities and repositories:
|
|
438
455
|
class User
|
439
456
|
include Datamappify::Entity
|
440
457
|
|
458
|
+
has_one :title, :via => Title
|
441
459
|
has_many :posts, :via => Post
|
442
460
|
end
|
443
461
|
|
462
|
+
class Title
|
463
|
+
include Datamappify::Entity
|
464
|
+
|
465
|
+
belongs_to :user
|
466
|
+
end
|
467
|
+
|
444
468
|
class Post
|
445
469
|
include Datamappify::Entity
|
446
470
|
|
@@ -454,9 +478,16 @@ class UserRepository
|
|
454
478
|
|
455
479
|
for_entity User
|
456
480
|
|
481
|
+
references :title, :via => TitleRepository
|
457
482
|
references :posts, :via => PostRepository
|
458
483
|
end
|
459
484
|
|
485
|
+
class TitleRepository
|
486
|
+
include Datamappify::Repository
|
487
|
+
|
488
|
+
for_entity Title
|
489
|
+
end
|
490
|
+
|
460
491
|
class PostRepository
|
461
492
|
include Datamappify::Repository
|
462
493
|
|
@@ -470,10 +501,12 @@ Usage examples:
|
|
470
501
|
new_post = Post.new(post_attributes)
|
471
502
|
another_new_post = Post.new(post_attributes)
|
472
503
|
user = UserRepository.find(1)
|
504
|
+
user.title = Title.new(title_attributes)
|
473
505
|
user.posts = [new_post, another_new_post]
|
474
506
|
|
475
507
|
persisted_user = UserRepository.save!(user)
|
476
508
|
|
509
|
+
persisted_user.title #=> associated title
|
477
510
|
persisted_user.posts #=> an array of associated posts
|
478
511
|
```
|
479
512
|
|
@@ -503,6 +536,25 @@ Datamappify.config do |c|
|
|
503
536
|
end
|
504
537
|
```
|
505
538
|
|
539
|
+
### Built-in extensions
|
540
|
+
|
541
|
+
Datamappify ships with a few extensions to make certain tasks easier.
|
542
|
+
|
543
|
+
#### Kaminari
|
544
|
+
|
545
|
+
Use `Criteria` with `page` and `per`.
|
546
|
+
|
547
|
+
```ruby
|
548
|
+
UserRepository.criteria(
|
549
|
+
:where => {
|
550
|
+
:gender => 'male',
|
551
|
+
:age => 42
|
552
|
+
},
|
553
|
+
:page => 1,
|
554
|
+
:per => 10
|
555
|
+
)
|
556
|
+
```
|
557
|
+
|
506
558
|
## API Documentation
|
507
559
|
|
508
560
|
- [Rubygem release version](http://rubydoc.info/gems/datamappify/frames)
|
data/datamappify.gemspec
CHANGED
@@ -18,7 +18,7 @@ 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", "
|
21
|
+
spec.add_dependency "virtus", "~> 1.0.0"
|
22
22
|
spec.add_dependency "activemodel", "~> 4.0.0"
|
23
23
|
spec.add_dependency "hooks", "~> 0.3.0"
|
24
24
|
|
@@ -28,11 +28,13 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "yard"
|
29
29
|
spec.add_development_dependency "rspec"
|
30
30
|
spec.add_development_dependency "pry"
|
31
|
+
spec.add_development_dependency "pry-stack_explorer"
|
31
32
|
spec.add_development_dependency "simplecov"
|
32
33
|
spec.add_development_dependency "cane"
|
33
34
|
spec.add_development_dependency "coveralls"
|
34
35
|
spec.add_development_dependency "sqlite3"
|
35
36
|
spec.add_development_dependency "sequel"
|
36
37
|
spec.add_development_dependency "activerecord", "~> 4.0.0"
|
37
|
-
spec.add_development_dependency "
|
38
|
+
spec.add_development_dependency "kaminari"
|
39
|
+
spec.add_development_dependency "database_cleaner", "~> 1.2.0"
|
38
40
|
end
|
@@ -2,7 +2,13 @@ module Datamappify
|
|
2
2
|
module Data
|
3
3
|
module Criteria
|
4
4
|
module ActiveRecord
|
5
|
-
class Limit <
|
5
|
+
class Limit < Common
|
6
|
+
def records(scope = nil)
|
7
|
+
limit, offset = criteria
|
8
|
+
offset ||= 0
|
9
|
+
|
10
|
+
(scope || source_class).limit(limit).offset(offset)
|
11
|
+
end
|
6
12
|
end
|
7
13
|
end
|
8
14
|
end
|
@@ -15,7 +15,13 @@ module Datamappify
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def save(record)
|
18
|
-
record
|
18
|
+
sanitise_attributes!(record)
|
19
|
+
|
20
|
+
record.update_attributes(attributes_and_values) unless ignore?
|
21
|
+
end
|
22
|
+
|
23
|
+
def sanitise_attributes!(record)
|
24
|
+
attributes_and_values.delete_if { |_, v| v.nil? } if record.new_record?
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -138,13 +138,13 @@ module Datamappify
|
|
138
138
|
|
139
139
|
private
|
140
140
|
|
141
|
-
# Ignores the attribute
|
142
|
-
#
|
143
|
-
# @todo implement proper dirty attribute tracking
|
141
|
+
# Ignores the attribute accordingly
|
144
142
|
#
|
145
143
|
# @return [Boolean]
|
146
144
|
def ignore_attribute?(attribute)
|
147
|
-
|
145
|
+
unless new_record? || entity.lazy_loaded?
|
146
|
+
!options[:dirty_attributes].include?(attribute.key)
|
147
|
+
end
|
148
148
|
end
|
149
149
|
end
|
150
150
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'datamappify/data/criteria/relational/criteria'
|
2
|
+
|
1
3
|
module Datamappify
|
2
4
|
module Data
|
3
5
|
module Criteria
|
4
6
|
module Relational
|
5
|
-
class Count <
|
7
|
+
class Count < Criteria
|
6
8
|
def perform
|
7
|
-
source_class.count
|
9
|
+
(records || source_class).count
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
@@ -15,7 +15,13 @@ module Datamappify
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def save(record)
|
18
|
-
record
|
18
|
+
sanitise_attributes!(record)
|
19
|
+
|
20
|
+
record.update(attributes_and_values) unless ignore?
|
21
|
+
end
|
22
|
+
|
23
|
+
def sanitise_attributes!(record)
|
24
|
+
attributes_and_values.delete_if { |_, v| v.nil? } if record.new?
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -28,6 +28,17 @@ module Datamappify
|
|
28
28
|
references name
|
29
29
|
end
|
30
30
|
|
31
|
+
# @param name [Symbol, String]
|
32
|
+
#
|
33
|
+
# @param options [Hash]
|
34
|
+
#
|
35
|
+
# @return [void]
|
36
|
+
def has_one(name, options = {})
|
37
|
+
attribute name, options[:via]
|
38
|
+
|
39
|
+
self.associations << name
|
40
|
+
end
|
41
|
+
|
31
42
|
# @param name [Symbol, String]
|
32
43
|
#
|
33
44
|
# @param options [Hash]
|
@@ -32,7 +32,7 @@ module Datamappify
|
|
32
32
|
# @return [void]
|
33
33
|
def aggregate_validation_errors(context)
|
34
34
|
self.associations.each do |association|
|
35
|
-
self.send(association).reject(&:destroy?).each do |entity|
|
35
|
+
Array.wrap(self.send(association)).reject(&:destroy?).each do |entity|
|
36
36
|
entity.invalid?(context) && referenced_entity_validation_errors(entity)
|
37
37
|
end
|
38
38
|
end
|
@@ -8,6 +8,8 @@ module Datamappify
|
|
8
8
|
|
9
9
|
def self.included(klass)
|
10
10
|
klass.class_eval do
|
11
|
+
extend ClassMethods
|
12
|
+
|
11
13
|
attribute :_destroy, Virtus::Attribute::Boolean, :default => false
|
12
14
|
end
|
13
15
|
end
|
@@ -16,6 +18,17 @@ module Datamappify
|
|
16
18
|
def destroy?
|
17
19
|
!!_destroy
|
18
20
|
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# a minimal compatibility implementation needed by nested_form
|
24
|
+
#
|
25
|
+
# @see: https://github.com/ryanb/nested_form/blob/68485d/lib/nested_form/builder_mixin.rb#L28
|
26
|
+
def reflect_on_association(association)
|
27
|
+
if member_type = self.attribute_set.detect { |a| a.name == association }.try(:member_type)
|
28
|
+
Struct.new(:klass).new(member_type.primitive)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
19
32
|
end
|
20
33
|
end
|
21
34
|
end
|
@@ -3,10 +3,26 @@ module Datamappify
|
|
3
3
|
module Compatibility
|
4
4
|
module Association
|
5
5
|
# Compatibility layer for ActionRecord
|
6
|
+
#
|
7
|
+
# adds `association_name_attributes` as required by nested attributes assignment
|
6
8
|
module ActiveRecord
|
7
|
-
#
|
8
|
-
|
9
|
-
|
9
|
+
# @param (see DSL#has_one)
|
10
|
+
def has_one(name, options = {})
|
11
|
+
super
|
12
|
+
|
13
|
+
self.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
14
|
+
def #{name}_attributes=(params = {})
|
15
|
+
params.delete('id') if params['id'].blank?
|
16
|
+
|
17
|
+
if self.#{name}
|
18
|
+
params['id'] = self.#{name}.id
|
19
|
+
end
|
20
|
+
|
21
|
+
self.#{name} = #{options[:via]}.new(params)
|
22
|
+
end
|
23
|
+
CODE
|
24
|
+
end
|
25
|
+
|
10
26
|
# @param (see DSL#has_many)
|
11
27
|
def has_many(name, options = {})
|
12
28
|
super
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Datamappify
|
2
|
+
module Extensions
|
3
|
+
module Kaminari
|
4
|
+
module CollectionMethods
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
include ::Kaminari::ConfigurationMethods::ClassMethods
|
8
|
+
include ::Kaminari::PageScopeMethods
|
9
|
+
|
10
|
+
attr_accessor :limit_value, :offset_value, :total_count
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'datamappify/extensions/kaminari/collection_methods'
|
2
|
+
|
3
|
+
module Datamappify
|
4
|
+
module Extensions
|
5
|
+
module Kaminari
|
6
|
+
module CriteriaProcessor
|
7
|
+
# @see (Repository::QueryMethods#criteria)
|
8
|
+
def criteria(criteria)
|
9
|
+
process_criteria!(criteria)
|
10
|
+
|
11
|
+
collection = super
|
12
|
+
|
13
|
+
collection.class_eval { include CollectionMethods }
|
14
|
+
|
15
|
+
collection.limit_value = @limit
|
16
|
+
collection.offset_value = @offset
|
17
|
+
collection.total_count = count(criteria.except(:limit))
|
18
|
+
|
19
|
+
collection
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @see (Repository::QueryMethods#criteria)
|
25
|
+
def process_criteria!(criteria)
|
26
|
+
if (page = criteria.delete(:page).try(:to_i)) && (per = criteria.delete(:per).try(:to_i))
|
27
|
+
@limit = per
|
28
|
+
@offset = ([page, 1].max - 1) * per
|
29
|
+
|
30
|
+
criteria[:limit] = [@limit, @offset]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -111,9 +111,9 @@ module Datamappify
|
|
111
111
|
:attributes => attributes,
|
112
112
|
:dirty_aware? => true,
|
113
113
|
:dirty_attributes => []
|
114
|
-
}).execute do |provider_name, source_class,
|
114
|
+
}).execute do |provider_name, source_class, options|
|
115
115
|
entity.repository.data_mapper.provider(provider_name).build_criteria(
|
116
|
-
:FindByKey, source_class, entity, attributes
|
116
|
+
:FindByKey, source_class, entity, options[:attributes]
|
117
117
|
)
|
118
118
|
end
|
119
119
|
end
|
@@ -1,3 +1,8 @@
|
|
1
|
+
begin
|
2
|
+
require 'kaminari'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
1
6
|
require 'datamappify/repository/lazy_checking'
|
2
7
|
require 'datamappify/repository/mapping_dsl'
|
3
8
|
require 'datamappify/repository/unit_of_work'
|
@@ -51,6 +56,10 @@ module Datamappify
|
|
51
56
|
def_delegators :instance, *QueryMethods.instance_methods
|
52
57
|
end
|
53
58
|
end
|
59
|
+
|
60
|
+
def method_missing(symbol, *args, &block)
|
61
|
+
instance.send(symbol, *args, &block)
|
62
|
+
end
|
54
63
|
end
|
55
64
|
end
|
56
65
|
end
|