lotus-model 0.0.0 → 0.1.0
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/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +5 -0
- data/EXAMPLE.md +217 -0
- data/Gemfile +14 -2
- data/README.md +303 -3
- data/Rakefile +17 -1
- data/lib/lotus-model.rb +1 -0
- data/lib/lotus/entity.rb +157 -0
- data/lib/lotus/model.rb +23 -2
- data/lib/lotus/model/adapters/abstract.rb +167 -0
- data/lib/lotus/model/adapters/implementation.rb +111 -0
- data/lib/lotus/model/adapters/memory/collection.rb +132 -0
- data/lib/lotus/model/adapters/memory/command.rb +90 -0
- data/lib/lotus/model/adapters/memory/query.rb +457 -0
- data/lib/lotus/model/adapters/memory_adapter.rb +149 -0
- data/lib/lotus/model/adapters/sql/collection.rb +209 -0
- data/lib/lotus/model/adapters/sql/command.rb +67 -0
- data/lib/lotus/model/adapters/sql/query.rb +615 -0
- data/lib/lotus/model/adapters/sql_adapter.rb +154 -0
- data/lib/lotus/model/mapper.rb +101 -0
- data/lib/lotus/model/mapping.rb +23 -0
- data/lib/lotus/model/mapping/coercer.rb +80 -0
- data/lib/lotus/model/mapping/collection.rb +336 -0
- data/lib/lotus/model/version.rb +4 -1
- data/lib/lotus/repository.rb +620 -0
- data/lotus-model.gemspec +15 -11
- data/test/entity_test.rb +126 -0
- data/test/fixtures.rb +81 -0
- data/test/model/adapters/abstract_test.rb +75 -0
- data/test/model/adapters/implementation_test.rb +22 -0
- data/test/model/adapters/memory/query_test.rb +91 -0
- data/test/model/adapters/memory_adapter_test.rb +1044 -0
- data/test/model/adapters/sql/query_test.rb +121 -0
- data/test/model/adapters/sql_adapter_test.rb +1078 -0
- data/test/model/mapper_test.rb +94 -0
- data/test/model/mapping/coercer_test.rb +27 -0
- data/test/model/mapping/collection_test.rb +82 -0
- data/test/repository_test.rb +283 -0
- data/test/test_helper.rb +30 -0
- data/test/version_test.rb +7 -0
- metadata +109 -11
@@ -0,0 +1,132 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Model
|
3
|
+
module Adapters
|
4
|
+
module Memory
|
5
|
+
# Acts like a SQL database table.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
# @since 0.1.0
|
9
|
+
class Collection
|
10
|
+
# A counter that simulates autoincrement primary key of a SQL table.
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 0.1.0
|
14
|
+
class PrimaryKey
|
15
|
+
# Initialize
|
16
|
+
#
|
17
|
+
# @return [Lotus::Model::Adapters::Memory::Collection::PrimaryKey]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @since 0.1.0
|
21
|
+
def initialize
|
22
|
+
@current = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
# Increment the current count by 1 and yields the given block
|
26
|
+
#
|
27
|
+
# @return [Fixnum] the incremented counter
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 0.1.0
|
31
|
+
def increment!
|
32
|
+
yield(@current += 1)
|
33
|
+
@current
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @attr_reader name [Symbol] the name of the collection (eg. `:users`)
|
38
|
+
#
|
39
|
+
# @since 0.1.0
|
40
|
+
# @api private
|
41
|
+
attr_reader :name
|
42
|
+
|
43
|
+
# @attr_reader identity [Symbol] the primary key of the collection
|
44
|
+
# (eg. `:id`)
|
45
|
+
#
|
46
|
+
# @since 0.1.0
|
47
|
+
# @api private
|
48
|
+
attr_reader :identity
|
49
|
+
|
50
|
+
# @attr_reader records [Hash] a set of records
|
51
|
+
#
|
52
|
+
# @since 0.1.0
|
53
|
+
# @api private
|
54
|
+
attr_reader :records
|
55
|
+
|
56
|
+
# Initialize a collection
|
57
|
+
#
|
58
|
+
# @param name [Symbol] the name of the collection (eg. `:users`).
|
59
|
+
# @param identity [Symbol] the primary key of the collection
|
60
|
+
# (eg. `:id`).
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
# @since 0.1.0
|
64
|
+
def initialize(name, identity)
|
65
|
+
@name, @identity = name, identity
|
66
|
+
clear
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates a record for the given entity and assigns an id.
|
70
|
+
#
|
71
|
+
# @param entity [Object] the entity to persist
|
72
|
+
#
|
73
|
+
# @see Lotus::Model::Adapters::Memory::Command#create
|
74
|
+
#
|
75
|
+
# @return the primary key of the created record
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
# @since 0.1.0
|
79
|
+
def create(entity)
|
80
|
+
@primary_key.increment! do |id|
|
81
|
+
entity[identity] = id
|
82
|
+
records[id] = entity
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Updates the record corresponding to the given entity.
|
87
|
+
#
|
88
|
+
# @param entity [Object] the entity to persist
|
89
|
+
#
|
90
|
+
# @see Lotus::Model::Adapters::Memory::Command#update
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
# @since 0.1.0
|
94
|
+
def update(entity)
|
95
|
+
records[entity.fetch(identity)] = entity
|
96
|
+
end
|
97
|
+
|
98
|
+
# Deletes the record corresponding to the given entity.
|
99
|
+
#
|
100
|
+
# @param entity [Object] the entity to delete
|
101
|
+
#
|
102
|
+
# @see Lotus::Model::Adapters::Memory::Command#delete
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
# @since 0.1.0
|
106
|
+
def delete(entity)
|
107
|
+
records.delete(entity.id)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns all the raw records
|
111
|
+
#
|
112
|
+
# @return [Array<Hash>]
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
# @since 0.1.0
|
116
|
+
def all
|
117
|
+
records.values
|
118
|
+
end
|
119
|
+
|
120
|
+
# Deletes all the records and resets the identity counter.
|
121
|
+
#
|
122
|
+
# @api private
|
123
|
+
# @since 0.1.0
|
124
|
+
def clear
|
125
|
+
@records = {}
|
126
|
+
@primary_key = PrimaryKey.new
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Model
|
3
|
+
module Adapters
|
4
|
+
module Memory
|
5
|
+
# Execute a command for the given collection.
|
6
|
+
#
|
7
|
+
# @see Lotus::Model::Adapters::Memory::Collection
|
8
|
+
# @see Lotus::Model::Mapping::Collection
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.1.0
|
12
|
+
class Command
|
13
|
+
# Initialize a command
|
14
|
+
#
|
15
|
+
# @param dataset [Lotus::Model::Adapters::Memory::Collection]
|
16
|
+
# @param collection [Lotus::Model::Mapping::Collection]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.1.0
|
20
|
+
def initialize(dataset, collection)
|
21
|
+
@dataset, @collection = dataset, collection
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates a record for the given entity.
|
25
|
+
#
|
26
|
+
# @param entity [Object] the entity to persist
|
27
|
+
#
|
28
|
+
# @see Lotus::Model::Adapters::Memory::Collection#insert
|
29
|
+
#
|
30
|
+
# @return the primary key of the just created record.
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.1.0
|
34
|
+
def create(entity)
|
35
|
+
@dataset.create(
|
36
|
+
_serialize(entity)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Updates the corresponding record for the given entity.
|
41
|
+
#
|
42
|
+
# @param entity [Object] the entity to persist
|
43
|
+
#
|
44
|
+
# @see Lotus::Model::Adapters::Memory::Collection#update
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.1.0
|
48
|
+
def update(entity)
|
49
|
+
@dataset.update(
|
50
|
+
_serialize(entity)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Deletes the corresponding record for the given entity.
|
55
|
+
#
|
56
|
+
# @param entity [Object] the entity to delete
|
57
|
+
#
|
58
|
+
# @see Lotus::Model::Adapters::Memory::Collection#delete
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
# @since 0.1.0
|
62
|
+
def delete(entity)
|
63
|
+
@dataset.delete(entity)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Deletes all the records from the table.
|
67
|
+
#
|
68
|
+
# @see Lotus::Model::Adapters::Memory::Collection#clear
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
# @since 0.1.0
|
72
|
+
def clear
|
73
|
+
@dataset.clear
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
# Serialize the given entity before to persist in the database.
|
78
|
+
#
|
79
|
+
# @return [Hash] the serialized entity
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
# @since 0.1.0
|
83
|
+
def _serialize(entity)
|
84
|
+
@collection.serialize(entity)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,457 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'lotus/utils/kernel'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Model
|
6
|
+
module Adapters
|
7
|
+
module Memory
|
8
|
+
# Query the in-memory database with a powerful API.
|
9
|
+
#
|
10
|
+
# All the methods are chainable, it allows advanced composition of
|
11
|
+
# conditions.
|
12
|
+
#
|
13
|
+
# This works as a lazy filtering mechanism: the records are fetched from
|
14
|
+
# the database only when needed.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
#
|
18
|
+
# query.where(language: 'ruby')
|
19
|
+
# .and(framework: 'lotus')
|
20
|
+
# .desc(:users_count).all
|
21
|
+
#
|
22
|
+
# # the records are fetched only when we invoke #all
|
23
|
+
#
|
24
|
+
# It implements Ruby's `Enumerable` and borrows some methods from `Array`.
|
25
|
+
# Expect a query to act like them.
|
26
|
+
#
|
27
|
+
# @since 0.1.0
|
28
|
+
class Query
|
29
|
+
include Enumerable
|
30
|
+
extend Forwardable
|
31
|
+
|
32
|
+
def_delegators :all, :each, :to_s, :empty?
|
33
|
+
|
34
|
+
# @attr_reader conditions [Array] an accumulator for the conditions
|
35
|
+
#
|
36
|
+
# @since 0.1.0
|
37
|
+
# @api private
|
38
|
+
attr_reader :conditions
|
39
|
+
|
40
|
+
# @attr_reader modifiers [Array] an accumulator for the modifiers
|
41
|
+
#
|
42
|
+
# @since 0.1.0
|
43
|
+
# @api private
|
44
|
+
attr_reader :modifiers
|
45
|
+
|
46
|
+
# Initialize a query
|
47
|
+
#
|
48
|
+
# @param dataset [Lotus::Model::Adapters::Memory::Collection]
|
49
|
+
# @param collection [Lotus::Model::Mapping::Collection]
|
50
|
+
# @param blk [Proc] an optional block that gets yielded in the
|
51
|
+
# context of the current query
|
52
|
+
#
|
53
|
+
# @since 0.1.0
|
54
|
+
# @api private
|
55
|
+
def initialize(dataset, collection, &blk)
|
56
|
+
@dataset = dataset
|
57
|
+
@collection = collection
|
58
|
+
@conditions = []
|
59
|
+
@modifiers = []
|
60
|
+
instance_eval(&blk) if block_given?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Resolves the query by fetching records from the database and
|
64
|
+
# translating them into entities.
|
65
|
+
#
|
66
|
+
# @return [Array] a collection of entities
|
67
|
+
#
|
68
|
+
# @since 0.1.0
|
69
|
+
def all
|
70
|
+
@collection.deserialize(run)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds a condition that behaves like SQL `WHERE`.
|
74
|
+
#
|
75
|
+
# It accepts a `Hash` with only one pair.
|
76
|
+
# The key must be the name of the column expressed as a `Symbol`.
|
77
|
+
# The value is the one used by the internal filtering logic.
|
78
|
+
#
|
79
|
+
# @param condition [Hash]
|
80
|
+
#
|
81
|
+
# @return self
|
82
|
+
#
|
83
|
+
# @since 0.1.0
|
84
|
+
#
|
85
|
+
# @example Fixed value
|
86
|
+
#
|
87
|
+
# query.where(language: 'ruby')
|
88
|
+
#
|
89
|
+
# @example Array
|
90
|
+
#
|
91
|
+
# query.where(id: [1, 3])
|
92
|
+
#
|
93
|
+
# @example Range
|
94
|
+
#
|
95
|
+
# query.where(year: 1900..1982)
|
96
|
+
#
|
97
|
+
# @example Multiple conditions
|
98
|
+
#
|
99
|
+
# query.where(language: 'ruby')
|
100
|
+
# .where(framework: 'lotus')
|
101
|
+
def where(condition)
|
102
|
+
column, value = _expand_condition(condition)
|
103
|
+
conditions.push(Proc.new{ find_all{|r| r.fetch(column) == value} })
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
alias_method :and, :where
|
108
|
+
alias_method :or, :where
|
109
|
+
|
110
|
+
# Logical negation of a #where condition.
|
111
|
+
#
|
112
|
+
# It accepts a `Hash` with only one pair.
|
113
|
+
# The key must be the name of the column expressed as a `Symbol`.
|
114
|
+
# The value is the one used by the internal filtering logic.
|
115
|
+
#
|
116
|
+
# @param condition [Hash]
|
117
|
+
#
|
118
|
+
# @since 0.1.0
|
119
|
+
#
|
120
|
+
# @return self
|
121
|
+
#
|
122
|
+
# @example Fixed value
|
123
|
+
#
|
124
|
+
# query.exclude(language: 'java')
|
125
|
+
#
|
126
|
+
# @example Array
|
127
|
+
#
|
128
|
+
# query.exclude(id: [4, 9])
|
129
|
+
#
|
130
|
+
# @example Range
|
131
|
+
#
|
132
|
+
# query.where(year: 1900..1982)
|
133
|
+
#
|
134
|
+
# @example Multiple conditions
|
135
|
+
#
|
136
|
+
# query.where(language: 'java')
|
137
|
+
# .where(company: 'enterprise')
|
138
|
+
def exclude(condition)
|
139
|
+
column, value = _expand_condition(condition)
|
140
|
+
conditions.push(Proc.new{ reject! {|r| r.fetch(column) == value} })
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
alias_method :not, :exclude
|
145
|
+
|
146
|
+
# Select only the specified columns.
|
147
|
+
#
|
148
|
+
# By default a query selects all the mapped columns.
|
149
|
+
#
|
150
|
+
# @param columns [Array<Symbol>]
|
151
|
+
#
|
152
|
+
# @return self
|
153
|
+
#
|
154
|
+
# @since 0.1.0
|
155
|
+
#
|
156
|
+
# @example Single column
|
157
|
+
#
|
158
|
+
# query.select(:name)
|
159
|
+
#
|
160
|
+
# @example Multiple columns
|
161
|
+
#
|
162
|
+
# query.select(:name, :year)
|
163
|
+
def select(*columns)
|
164
|
+
columns = Lotus::Utils::Kernel.Array(columns).uniq
|
165
|
+
modifiers.push(Proc.new{ flatten!; each {|r| r.delete_if {|k,_| !columns.include?(k)} } })
|
166
|
+
end
|
167
|
+
|
168
|
+
# Specify the ascending order of the records, sorted by the given
|
169
|
+
# columns.
|
170
|
+
#
|
171
|
+
# @param columns [Array<Symbol>] the column names
|
172
|
+
#
|
173
|
+
# @return self
|
174
|
+
#
|
175
|
+
# @since 0.1.0
|
176
|
+
#
|
177
|
+
# @see Lotus::Model::Adapters::Sql::Query#desc
|
178
|
+
#
|
179
|
+
# @example Single column
|
180
|
+
#
|
181
|
+
# query.order(:name)
|
182
|
+
#
|
183
|
+
# @example Multiple columns
|
184
|
+
#
|
185
|
+
# query.order(:name, :year)
|
186
|
+
#
|
187
|
+
# @example Multiple invokations
|
188
|
+
#
|
189
|
+
# query.order(:name).order(:year)
|
190
|
+
def order(*columns)
|
191
|
+
Lotus::Utils::Kernel.Array(columns).each do |column|
|
192
|
+
conditions.push(Proc.new{ sort_by{|r| r.fetch(column)} })
|
193
|
+
end
|
194
|
+
|
195
|
+
self
|
196
|
+
end
|
197
|
+
|
198
|
+
alias_method :asc, :order
|
199
|
+
|
200
|
+
# Specify the descending order of the records, sorted by the given
|
201
|
+
# columns.
|
202
|
+
#
|
203
|
+
# @param columns [Array<Symbol>] the column names
|
204
|
+
#
|
205
|
+
# @return self
|
206
|
+
#
|
207
|
+
# @since 0.1.0
|
208
|
+
#
|
209
|
+
# @see Lotus::Model::Adapters::Sql::Query#order
|
210
|
+
#
|
211
|
+
# @example Single column
|
212
|
+
#
|
213
|
+
# query.desc(:name)
|
214
|
+
#
|
215
|
+
# @example Multiple columns
|
216
|
+
#
|
217
|
+
# query.desc(:name, :year)
|
218
|
+
#
|
219
|
+
# @example Multiple invokations
|
220
|
+
#
|
221
|
+
# query.desc(:name).desc(:year)
|
222
|
+
def desc(*columns)
|
223
|
+
Lotus::Utils::Kernel.Array(columns).each do |column|
|
224
|
+
conditions.push(Proc.new{ sort_by{|r| r.fetch(column)}.reverse })
|
225
|
+
end
|
226
|
+
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
230
|
+
# Limit the number of records to return.
|
231
|
+
#
|
232
|
+
# @param number [Fixnum]
|
233
|
+
#
|
234
|
+
# @return self
|
235
|
+
#
|
236
|
+
# @since 0.1.0
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
#
|
240
|
+
# query.limit(1)
|
241
|
+
def limit(number)
|
242
|
+
modifiers.push(Proc.new{ replace(flatten.first(number)) })
|
243
|
+
self
|
244
|
+
end
|
245
|
+
|
246
|
+
# Simulate an `OFFSET` clause, without the need of specify a limit.
|
247
|
+
#
|
248
|
+
# @param number [Fixnum]
|
249
|
+
#
|
250
|
+
# @return self
|
251
|
+
#
|
252
|
+
# @since 0.1.0
|
253
|
+
#
|
254
|
+
# @example
|
255
|
+
#
|
256
|
+
# query.offset(10)
|
257
|
+
def offset(number)
|
258
|
+
modifiers.unshift(Proc.new{ replace(flatten.last(number)) })
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
262
|
+
# Returns the sum of the values for the given column.
|
263
|
+
#
|
264
|
+
# @param column [Symbol] the colum name
|
265
|
+
#
|
266
|
+
# @return [Numeric]
|
267
|
+
#
|
268
|
+
# @since 0.1.0
|
269
|
+
#
|
270
|
+
# @example
|
271
|
+
#
|
272
|
+
# query.sum(:comments_count)
|
273
|
+
def sum(column)
|
274
|
+
result = all
|
275
|
+
|
276
|
+
if result.any?
|
277
|
+
result.inject(0.0) do |acc, record|
|
278
|
+
if value = record.public_send(column)
|
279
|
+
acc += value
|
280
|
+
end
|
281
|
+
|
282
|
+
acc
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns the average of the values for the given column.
|
288
|
+
#
|
289
|
+
# @param column [Symbol] the colum name
|
290
|
+
#
|
291
|
+
# @return [Numeric]
|
292
|
+
#
|
293
|
+
# @since 0.1.0
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
#
|
297
|
+
# query.average(:comments_count)
|
298
|
+
def average(column)
|
299
|
+
if s = sum(column)
|
300
|
+
s / _all_with_present_column(column).count.to_f
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
alias_method :avg, :average
|
305
|
+
|
306
|
+
# Returns the maximum value for the given column.
|
307
|
+
#
|
308
|
+
# @param column [Symbol] the colum name
|
309
|
+
#
|
310
|
+
# @return result
|
311
|
+
#
|
312
|
+
# @since 0.1.0
|
313
|
+
#
|
314
|
+
# @example
|
315
|
+
#
|
316
|
+
# query.max(:comments_count)
|
317
|
+
def max(column)
|
318
|
+
_all_with_present_column(column).max
|
319
|
+
end
|
320
|
+
|
321
|
+
# Returns the minimum value for the given column.
|
322
|
+
#
|
323
|
+
# @param column [Symbol] the colum name
|
324
|
+
#
|
325
|
+
# @return result
|
326
|
+
#
|
327
|
+
# @since 0.1.0
|
328
|
+
#
|
329
|
+
# @example
|
330
|
+
#
|
331
|
+
# query.min(:comments_count)
|
332
|
+
def min(column)
|
333
|
+
_all_with_present_column(column).min
|
334
|
+
end
|
335
|
+
|
336
|
+
# Returns the difference between the MAX and MIN for the given column.
|
337
|
+
#
|
338
|
+
# @param column [Symbol] the colum name
|
339
|
+
#
|
340
|
+
# @return [Numeric]
|
341
|
+
#
|
342
|
+
# @since 0.1.0
|
343
|
+
#
|
344
|
+
# @see Lotus::Model::Adapters::Memory::Query#max
|
345
|
+
# @see Lotus::Model::Adapters::Memory::Query#min
|
346
|
+
#
|
347
|
+
# @example
|
348
|
+
#
|
349
|
+
# query.interval(:comments_count)
|
350
|
+
def interval(column)
|
351
|
+
max(column) - min(column)
|
352
|
+
rescue NoMethodError
|
353
|
+
end
|
354
|
+
|
355
|
+
# Returns a range of values between the MAX and the MIN for the given
|
356
|
+
# column.
|
357
|
+
#
|
358
|
+
# @param column [Symbol] the colum name
|
359
|
+
#
|
360
|
+
# @return [Range]
|
361
|
+
#
|
362
|
+
# @since 0.1.0
|
363
|
+
#
|
364
|
+
# @see Lotus::Model::Adapters::Memory::Query#max
|
365
|
+
# @see Lotus::Model::Adapters::Memory::Query#min
|
366
|
+
#
|
367
|
+
# @example
|
368
|
+
#
|
369
|
+
# query.range(:comments_count)
|
370
|
+
def range(column)
|
371
|
+
min(column)..max(column)
|
372
|
+
end
|
373
|
+
|
374
|
+
# Checks if at least one record exists for the current conditions.
|
375
|
+
#
|
376
|
+
# @return [TrueClass,FalseClass]
|
377
|
+
#
|
378
|
+
# @since 0.1.0
|
379
|
+
#
|
380
|
+
# @example
|
381
|
+
#
|
382
|
+
# query.where(author_id: 23).exists? # => true
|
383
|
+
def exist?
|
384
|
+
!count.zero?
|
385
|
+
end
|
386
|
+
|
387
|
+
# Returns a count of the records for the current conditions.
|
388
|
+
#
|
389
|
+
# @return [Fixnum]
|
390
|
+
#
|
391
|
+
# @since 0.1.0
|
392
|
+
#
|
393
|
+
# @example
|
394
|
+
#
|
395
|
+
# query.where(author_id: 23).count # => 5
|
396
|
+
def count
|
397
|
+
run.count
|
398
|
+
end
|
399
|
+
|
400
|
+
# This method is defined in order to make the interface of
|
401
|
+
# `Memory::Query` identical to `Sql::Query`, but this feature is NOT
|
402
|
+
# implemented
|
403
|
+
#
|
404
|
+
# @raise [NotImplementedError]
|
405
|
+
#
|
406
|
+
# @since 0.1.0
|
407
|
+
#
|
408
|
+
# @see Lotus::Model::Adapters::Sql::Query#negate!
|
409
|
+
def negate!
|
410
|
+
raise NotImplementedError
|
411
|
+
end
|
412
|
+
|
413
|
+
protected
|
414
|
+
def method_missing(m, *args, &blk)
|
415
|
+
if @context.respond_to?(m)
|
416
|
+
apply @context.public_send(m, *args, &blk)
|
417
|
+
else
|
418
|
+
super
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
private
|
423
|
+
# Apply all the conditions and returns a filtered collection.
|
424
|
+
#
|
425
|
+
# This operation is idempotent, but the records are actually fetched
|
426
|
+
# from the memory store.
|
427
|
+
#
|
428
|
+
# @return [Array]
|
429
|
+
#
|
430
|
+
# @api private
|
431
|
+
# @since 0.1.0
|
432
|
+
def run
|
433
|
+
result = @dataset.all.dup
|
434
|
+
|
435
|
+
result = conditions.map do |condition|
|
436
|
+
result.instance_exec(&condition)
|
437
|
+
end if conditions.any?
|
438
|
+
|
439
|
+
modifiers.map do |modifier|
|
440
|
+
result.instance_exec(&modifier)
|
441
|
+
end
|
442
|
+
|
443
|
+
Lotus::Utils::Kernel.Array(result)
|
444
|
+
end
|
445
|
+
|
446
|
+
def _all_with_present_column(column)
|
447
|
+
all.map {|record| record.public_send(column) }.compact
|
448
|
+
end
|
449
|
+
|
450
|
+
def _expand_condition(condition)
|
451
|
+
Array(condition).flatten
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|