lotus-model 0.1.2 → 0.2.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 +52 -168
- data/EXAMPLE.md +35 -40
- data/README.md +152 -12
- data/lib/lotus/entity.rb +107 -24
- data/lib/lotus/model.rb +169 -8
- data/lib/lotus/model/adapters/abstract.rb +3 -2
- data/lib/lotus/model/adapters/file_system_adapter.rb +272 -0
- data/lib/lotus/model/adapters/implementation.rb +1 -1
- data/lib/lotus/model/adapters/memory/command.rb +2 -1
- data/lib/lotus/model/adapters/memory/query.rb +49 -10
- data/lib/lotus/model/adapters/memory_adapter.rb +13 -5
- data/lib/lotus/model/adapters/null_adapter.rb +20 -0
- data/lib/lotus/model/adapters/sql/collection.rb +18 -18
- data/lib/lotus/model/adapters/sql/query.rb +23 -3
- data/lib/lotus/model/config/adapter.rb +108 -0
- data/lib/lotus/model/config/mapper.rb +45 -0
- data/lib/lotus/model/configuration.rb +187 -0
- data/lib/lotus/model/mapper.rb +26 -3
- data/lib/lotus/model/mapping.rb +24 -0
- data/lib/lotus/model/mapping/coercer.rb +3 -3
- data/lib/lotus/model/mapping/coercions.rb +30 -0
- data/lib/lotus/model/mapping/collection.rb +127 -9
- data/lib/lotus/model/version.rb +1 -1
- data/lib/lotus/repository.rb +19 -11
- data/lotus-model.gemspec +2 -1
- metadata +16 -5
data/lib/lotus/entity.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'lotus/utils/kernel'
|
2
|
+
require 'lotus/utils/attributes'
|
2
3
|
|
3
4
|
module Lotus
|
4
5
|
# An object that is defined by its identity.
|
5
|
-
# See Domain Driven Design by Eric Evans.
|
6
|
+
# See "Domain Driven Design" by Eric Evans.
|
6
7
|
#
|
7
8
|
# An entity is the core of an application, where the part of the domain
|
8
|
-
# logic is implemented. It's a small, cohesive object that
|
9
|
+
# logic is implemented. It's a small, cohesive object that expresses coherent
|
9
10
|
# and meaningful behaviors.
|
10
11
|
#
|
11
12
|
# It deals with one and only one responsibility that is pertinent to the
|
@@ -21,18 +22,18 @@ module Lotus
|
|
21
22
|
#
|
22
23
|
# class Person
|
23
24
|
# include Lotus::Entity
|
24
|
-
#
|
25
|
+
# attributes :name, :age
|
25
26
|
# end
|
26
27
|
#
|
27
|
-
# When a class includes `Lotus::Entity`
|
28
|
-
# By then calling the `.attributes=` class method, the following methods are
|
29
|
-
# added:
|
28
|
+
# When a class includes `Lotus::Entity` it receives the following interface:
|
30
29
|
#
|
31
30
|
# * #id
|
32
31
|
# * #id=
|
33
32
|
# * #initialize(attributes = {})
|
34
33
|
#
|
35
|
-
#
|
34
|
+
# `Lotus::Entity` also provides the `.attributes=` for defining attribute accessors for the given names.
|
35
|
+
#
|
36
|
+
# If we expand the code above in **pure Ruby**, it would be:
|
36
37
|
#
|
37
38
|
# @example Pure Ruby
|
38
39
|
# class Person
|
@@ -43,11 +44,18 @@ module Lotus
|
|
43
44
|
# end
|
44
45
|
# end
|
45
46
|
#
|
46
|
-
#
|
47
|
-
#
|
47
|
+
# **Lotus::Model** ships `Lotus::Entity` for developers's convenience.
|
48
|
+
#
|
49
|
+
# **Lotus::Model** depends on a narrow and well-defined interface for an
|
50
|
+
# Entity - `#id`, `#id=`, `#initialize(attributes={})`.If your object
|
51
|
+
# implements that interface then that object can be used as an Entity in the
|
52
|
+
# **Lotus::Model** framework.
|
53
|
+
#
|
54
|
+
# However, we suggest to implement this interface by including
|
55
|
+
# `Lotus::Entity`, in case that future versions of the framework will expand
|
56
|
+
# it.
|
48
57
|
#
|
49
|
-
#
|
50
|
-
# in case that future versions of the framework will expand it.
|
58
|
+
# See Dependency Inversion Principle for more on interfaces.
|
51
59
|
#
|
52
60
|
# @since 0.1.0
|
53
61
|
#
|
@@ -88,9 +96,9 @@ module Lotus
|
|
88
96
|
# Please notice that the required `id` attribute is automatically defined
|
89
97
|
# and can be omitted in the arguments.
|
90
98
|
#
|
91
|
-
# @param
|
99
|
+
# @param attrs [Array<Symbol>] a set of arbitrary attribute names
|
92
100
|
#
|
93
|
-
# @since 0.
|
101
|
+
# @since 0.2.0
|
94
102
|
#
|
95
103
|
# @see Lotus::Repository
|
96
104
|
# @see Lotus::Model::Mapper
|
@@ -100,22 +108,59 @@ module Lotus
|
|
100
108
|
#
|
101
109
|
# class User
|
102
110
|
# include Lotus::Entity
|
103
|
-
#
|
111
|
+
# attributes :name, :age
|
112
|
+
# end
|
113
|
+
# User.attributes => #<Set: {:id, :name, :age}>
|
114
|
+
#
|
115
|
+
# @example Given params is array of attributes
|
116
|
+
# require 'lotus/model'
|
117
|
+
#
|
118
|
+
# class User
|
119
|
+
# include Lotus::Entity
|
120
|
+
# attributes [:name, :age]
|
121
|
+
# end
|
122
|
+
# User.attributes => #<Set: {:id, :name, :age}>
|
123
|
+
#
|
124
|
+
# @example Extend entity
|
125
|
+
# require 'lotus/model'
|
126
|
+
#
|
127
|
+
# class User
|
128
|
+
# include Lotus::Entity
|
129
|
+
# attributes :name
|
104
130
|
# end
|
105
|
-
|
106
|
-
|
131
|
+
#
|
132
|
+
# class DeletedUser < User
|
133
|
+
# include Lotus::Entity
|
134
|
+
# attributes :deleted_at
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# User.attributes => #<Set: {:id, :name}>
|
138
|
+
# DeletedUser.attributes => #<Set: {:id, :name, :deleted_at}>
|
139
|
+
def attributes(*attrs)
|
140
|
+
if attrs.any?
|
141
|
+
self.attributes.merge Lotus::Utils::Kernel.Array(attrs)
|
107
142
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
143
|
+
class_eval <<-END_EVAL, __FILE__, __LINE__
|
144
|
+
def initialize(attributes = {})
|
145
|
+
attributes = Lotus::Utils::Attributes.new(attributes)
|
146
|
+
#{@attributes.map do |a|
|
147
|
+
"@#{a} = attributes.get(:#{a})"
|
148
|
+
end.join("\n") }
|
149
|
+
end
|
150
|
+
END_EVAL
|
113
151
|
|
114
|
-
|
152
|
+
attr_accessor *@attributes
|
153
|
+
else
|
154
|
+
@attributes ||= Set.new([:id])
|
155
|
+
end
|
115
156
|
end
|
116
157
|
|
117
|
-
|
118
|
-
|
158
|
+
protected
|
159
|
+
|
160
|
+
# @see Class#inherited
|
161
|
+
def inherited(subclass)
|
162
|
+
subclass.attributes *attributes
|
163
|
+
super
|
119
164
|
end
|
120
165
|
end
|
121
166
|
|
@@ -146,6 +191,44 @@ module Lotus
|
|
146
191
|
self.class == other.class &&
|
147
192
|
self.id == other.id
|
148
193
|
end
|
194
|
+
|
195
|
+
# Return the hash of attributes
|
196
|
+
#
|
197
|
+
# @since 0.2.0
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
# require 'lotus/model'
|
201
|
+
# class User
|
202
|
+
# include Lotus::Entity
|
203
|
+
# attributes :name
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# user = User.new(id: 23, name: 'Luca')
|
207
|
+
# user.to_h # => { :id => 23, :name => "Luca" }
|
208
|
+
def to_h
|
209
|
+
Hash[self.class.attributes.map { |a| [a, public_send(a)] }]
|
210
|
+
end
|
211
|
+
|
212
|
+
# Set attributes for entity
|
213
|
+
#
|
214
|
+
# @since 0.2.0
|
215
|
+
#
|
216
|
+
# @example
|
217
|
+
# require 'lotus/model'
|
218
|
+
# class User
|
219
|
+
# include Lotus::Entity
|
220
|
+
# attributes :name
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# user = User.new(name: 'Lucca')
|
224
|
+
# user.update(name: 'Luca')
|
225
|
+
# user.name # => 'Luca'
|
226
|
+
def update(attributes={})
|
227
|
+
attributes.each do |attribute, value|
|
228
|
+
public_send("#{attribute}=", value)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
149
232
|
end
|
150
233
|
end
|
151
234
|
|
data/lib/lotus/model.rb
CHANGED
@@ -2,20 +2,13 @@ require 'lotus/model/version'
|
|
2
2
|
require 'lotus/entity'
|
3
3
|
require 'lotus/repository'
|
4
4
|
require 'lotus/model/mapper'
|
5
|
+
require 'lotus/model/configuration'
|
5
6
|
|
6
7
|
module Lotus
|
7
8
|
# Model
|
8
9
|
#
|
9
10
|
# @since 0.1.0
|
10
11
|
module Model
|
11
|
-
# Error for not found entity
|
12
|
-
#
|
13
|
-
# @since 0.1.0
|
14
|
-
#
|
15
|
-
# @see Lotus::Repository.find
|
16
|
-
class EntityNotFound < ::StandardError
|
17
|
-
end
|
18
|
-
|
19
12
|
# Error for non persisted entity
|
20
13
|
# It's raised when we try to update or delete a non persisted entity.
|
21
14
|
#
|
@@ -24,5 +17,173 @@ module Lotus
|
|
24
17
|
# @see Lotus::Repository.update
|
25
18
|
class NonPersistedEntityError < ::StandardError
|
26
19
|
end
|
20
|
+
|
21
|
+
# Error for invalid mapper configuration
|
22
|
+
# It's raised when mapping is not configured correctly
|
23
|
+
#
|
24
|
+
# @since 0.2.0
|
25
|
+
#
|
26
|
+
# @see Lotus::Configuration#mapping
|
27
|
+
class InvalidMappingError < ::StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
include Utils::ClassAttribute
|
31
|
+
|
32
|
+
# Framework configuration
|
33
|
+
#
|
34
|
+
# @since 0.2.0
|
35
|
+
# @api private
|
36
|
+
class_attribute :configuration
|
37
|
+
self.configuration = Configuration.new
|
38
|
+
|
39
|
+
# Configure the framework.
|
40
|
+
# It yields the given block in the context of the configuration
|
41
|
+
#
|
42
|
+
# @param blk [Proc] the configuration block
|
43
|
+
#
|
44
|
+
# @since 0.2.0
|
45
|
+
#
|
46
|
+
# @see Lotus::Model
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# require 'lotus/model'
|
50
|
+
#
|
51
|
+
# Lotus::Model.configure do
|
52
|
+
# adapter type: :sql, uri: 'postgres://localhost/database'
|
53
|
+
#
|
54
|
+
# mapping do
|
55
|
+
# collection :users do
|
56
|
+
# entity User
|
57
|
+
#
|
58
|
+
# attribute :id, Integer
|
59
|
+
# attribute :name, String
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Adapter MUST follow the convention in which adapter class is inflection of adapter name
|
65
|
+
# The above example has name :sql, thus derived class will be `Lotus::Model::Adapters::SqlAdapter`
|
66
|
+
def self.configure(&blk)
|
67
|
+
configuration.instance_eval(&blk)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Load the framework
|
72
|
+
#
|
73
|
+
# @since 0.2.0
|
74
|
+
# @api private
|
75
|
+
def self.load!
|
76
|
+
configuration.load!
|
77
|
+
end
|
78
|
+
|
79
|
+
# Unload the framework
|
80
|
+
#
|
81
|
+
# @since 0.2.0
|
82
|
+
# @api private
|
83
|
+
def self.unload!
|
84
|
+
configuration.unload!
|
85
|
+
end
|
86
|
+
|
87
|
+
# Duplicate Lotus::Model in order to create a new separated instance
|
88
|
+
# of the framework.
|
89
|
+
#
|
90
|
+
# The new instance of the framework will be completely decoupled from the
|
91
|
+
# original. It will inherit the configuration, but all the changes that
|
92
|
+
# happen after the duplication, won't be reflected on the other copies.
|
93
|
+
#
|
94
|
+
# @return [Module] a copy of Lotus::Model
|
95
|
+
#
|
96
|
+
# @since 0.2.0
|
97
|
+
# @api private
|
98
|
+
#
|
99
|
+
# @example Basic usage
|
100
|
+
# require 'lotus/model'
|
101
|
+
#
|
102
|
+
# module MyApp
|
103
|
+
# Model = Lotus::Model.dupe
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# MyApp::Model == Lotus::Model # => false
|
107
|
+
#
|
108
|
+
# MyApp::Model.configuration ==
|
109
|
+
# Lotus::Model.configuration # => false
|
110
|
+
#
|
111
|
+
# @example Inheriting configuration
|
112
|
+
# require 'lotus/model'
|
113
|
+
#
|
114
|
+
# Lotus::Model.configure do
|
115
|
+
# adapter type: :sql, uri: 'sqlite3://uri'
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# module MyApp
|
119
|
+
# Model = Lotus::Model.dupe
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# module MyApi
|
123
|
+
# Model = Lotus::Model.dupe
|
124
|
+
# Model.configure do
|
125
|
+
# adapter type: :sql, uri: 'postgresql://uri'
|
126
|
+
# end
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# Lotus::Model.configuration.adapter_config.uri # => 'sqlite3://uri'
|
130
|
+
# MyApp::Model.configuration.adapter_config.uri # => 'sqlite3://uri'
|
131
|
+
# MyApi::Model.configuration.adapter_config.uri # => 'postgresql://uri'
|
132
|
+
def self.dupe
|
133
|
+
dup.tap do |duplicated|
|
134
|
+
duplicated.configuration = configuration.duplicate
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Duplicate the framework and generate modules for the target application
|
139
|
+
#
|
140
|
+
# @param mod [Module] the Ruby namespace of the application
|
141
|
+
# @param blk [Proc] an optional block to configure the framework
|
142
|
+
#
|
143
|
+
# @return [Module] a copy of Lotus::Model
|
144
|
+
#
|
145
|
+
# @since 0.2.0
|
146
|
+
#
|
147
|
+
# @see Lotus::Model#dupe
|
148
|
+
# @see Lotus::Model::Configuration
|
149
|
+
#
|
150
|
+
# @example Basic usage
|
151
|
+
# require 'lotus/model'
|
152
|
+
#
|
153
|
+
# module MyApp
|
154
|
+
# Model = Lotus::Model.dupe
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# # It will:
|
158
|
+
# #
|
159
|
+
# # 1. Generate MyApp::Model
|
160
|
+
# # 2. Generate MyApp::Entity
|
161
|
+
# # 3. Generate MyApp::Repository
|
162
|
+
#
|
163
|
+
# MyApp::Model == Lotus::Model # => false
|
164
|
+
# MyApp::Repository == Lotus::Repository # => false
|
165
|
+
#
|
166
|
+
# @example Block usage
|
167
|
+
# require 'lotus/model'
|
168
|
+
#
|
169
|
+
# module MyApp
|
170
|
+
# Model = Lotus::Model.duplicate(self) do
|
171
|
+
# adapter type: :memory, uri: 'memory://localhost'
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# Lotus::Model.configuration.adapter_config # => nil
|
176
|
+
# MyApp::Model.configuration.adapter_config # => #<Lotus::Model::Config::Adapter:0x007ff0ff0244f8 @type=:memory, @uri="memory://localhost", @class_name="MemoryAdapter">
|
177
|
+
def self.duplicate(mod, &blk)
|
178
|
+
dupe.tap do |duplicated|
|
179
|
+
mod.module_eval %{
|
180
|
+
Entity = Lotus::Entity.dup
|
181
|
+
Repository = Lotus::Repository.dup
|
182
|
+
}
|
183
|
+
|
184
|
+
duplicated.configure(&blk) if block_given?
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
27
188
|
end
|
28
189
|
end
|
@@ -35,7 +35,8 @@ module Lotus
|
|
35
35
|
#
|
36
36
|
# @since 0.1.0
|
37
37
|
def initialize(mapper, uri = nil)
|
38
|
-
@mapper
|
38
|
+
@mapper = mapper
|
39
|
+
@uri = uri
|
39
40
|
end
|
40
41
|
|
41
42
|
# Creates or updates a record in the database for the given entity.
|
@@ -96,7 +97,7 @@ module Lotus
|
|
96
97
|
raise NotImplementedError
|
97
98
|
end
|
98
99
|
|
99
|
-
# Returns
|
100
|
+
# Returns a unique record from the given collection, with the given
|
100
101
|
# identity.
|
101
102
|
#
|
102
103
|
# @param collection [Symbol] the target collection (it must be mapped).
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'pathname'
|
3
|
+
require 'lotus/model/adapters/memory_adapter'
|
4
|
+
|
5
|
+
module Lotus
|
6
|
+
module Model
|
7
|
+
module Adapters
|
8
|
+
# In memory adapter with file system persistence.
|
9
|
+
# It behaves like the SQL adapter, but it doesn't support all the SQL
|
10
|
+
# features offered by that kind of databases.
|
11
|
+
#
|
12
|
+
# This adapter SHOULD be used only for development or testing purposes.
|
13
|
+
# Each read/write operation is wrapped by a `Mutex` and persisted to the
|
14
|
+
# disk.
|
15
|
+
#
|
16
|
+
# For those reasons it's really unefficient, but great for quick
|
17
|
+
# prototyping as it's schema-less.
|
18
|
+
#
|
19
|
+
# It works exactly like the `MemoryAdapter`, with the only difference
|
20
|
+
# that it persist data to the disk.
|
21
|
+
#
|
22
|
+
# The persistence policy uses Ruby `Marshal` `dump` and `load` operations.
|
23
|
+
# Please be aware of the limitations this model.
|
24
|
+
#
|
25
|
+
# @see Lotus::Model::Adapters::Implementation
|
26
|
+
# @see Lotus::Model::Adapters::MemoryAdapter
|
27
|
+
# @see http://www.ruby-doc.org/core/Marshal.html
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 0.2.0
|
31
|
+
class FileSystemAdapter < MemoryAdapter
|
32
|
+
# Default writing mode
|
33
|
+
#
|
34
|
+
# Binary, write only, create file if missing or erase if don't.
|
35
|
+
#
|
36
|
+
# @see http://ruby-doc.org/core/File/Constants.html
|
37
|
+
#
|
38
|
+
# @since 0.2.0
|
39
|
+
# @api private
|
40
|
+
WRITING_MODE = File::WRONLY|File::BINARY|File::CREAT
|
41
|
+
|
42
|
+
# Default chmod
|
43
|
+
#
|
44
|
+
# @see http://en.wikipedia.org/wiki/Chmod
|
45
|
+
#
|
46
|
+
# @since 0.2.0
|
47
|
+
# @api private
|
48
|
+
CHMOD = 0644
|
49
|
+
|
50
|
+
# File scheme
|
51
|
+
#
|
52
|
+
# @see https://tools.ietf.org/html/rfc3986
|
53
|
+
#
|
54
|
+
# @since 0.2.0
|
55
|
+
# @api private
|
56
|
+
FILE_SCHEME = 'file:///'.freeze
|
57
|
+
|
58
|
+
# Initialize the adapter.
|
59
|
+
#
|
60
|
+
# @param mapper [Object] the database mapper
|
61
|
+
# @param uri [String] the connection uri
|
62
|
+
#
|
63
|
+
# @return [Lotus::Model::Adapters::FileSystemAdapter]
|
64
|
+
#
|
65
|
+
# @see Lotus::Model::Mapper
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.2.0
|
69
|
+
def initialize(mapper, uri)
|
70
|
+
super
|
71
|
+
prepare(uri)
|
72
|
+
|
73
|
+
@_mutex = Mutex.new
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns all the records for the given collection
|
77
|
+
#
|
78
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
79
|
+
#
|
80
|
+
# @return [Array] all the records
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
# @since 0.2.0
|
84
|
+
def all(collection)
|
85
|
+
_synchronize do
|
86
|
+
read(collection)
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a unique record from the given collection, with the given
|
92
|
+
# id.
|
93
|
+
#
|
94
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
95
|
+
# @param id [Object] the identity of the object.
|
96
|
+
#
|
97
|
+
# @return [Object] the entity
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
# @since 0.2.0
|
101
|
+
def find(collection, id)
|
102
|
+
_synchronize do
|
103
|
+
read(collection)
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the first record in the given collection.
|
109
|
+
#
|
110
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
111
|
+
#
|
112
|
+
# @return [Object] the first entity
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
# @since 0.2.0
|
116
|
+
def first(collection)
|
117
|
+
_synchronize do
|
118
|
+
read(collection)
|
119
|
+
super
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the last record in the given collection.
|
124
|
+
#
|
125
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
126
|
+
#
|
127
|
+
# @return [Object] the last entity
|
128
|
+
#
|
129
|
+
# @api private
|
130
|
+
# @since 0.2.0
|
131
|
+
def last(collection)
|
132
|
+
_synchronize do
|
133
|
+
read(collection)
|
134
|
+
super
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Creates a record in the database for the given entity.
|
139
|
+
# It assigns the `id` attribute, in case of success.
|
140
|
+
#
|
141
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
142
|
+
# @param entity [#id=] the entity to create
|
143
|
+
#
|
144
|
+
# @return [Object] the entity
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
# @since 0.2.0
|
148
|
+
def create(collection, entity)
|
149
|
+
_synchronize do
|
150
|
+
super
|
151
|
+
write(collection)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Updates a record in the database corresponding to the given entity.
|
156
|
+
#
|
157
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
158
|
+
# @param entity [#id] the entity to update
|
159
|
+
#
|
160
|
+
# @return [Object] the entity
|
161
|
+
#
|
162
|
+
# @api private
|
163
|
+
# @since 0.2.0
|
164
|
+
def update(collection, entity)
|
165
|
+
_synchronize do
|
166
|
+
super
|
167
|
+
write(collection)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Deletes a record in the database corresponding to the given entity.
|
172
|
+
#
|
173
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
174
|
+
# @param entity [#id] the entity to delete
|
175
|
+
#
|
176
|
+
# @api private
|
177
|
+
# @since 0.2.0
|
178
|
+
def delete(collection, entity)
|
179
|
+
_synchronize do
|
180
|
+
super
|
181
|
+
write(collection)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Deletes all the records from the given collection and resets the
|
186
|
+
# identity counter.
|
187
|
+
#
|
188
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
189
|
+
#
|
190
|
+
# @api private
|
191
|
+
# @since 0.2.0
|
192
|
+
def clear(collection)
|
193
|
+
_synchronize do
|
194
|
+
super
|
195
|
+
write(collection)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Fabricates a query
|
200
|
+
#
|
201
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
202
|
+
# @param blk [Proc] a block of code to be executed in the context of
|
203
|
+
# the query.
|
204
|
+
#
|
205
|
+
# @return [Lotus::Model::Adapters::Memory::Query]
|
206
|
+
#
|
207
|
+
# @see Lotus::Model::Adapters::Memory::Query
|
208
|
+
#
|
209
|
+
# @api private
|
210
|
+
# @since 0.2.0
|
211
|
+
def query(collection, context = nil, &blk)
|
212
|
+
# _synchronize do
|
213
|
+
read(collection)
|
214
|
+
super
|
215
|
+
# end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Database informations
|
219
|
+
#
|
220
|
+
# @return [Hash] per collection informations
|
221
|
+
#
|
222
|
+
# @api private
|
223
|
+
# @since 0.2.0
|
224
|
+
def info
|
225
|
+
@collections.each_with_object({}) do |(collection,_), result|
|
226
|
+
result[collection] = query(collection).count
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
# @api private
|
232
|
+
# @since 0.2.0
|
233
|
+
def prepare(uri)
|
234
|
+
@root = Pathname.new(uri.sub(FILE_SCHEME, ''))
|
235
|
+
@root.mkpath
|
236
|
+
end
|
237
|
+
|
238
|
+
# @api private
|
239
|
+
# @since 0.2.0
|
240
|
+
def _synchronize
|
241
|
+
@_mutex.synchronize { yield }
|
242
|
+
end
|
243
|
+
|
244
|
+
# @api private
|
245
|
+
# @since 0.2.0
|
246
|
+
def write(collection)
|
247
|
+
path = @root.join("#{ collection }")
|
248
|
+
path.open(WRITING_MODE, CHMOD) {|f| f.write _dump( @collections.fetch(collection) ) }
|
249
|
+
end
|
250
|
+
|
251
|
+
# @api private
|
252
|
+
# @since 0.2.0
|
253
|
+
def read(collection)
|
254
|
+
path = @root.join("#{ collection }")
|
255
|
+
@collections[collection] = _load(path.read) if path.exist?
|
256
|
+
end
|
257
|
+
|
258
|
+
# @api private
|
259
|
+
# @since 0.2.0
|
260
|
+
def _dump(contents)
|
261
|
+
Marshal.dump(contents)
|
262
|
+
end
|
263
|
+
|
264
|
+
# @api private
|
265
|
+
# @since 0.2.0
|
266
|
+
def _load(contents)
|
267
|
+
Marshal.load(contents)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|