datamappify 0.30.0 → 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/ERD.png +0 -0
- data/README.md +78 -16
- data/datamappify.gemspec +4 -3
- data/lib/datamappify/data/criteria/active_record/destroy.rb +1 -1
- data/lib/datamappify/data/criteria/active_record/exists.rb +2 -2
- data/lib/datamappify/data/criteria/active_record/transaction.rb +1 -1
- data/lib/datamappify/data/criteria/common.rb +21 -1
- data/lib/datamappify/data/criteria/relational/count.rb +1 -1
- data/lib/datamappify/data/criteria/relational/find.rb +1 -1
- data/lib/datamappify/data/criteria/relational/save.rb +1 -1
- data/lib/datamappify/data/criteria/sequel/destroy.rb +1 -1
- data/lib/datamappify/data/criteria/sequel/exists.rb +1 -1
- data/lib/datamappify/data/criteria/sequel/transaction.rb +1 -1
- data/lib/datamappify/data/mapper/attribute.rb +9 -0
- data/lib/datamappify/data/mapper.rb +7 -2
- data/lib/datamappify/data/provider/common_provider.rb +1 -1
- data/lib/datamappify/entity/lazy_checking.rb +12 -0
- data/lib/datamappify/entity.rb +4 -0
- data/lib/datamappify/lazy/attributes_handler.rb +123 -0
- data/lib/datamappify/lazy/source_attributes_walker.rb +49 -0
- data/lib/datamappify/lazy.rb +24 -0
- data/lib/datamappify/logger.rb +13 -0
- data/lib/datamappify/repository/lazy_checking.rb +19 -0
- data/lib/datamappify/repository/mapping_dsl.rb +7 -1
- data/lib/datamappify/repository/query_method/callbacks.rb +83 -0
- data/lib/datamappify/repository/query_method/count.rb +6 -1
- data/lib/datamappify/repository/query_method/create.rb +10 -0
- data/lib/datamappify/repository/query_method/destroy.rb +6 -14
- data/lib/datamappify/repository/query_method/exists.rb +17 -0
- data/lib/datamappify/repository/query_method/find.rb +12 -19
- data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +81 -0
- data/lib/datamappify/repository/query_method/method.rb +74 -25
- data/lib/datamappify/repository/query_method/save.rb +15 -14
- data/lib/datamappify/repository/query_method/update.rb +10 -0
- data/lib/datamappify/repository/query_methods.rb +123 -0
- data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +122 -0
- data/lib/datamappify/repository/unit_of_work/persistent_states.rb +54 -0
- data/lib/datamappify/repository/unit_of_work/transaction.rb +18 -0
- data/lib/datamappify/repository/unit_of_work.rb +1 -0
- data/lib/datamappify/repository.rb +16 -51
- data/lib/datamappify/version.rb +1 -1
- data/lib/datamappify.rb +3 -1
- data/spec/lazy_spec.rb +73 -0
- data/spec/repository/callbacks_spec.rb +140 -0
- data/spec/repository/dirty_persistence_spec.rb +44 -0
- data/spec/repository/dirty_tracking_spec.rb +82 -0
- data/spec/repository/persistence_spec.rb +41 -119
- data/spec/repository/transactions_spec.rb +25 -0
- data/spec/repository/validation_spec.rb +42 -0
- data/spec/repository_spec.rb +8 -6
- data/spec/spec_helper.rb +2 -2
- data/spec/support/entities/hero_user.rb +5 -0
- data/spec/support/repositories/callbacks_chaining_repository.rb +92 -0
- data/spec/support/repositories/hero_user_repository.rb +30 -0
- data/spec/support/shared/contexts.rb +10 -0
- data/spec/support/tables/sequel.rb +1 -0
- data/spec/unit/repository/query_method_spec.rb +55 -0
- metadata +57 -10
- data/lib/datamappify/repository/query_method/transaction.rb +0 -18
- data/lib/datamappify/repository/query_method.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 425218c5431f6668819ee6dee7cec37c8aa9e23f
|
4
|
+
data.tar.gz: a928d202dd9750197c297721fcb7142279a52d42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 250ba0d6203d7a76ee0deab3ed581783048960851f280821be7670507b4cee18115d5593f97c74a790a628b5c342e4ac9bba6a63b9997bfba2ae1a76a94857cc
|
7
|
+
data.tar.gz: aa64bb2f335e6e480c8ae2b0f3dbb70a70d5a45f924920238ac416a19550e54f0d7e652cb3118769c8af3f351329a4365a6accd7ab2c522c5c8f1cfaaca0d2bf
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## master
|
2
2
|
|
3
|
+
## 0.40.0 [2013-05-21]
|
4
|
+
|
5
|
+
- Added dirty attribute tracking.
|
6
|
+
- Implemented attribute lazy loading!
|
7
|
+
- Query methods (`find`, `save` and `destroy`) no longer support multiple entities.
|
8
|
+
- e.g. `UserRepository.find([1, 2, 3])` is no longer valid.
|
9
|
+
- Added `exists?`, `create`, `create!`, `update` and `update!` to repository.
|
10
|
+
- Implemented callbacks (e.g. `before_save` and `after_save`, etc).
|
11
|
+
|
3
12
|
## 0.30.0 [2013-04-12]
|
4
13
|
|
5
14
|
- __Completely reimplemented Datamappify__
|
data/ERD.png
ADDED
Binary file
|
data/README.md
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
# Datamappify [![Gem Version](https://badge.fury.io/rb/datamappify.png)](http://badge.fury.io/rb/datamappify) [![Build Status](https://api.travis-ci.org/fredwu/datamappify.png)](http://travis-ci.org/fredwu/datamappify) [![Coverage Status](https://coveralls.io/repos/fredwu/datamappify/badge.png)](https://coveralls.io/r/fredwu/datamappify) [![Code Climate](https://codeclimate.com/github/fredwu/datamappify.png)](https://codeclimate.com/github/fredwu/datamappify)
|
2
|
-
|
3
|
-
__Datamappify is current in Proof-of-Concept stage, do NOT use it for anything other than experimentation.__
|
1
|
+
# Datamappify [![Gem Version](https://badge.fury.io/rb/datamappify.png)](http://badge.fury.io/rb/datamappify) [![Build Status](https://api.travis-ci.org/fredwu/datamappify.png?branch=master)](http://travis-ci.org/fredwu/datamappify) [![Coverage Status](https://coveralls.io/repos/fredwu/datamappify/badge.png)](https://coveralls.io/r/fredwu/datamappify) [![Code Climate](https://codeclimate.com/github/fredwu/datamappify.png)](https://codeclimate.com/github/fredwu/datamappify)
|
4
2
|
|
5
3
|
## Overview
|
6
4
|
|
@@ -8,15 +6,21 @@ Compose and manage domain logic and data persistence separately and intelligentl
|
|
8
6
|
|
9
7
|
Datamappify is built using [Virtus](https://github.com/solnic/virtus) and existing ORMs (ActiveRecord and Sequel, etc). The design goal is to utilise the powerfulness of existing ORMs as well as to separate domain logic (model behaviour) from data persistence.
|
10
8
|
|
9
|
+
My motivation for creating Datamappify is to hide the complexity of dealing with data in different data sources including the ones from external web services. Features like lazy loading and dirty tracking are designed to enhance the usability of dealing with web services.
|
10
|
+
|
11
11
|
Datamappify consists of three components:
|
12
12
|
|
13
13
|
- __Entity__ contains models behaviour, think an ActiveRecord model with the persistence specifics removed.
|
14
|
-
- __Data__ as the name suggests, holds your model data. It is an ORM object (ActiveRecord and Sequel, etc).
|
15
14
|
- __Repository__ is responsible for data retrieval and persistence, e.g. `find`, `save` and `destroy`, etc.
|
15
|
+
- __Data__ as the name suggests, holds your model data. It contains ORM objects (ActiveRecord and Sequel, etc).
|
16
|
+
|
17
|
+
![](ERD.png)
|
16
18
|
|
17
19
|
Note: Datamappify is NOT affiliated with the [Datamapper](https://github.com/datamapper/) project.
|
18
20
|
|
19
|
-
###
|
21
|
+
### Built-in ORMs for Persistence
|
22
|
+
|
23
|
+
You may implement your own [data provider and criterias](lib/datamappify/data), but Datamappify comes with build-in support for the following ORMS:
|
20
24
|
|
21
25
|
- ActiveRecord
|
22
26
|
- Sequel
|
@@ -55,6 +59,19 @@ class User
|
|
55
59
|
end
|
56
60
|
```
|
57
61
|
|
62
|
+
#### Lazy loading
|
63
|
+
|
64
|
+
Datamappify supports attribute lazy loading via the `Lazy` module.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class User
|
68
|
+
include Datamappify::Entity
|
69
|
+
include Datamappify::Lazy
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
When an entity is lazy loaded, only attributes from the default source will be loaded. Other attributes will only be loaded once they are called. This is especially useful if some of your data sources are external services.
|
74
|
+
|
58
75
|
### Repository
|
59
76
|
|
60
77
|
Map entity attributes to DB columns - better yet, you can even map attributes to __different ORMs__!
|
@@ -85,48 +102,93 @@ end
|
|
85
102
|
|
86
103
|
#### Retrieving entities
|
87
104
|
|
88
|
-
Pass in an id
|
105
|
+
Pass in an id.
|
89
106
|
|
90
107
|
```ruby
|
91
108
|
user = UserRepository.find(1)
|
92
|
-
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Checking if an entity exists in the repository
|
112
|
+
|
113
|
+
Pass in an entity.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
UserRepository.exists?(user)
|
93
117
|
```
|
94
118
|
|
95
119
|
#### Saving/updating entities
|
96
120
|
|
97
|
-
Pass in an entity
|
121
|
+
Pass in an entity.
|
98
122
|
|
99
123
|
There is also `save!` that raises `Datamappify::Data::EntityNotSaved`.
|
100
124
|
|
101
125
|
```ruby
|
102
126
|
UserRepository.save(user)
|
103
|
-
UserRepository.save([user, user2, user3])
|
104
127
|
```
|
105
128
|
|
129
|
+
Datamappify supports attribute dirty tracking - only dirty attributes will be saved.
|
130
|
+
|
106
131
|
#### Destroying an entity
|
107
132
|
|
108
|
-
Pass in an entity
|
133
|
+
Pass in an entity.
|
109
134
|
|
110
135
|
There is also `destroy!` that raises `Datamappify::Data::EntityNotDestroyed`.
|
111
136
|
|
112
137
|
Note that due to the attributes mapping, any data found in mapped ActiveRecord objects are not touched.
|
113
138
|
|
114
139
|
```ruby
|
115
|
-
UserRepository.destroy(1)
|
116
|
-
UserRepository.destroy([1, 2, 3])
|
117
140
|
UserRepository.destroy(user)
|
118
|
-
UserRepository.destroy([user, user2, user3])
|
119
141
|
```
|
120
142
|
|
143
|
+
#### Callbacks
|
144
|
+
|
145
|
+
Datamappify supports the following callbacks via [Hooks](https://github.com/apotonick/hooks):
|
146
|
+
|
147
|
+
- before_create
|
148
|
+
- before_update
|
149
|
+
- before_save
|
150
|
+
- before_destroy
|
151
|
+
- after_create
|
152
|
+
- after_update
|
153
|
+
- after_save
|
154
|
+
- after_destroy
|
155
|
+
|
156
|
+
Callbacks are defined in repositories, and they have access to the entity. Example:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class UserRepository
|
160
|
+
include Datamappify::Repository
|
161
|
+
|
162
|
+
before_create :make_me_admin
|
163
|
+
before_create :make_me_awesome
|
164
|
+
after_save :make_me_smile
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def make_me_admin(entity)
|
169
|
+
# ...
|
170
|
+
end
|
171
|
+
|
172
|
+
def make_me_awesome(entity)
|
173
|
+
# ...
|
174
|
+
end
|
175
|
+
|
176
|
+
def make_me_smile(entity)
|
177
|
+
# ...
|
178
|
+
end
|
179
|
+
|
180
|
+
# ...
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
Note: Returning either `nil` or `false` from the callback will cancel all subsequent callbacks (and the action itself, it it's a `before_` callback).
|
185
|
+
|
121
186
|
## Changelog
|
122
187
|
|
123
188
|
Refer to [CHANGELOG](CHANGELOG.md).
|
124
189
|
|
125
190
|
## Todo
|
126
191
|
|
127
|
-
- Track dirty entity attributes.
|
128
|
-
- Attribute lazy-loading.
|
129
|
-
- Hooks for persistence (`before_save` and `after_save`, etc).
|
130
192
|
- [Authoritative source](http://msdn.microsoft.com/en-au/library/ff649505.aspx).
|
131
193
|
- Enforce attribute type casting.
|
132
194
|
- Support for configurable primary keys and foreign keys.
|
data/datamappify.gemspec
CHANGED
@@ -18,8 +18,9 @@ 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",
|
22
|
-
spec.add_dependency "
|
21
|
+
spec.add_dependency "virtus", "~> 0.5"
|
22
|
+
spec.add_dependency "activemodel", ">= 4.0.0.rc1", "< 5"
|
23
|
+
spec.add_dependency "hooks", "~> 0.2.2"
|
23
24
|
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.3"
|
25
26
|
spec.add_development_dependency "rake"
|
@@ -32,6 +33,6 @@ Gem::Specification.new do |spec|
|
|
32
33
|
spec.add_development_dependency "coveralls"
|
33
34
|
spec.add_development_dependency "sqlite3"
|
34
35
|
spec.add_development_dependency "sequel"
|
35
|
-
spec.add_development_dependency "activerecord",
|
36
|
+
spec.add_development_dependency "activerecord", ">= 4.0.0.rc1", "< 5"
|
36
37
|
spec.add_development_dependency "database_cleaner", ">= 1.0.0.RC1", "< 2"
|
37
38
|
end
|
@@ -13,7 +13,7 @@ module Datamappify
|
|
13
13
|
# @return [void]
|
14
14
|
attr_reader :criteria
|
15
15
|
|
16
|
-
# @return [Set]
|
16
|
+
# @return [Set<Mapper::Attribute>]
|
17
17
|
attr_reader :attributes
|
18
18
|
|
19
19
|
# @param source_class [Class]
|
@@ -28,6 +28,17 @@ module Datamappify
|
|
28
28
|
@block = block
|
29
29
|
end
|
30
30
|
|
31
|
+
# Performs the action (defined by child method classes) with callbacks
|
32
|
+
#
|
33
|
+
# @return [void]
|
34
|
+
def perform_with_callbacks
|
35
|
+
result = perform
|
36
|
+
|
37
|
+
store_attribute_value if attributes
|
38
|
+
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
31
42
|
protected
|
32
43
|
|
33
44
|
# Key name of either the primary key (e.g. +id+) or foreign key (e.g. +user_id+)
|
@@ -80,6 +91,15 @@ module Datamappify
|
|
80
91
|
hash
|
81
92
|
end
|
82
93
|
|
94
|
+
# Stores the attribute value in {Mapper::Attribute} for later use
|
95
|
+
#
|
96
|
+
# @return [void]
|
97
|
+
def store_attribute_value
|
98
|
+
attributes.each do |attribute|
|
99
|
+
attribute.value = entity.instance_variable_get("@#{attribute.name}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
83
103
|
private
|
84
104
|
|
85
105
|
# Ignores the attribute if it isn't dirty or if it's a primary key
|
@@ -3,6 +3,11 @@ module Datamappify
|
|
3
3
|
class Mapper
|
4
4
|
# Represents an entity attribute and its associated data source
|
5
5
|
class Attribute
|
6
|
+
# Same as name, but in symbol
|
7
|
+
#
|
8
|
+
# @return [Symbol]
|
9
|
+
attr_reader :key
|
10
|
+
|
6
11
|
# @return [String]
|
7
12
|
attr_reader :name
|
8
13
|
|
@@ -15,6 +20,9 @@ module Datamappify
|
|
15
20
|
# @return [String]
|
16
21
|
attr_reader :source_attribute_name
|
17
22
|
|
23
|
+
# @return [any]
|
24
|
+
attr_accessor :value
|
25
|
+
|
18
26
|
# @param name [Symbol]
|
19
27
|
# name of the attribute
|
20
28
|
#
|
@@ -22,6 +30,7 @@ module Datamappify
|
|
22
30
|
# data provider, class and attribute,
|
23
31
|
# e.g. "ActiveRecord::User#surname"
|
24
32
|
def initialize(name, source)
|
33
|
+
@key = name
|
25
34
|
@name = name.to_s
|
26
35
|
|
27
36
|
@provider_name, @source_class_name, @source_attribute_name = parse_source(source)
|
@@ -34,10 +34,15 @@ module Datamappify
|
|
34
34
|
@default_source_class ||= default_provider.find_or_build_record_class(entity_class.name)
|
35
35
|
end
|
36
36
|
|
37
|
-
# @return [
|
37
|
+
# @return [Set<Attribute>]
|
38
|
+
def attributes
|
39
|
+
@attributes ||= Set.new(default_attributes + custom_attributes)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Hash<Set>]
|
38
43
|
# attribute sets classified by the names of their data provider
|
39
44
|
def classified_attributes
|
40
|
-
@classified_attributes ||= Set.new(
|
45
|
+
@classified_attributes ||= Set.new(attributes).classify(&:provider_name)
|
41
46
|
end
|
42
47
|
|
43
48
|
private
|
@@ -59,7 +59,7 @@ module Datamappify
|
|
59
59
|
# @yield
|
60
60
|
# an optional block passed to the +Criteria+ {Criteria::Common#initialize initialiser}
|
61
61
|
def build_criteria(name, *args, &block)
|
62
|
-
Data::Criteria.const_get(class_name).const_get(name).new(*args, &block).
|
62
|
+
Data::Criteria.const_get(class_name).const_get(name).new(*args, &block).perform_with_callbacks
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
data/lib/datamappify/entity.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
+
require 'observer'
|
1
2
|
require 'virtus'
|
3
|
+
require 'datamappify/entity/lazy_checking'
|
2
4
|
|
3
5
|
module Datamappify
|
4
6
|
module Entity
|
5
7
|
def self.included(klass)
|
6
8
|
klass.class_eval do
|
9
|
+
include Observable
|
7
10
|
include Virtus
|
8
11
|
include Virtus::Equalizer.new(inspect)
|
9
12
|
include ActiveModel::Validations
|
13
|
+
include LazyChecking
|
10
14
|
|
11
15
|
attribute :id, Integer
|
12
16
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Datamappify
|
2
|
+
module Lazy
|
3
|
+
# Overrides attribute setters and getters for lazy loading
|
4
|
+
class AttributesHandler
|
5
|
+
# @return [Array]
|
6
|
+
attr_accessor :uncached_attributes
|
7
|
+
|
8
|
+
# @param entity [Entity]
|
9
|
+
def initialize(entity)
|
10
|
+
@entity = entity
|
11
|
+
@uncached_attributes = entity.attributes.keys
|
12
|
+
|
13
|
+
entity.add_observer(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Triggers only when a reader query (e.g. {Repository::QueryMethod::Find}) is performed
|
17
|
+
#
|
18
|
+
# @param (see Repository::QueryMethod::Method::SourceAttributesWalker#walk_performed)
|
19
|
+
def update(query_method, attributes)
|
20
|
+
if query_method && query_method.reader?
|
21
|
+
cache_attributes!
|
22
|
+
uncached_attributes.each { |name| override_attribute(name) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @see Data::Mapper#attributes
|
29
|
+
#
|
30
|
+
# @return (see Data::Mapper#attributes)
|
31
|
+
def all_attributes
|
32
|
+
@all_attributes ||= @entity.repository.data_mapper.attributes
|
33
|
+
end
|
34
|
+
|
35
|
+
# Removes the cached attributes from the uncached attributes array
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
def cache_attributes!
|
39
|
+
@uncached_attributes = @uncached_attributes - @entity.cached_attributes.keys
|
40
|
+
end
|
41
|
+
|
42
|
+
# Overrides attribute setters and getters
|
43
|
+
#
|
44
|
+
# @param name [Symbol]
|
45
|
+
#
|
46
|
+
# @return [void]
|
47
|
+
def override_attribute(name)
|
48
|
+
override_attribute_setter(name)
|
49
|
+
override_attribute_getter(name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param (see #override_attribute)
|
53
|
+
#
|
54
|
+
# @return [void]
|
55
|
+
def override_attribute_setter(name)
|
56
|
+
@entity.define_singleton_method "#{name}=" do |value|
|
57
|
+
super(value)
|
58
|
+
|
59
|
+
self.define_singleton_method name do
|
60
|
+
instance_variable_set "@#{name}", value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param (see #override_attribute)
|
66
|
+
#
|
67
|
+
# @return [void]
|
68
|
+
def override_attribute_getter(name)
|
69
|
+
entity = @entity
|
70
|
+
attributes = attributes_from_same_source(name)
|
71
|
+
|
72
|
+
entity.define_singleton_method name do
|
73
|
+
Logger.performed(:override_attribute, name)
|
74
|
+
|
75
|
+
AttributesHandler.walk_attributes(name, entity, attributes)
|
76
|
+
|
77
|
+
instance_variable_get "@#{name}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param name [Symbol]
|
82
|
+
#
|
83
|
+
# @return [Set<Data::Mapper::Attribute>]
|
84
|
+
def attributes_from_same_source(name)
|
85
|
+
source_class_name = attribute_by_name(name).source_class_name
|
86
|
+
|
87
|
+
attributes = all_attributes.select do |attribute|
|
88
|
+
attribute.source_class_name == source_class_name
|
89
|
+
end
|
90
|
+
|
91
|
+
Set.new(attributes)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param name [Symbol]
|
95
|
+
#
|
96
|
+
# @return [Data::Mapper::Attribute]
|
97
|
+
def attribute_by_name(name)
|
98
|
+
all_attributes.find { |attribute| attribute.key == name }
|
99
|
+
end
|
100
|
+
|
101
|
+
class << self
|
102
|
+
# @param name [Symbol]
|
103
|
+
#
|
104
|
+
# @param attributes [Set<Data::Mapper::Attribute>]
|
105
|
+
#
|
106
|
+
# @return [void]
|
107
|
+
def walk_attributes(name, entity, attributes)
|
108
|
+
SourceAttributesWalker.new({
|
109
|
+
:entity => entity,
|
110
|
+
:provider_name => attributes.first.provider_name,
|
111
|
+
:attributes => attributes,
|
112
|
+
:dirty_aware? => true,
|
113
|
+
:dirty_attributes => []
|
114
|
+
}).execute do |provider_name, source_class, attributes|
|
115
|
+
entity.repository.data_mapper.provider(provider_name).build_criteria(
|
116
|
+
:FindByKey, source_class, entity, attributes
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'datamappify/repository/query_method/method/source_attributes_walker'
|
2
|
+
|
3
|
+
module Datamappify
|
4
|
+
module Lazy
|
5
|
+
class SourceAttributesWalker < Repository::QueryMethod::Method::SourceAttributesWalker
|
6
|
+
private
|
7
|
+
|
8
|
+
# @param (see Repository::QueryMethod::Method::SourceAttributesWalker#do_walk?)
|
9
|
+
#
|
10
|
+
# @see Repository::QueryMethod::Method::SourceAttributesWalker#do_walk?
|
11
|
+
#
|
12
|
+
# @return (see Repository::QueryMethod::Method::SourceAttributesWalker#do_walk?)
|
13
|
+
def do_walk?(source_class, attributes)
|
14
|
+
read_only? ? default_source_class?(source_class) : true
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param (see Repository::QueryMethod::Method::SourceAttributesWalker#walk_performed)
|
18
|
+
#
|
19
|
+
# @see Repository::QueryMethod::Method::SourceAttributesWalker#walk_performed
|
20
|
+
#
|
21
|
+
# @return (see Repository::QueryMethod::Method::SourceAttributesWalker#walk_performed)
|
22
|
+
def walk_performed(attributes)
|
23
|
+
attributes.each do |attribute|
|
24
|
+
@entity.cached_attributes[attribute.key] = attribute.value
|
25
|
+
end
|
26
|
+
|
27
|
+
@entity.changed
|
28
|
+
@entity.notify_observers(@query_method, attributes)
|
29
|
+
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param source_class [Class]
|
34
|
+
#
|
35
|
+
# @return [Boolean]
|
36
|
+
def default_source_class?(source_class)
|
37
|
+
@entity.repository.data_mapper.default_source_class == source_class
|
38
|
+
end
|
39
|
+
|
40
|
+
# Whether the walker is in read-only mode, it is determined from
|
41
|
+
# the {Repository::QueryMethod::Method query method} if available
|
42
|
+
#
|
43
|
+
# @return [Boolean]
|
44
|
+
def read_only?
|
45
|
+
!!@query_method && @query_method.reader?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'datamappify/lazy/source_attributes_walker'
|
2
|
+
require 'datamappify/lazy/source_attributes_walker'
|
3
|
+
require 'datamappify/lazy/attributes_handler'
|
4
|
+
|
5
|
+
module Datamappify
|
6
|
+
module Lazy
|
7
|
+
def self.included(klass)
|
8
|
+
klass.class_eval do
|
9
|
+
# @return [Repository]
|
10
|
+
cattr_accessor :repository
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
attr_accessor :cached_attributes
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
super
|
19
|
+
|
20
|
+
@cached_attributes = {}
|
21
|
+
@attributes_handler = AttributesHandler.new(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Datamappify
|
2
|
+
class Logger
|
3
|
+
# A meta method to help record whether an action has been performed
|
4
|
+
#
|
5
|
+
# @param args [any]
|
6
|
+
# optional args for logging/tracking purpose
|
7
|
+
#
|
8
|
+
# @return [TrueClass]
|
9
|
+
def self.performed(*args)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Datamappify
|
2
|
+
module Repository
|
3
|
+
module LazyChecking
|
4
|
+
# Is the repository for an entity that requires lazy loading?
|
5
|
+
#
|
6
|
+
# @return [Boolean]
|
7
|
+
def lazy_load?
|
8
|
+
data_mapper.entity_class.included_modules.include?(Datamappify::Lazy)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Assign the repository itself to the lazy entity
|
12
|
+
#
|
13
|
+
# @return [void]
|
14
|
+
def assign_to_entity
|
15
|
+
data_mapper.entity_class.repository = self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|