spiderfw 0.6.23 → 0.6.24
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.
- data/CHANGELOG +10 -1
- data/README.rdoc +1 -1
- data/VERSION +1 -1
- data/apps/config_editor/_init.rb +1 -2
- data/apps/config_editor/controllers/config_editor_controller.rb +1 -7
- data/apps/core/admin/controllers/admin_controller.rb +1 -1
- data/apps/core/admin/public/css/sass/admin.css +35 -31
- data/apps/core/admin/public/sass/admin.scss +6 -1
- data/apps/core/components/widgets/crud/crud.shtml +2 -2
- data/apps/core/components/widgets/table/table.rb +5 -5
- data/apps/core/forms/tags/element_row.erb +15 -10
- data/apps/core/forms/widgets/form/form.rb +35 -22
- data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +2 -2
- data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +2 -2
- data/apps/core/forms/widgets/inputs/file_input/file_input.shtml +2 -2
- data/apps/core/forms/widgets/inputs/html_area/html_area.shtml +2 -2
- data/apps/core/forms/widgets/inputs/input/input.shtml +2 -2
- data/apps/core/forms/widgets/inputs/password/password.shtml +2 -2
- data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +1 -1
- data/apps/core/forms/widgets/inputs/select/select.shtml +2 -2
- data/apps/core/forms/widgets/inputs/text/text.shtml +2 -2
- data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +2 -2
- data/apps/core/forms/widgets/inputs/time_span/time_span.shtml +1 -1
- data/blueprints/home/config.ru +8 -0
- data/lib/spiderfw/app.rb +416 -224
- data/lib/spiderfw/cmd/commands/app.rb +243 -239
- data/lib/spiderfw/cmd/commands/cert.rb +421 -417
- data/lib/spiderfw/cmd/commands/config.rb +85 -82
- data/lib/spiderfw/cmd/commands/console.rb +64 -40
- data/lib/spiderfw/cmd/commands/content.rb +29 -25
- data/lib/spiderfw/cmd/commands/create.rb +58 -54
- data/lib/spiderfw/cmd/commands/model.rb +118 -114
- data/lib/spiderfw/cmd/commands/setup.rb +55 -51
- data/lib/spiderfw/cmd/commands/test.rb +63 -59
- data/lib/spiderfw/cmd/commands/webserver.rb +56 -51
- data/lib/spiderfw/config/options/spider.rb +4 -3
- data/lib/spiderfw/controller/controller.rb +2 -0
- data/lib/spiderfw/controller/http_controller.rb +1 -2
- data/lib/spiderfw/controller/mixins/static_content.rb +3 -3
- data/lib/spiderfw/controller/mixins/visual.rb +30 -15
- data/lib/spiderfw/controller/response.rb +84 -0
- data/lib/spiderfw/controller/session/file_session.rb +2 -2
- data/lib/spiderfw/http/adapters/rack.rb +12 -13
- data/lib/spiderfw/http/server.rb +80 -46
- data/lib/spiderfw/i18n/cldr.rb +6 -9
- data/lib/spiderfw/model/base_model.rb +103 -23
- data/lib/spiderfw/model/condition.rb +110 -25
- data/lib/spiderfw/model/mappers/db_mapper.rb +14 -6
- data/lib/spiderfw/model/mappers/mapper.rb +440 -197
- data/lib/spiderfw/model/model.rb +105 -21
- data/lib/spiderfw/model/model_hash.rb +9 -1
- data/lib/spiderfw/model/query.rb +50 -9
- data/lib/spiderfw/model/query_set.rb +211 -44
- data/lib/spiderfw/model/request.rb +28 -21
- data/lib/spiderfw/model/storage/base_storage.rb +125 -10
- data/lib/spiderfw/model/storage/db/db_storage.rb +7 -4
- data/lib/spiderfw/model/storage.rb +8 -1
- data/lib/spiderfw/setup/spider_setup_wizard.rb +9 -7
- data/lib/spiderfw/spider.rb +270 -43
- data/lib/spiderfw/templates/layout.rb +9 -4
- data/lib/spiderfw/templates/resources/sass.rb +3 -2
- data/lib/spiderfw/templates/template.rb +1 -0
- data/lib/spiderfw/utils/annotations.rb +3 -1
- data/lib/spiderfw/utils/logger.rb +1 -1
- data/lib/spiderfw/utils/monkey/symbol.rb +4 -2
- data/lib/spiderfw/utils/shared_store/file_shared_store.rb +2 -2
- data/lib/spiderfw/utils/thread_out.rb +3 -1
- data/public/css/error_page.css +83 -0
- data/public/js/error_page.js +5 -0
- data/spider.gemspec +4 -1
- data/templates/email/error.erb +9 -0
- metadata +28 -12
- data/apps/config_editor/widgets/edit_bool/edit_bool.rb +0 -8
- data/apps/config_editor/widgets/edit_bool/edit_bool.shtml +0 -5
@@ -1,22 +1,35 @@
|
|
1
1
|
module Spider; module Model
|
2
2
|
|
3
|
+
# @abstract
|
3
4
|
# The Mapper connects a BaseModel to a Storage; it fetches data from the Storage and converts it to objects,
|
4
5
|
# and vice versa.
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
6
|
+
#
|
7
|
+
# Each model has one instance of the mapper, retrieved by {BaseModel.mapper}. The mapper has a pointer to
|
8
|
+
# its model, and one to a {Storage} instance, which is shared between all models accessing the same storage.
|
9
|
+
#
|
10
|
+
# The BaseModel provides methods for interacting with the mapper; it is not usually called directly,
|
11
|
+
# but it can be if needed (for example, to call the {#delete_all!} method, which is not exposed on the model).
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# Its methods may be overridden with BaseModel.with_mapper.
|
8
15
|
class Mapper
|
16
|
+
# @return [BaseModel] pointer to the model instance
|
9
17
|
attr_reader :model
|
18
|
+
# @return [Storage] pointer to the Storage instance
|
10
19
|
attr_accessor :storage
|
11
|
-
#
|
20
|
+
# A Symbolic name for the Mapper's subclass
|
21
|
+
# @return [Symbol]
|
12
22
|
attr_reader :type
|
13
23
|
|
14
24
|
# Returns whether this Mapper can write to the storage.
|
25
|
+
# return [true]
|
15
26
|
def self.write?
|
16
27
|
true
|
17
28
|
end
|
18
29
|
|
19
30
|
# Takes a BaseModel class and a storage instance.
|
31
|
+
# @param [BaseModel] model
|
32
|
+
# @param [Storage] storage
|
20
33
|
def initialize(model, storage)
|
21
34
|
@model = model
|
22
35
|
@storage = storage
|
@@ -28,12 +41,15 @@ module Spider; module Model
|
|
28
41
|
|
29
42
|
# Configuration methods
|
30
43
|
|
31
|
-
#
|
44
|
+
# Tells to the mapper that the given elements should not be handled.
|
45
|
+
# @param [*Element] Elements which should not be mapped
|
46
|
+
# @return [void]
|
32
47
|
def no_map(*els)
|
33
48
|
els.each{ |el| @no_map_elements[el] = true }
|
34
49
|
end
|
35
50
|
|
36
51
|
# Returns whether the given element can be handled by the mapper.
|
52
|
+
# @return [bool]
|
37
53
|
def mapped?(element)
|
38
54
|
element = element.name if (element.is_a? Element)
|
39
55
|
element = @model.elements[element]
|
@@ -43,25 +59,28 @@ module Spider; module Model
|
|
43
59
|
return true
|
44
60
|
end
|
45
61
|
|
62
|
+
# @param [Symbol|Element] element
|
63
|
+
# @return [bool] True if the mapper can sort by this element
|
46
64
|
def sortable?(element)
|
47
65
|
element = element.name if (element.is_a? Element)
|
48
66
|
element = @model.elements[element]
|
49
67
|
mapped?(element) || element.attributes[:sortable]
|
50
68
|
end
|
51
69
|
|
70
|
+
# Returns the base type corresponding to a type; see {Model.base_type}
|
71
|
+
# @return [Class] the base type corresponding to type
|
52
72
|
def base_type(type)
|
53
73
|
Spider::Model.base_type(type)
|
54
74
|
end
|
55
75
|
|
56
76
|
# Utility methods
|
57
77
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def execute_action(action, object, params={}) # :nodoc:
|
78
|
+
# Executes the given UnitOfWork action.
|
79
|
+
# @param [Symbol] action
|
80
|
+
# @param [BaseModel] object
|
81
|
+
# @param [Hash] params
|
82
|
+
# @return [void]
|
83
|
+
def execute_action(action, object, params={})
|
65
84
|
case action
|
66
85
|
when :save
|
67
86
|
if params[:force] == :insert
|
@@ -80,7 +99,9 @@ module Spider; module Model
|
|
80
99
|
end
|
81
100
|
end
|
82
101
|
|
83
|
-
# Converts hashes and arrays to QuerySets and BaseModel instances.
|
102
|
+
# Converts hashes and arrays inside an object to QuerySets and BaseModel instances.
|
103
|
+
# @param [BaseModel] obj
|
104
|
+
# @return [void]
|
84
105
|
def normalize(obj)
|
85
106
|
obj.no_autoload do
|
86
107
|
@model.elements.select{ |n, el|
|
@@ -103,18 +124,16 @@ module Spider; module Model
|
|
103
124
|
# Info #
|
104
125
|
#############################################################
|
105
126
|
|
106
|
-
#
|
107
|
-
#
|
127
|
+
# @abstract
|
128
|
+
# Returns true if information to find the given element is accessible to the mapper
|
129
|
+
# (see {DbMapper#have_references?} for an implementation)
|
130
|
+
# @param [Symbol|Element] element
|
131
|
+
# @return [bool] True if the storage has a field to write the element or a reference to the element (primary keys),
|
132
|
+
# false otherwise
|
108
133
|
def have_references?(element)
|
109
134
|
raise MapperError, "Unimplemented"
|
110
135
|
end
|
111
136
|
|
112
|
-
# Returns true if information to find the given element is accessible to the mapper, or to an integrated model's mapper.
|
113
|
-
# (see for example DbMapper#someone_have_references?)
|
114
|
-
def someone_have_references?(element)
|
115
|
-
raise MapperError, "Unimplemented"
|
116
|
-
end
|
117
|
-
|
118
137
|
|
119
138
|
##############################################################
|
120
139
|
# Save (insert and update) #
|
@@ -122,8 +141,12 @@ module Spider; module Model
|
|
122
141
|
|
123
142
|
# This method is called before a save operation, normalizing and preparing the object.
|
124
143
|
# 'mode' can be :insert or :update.
|
125
|
-
# This method is well suited for being overridden
|
126
|
-
#
|
144
|
+
# This method is well suited for being overridden (with {BaseModel.with_mapper},
|
145
|
+
# to add custom preprocessing of the object; just
|
146
|
+
# remember to call super, or use #before_insert and #before_update instead.
|
147
|
+
# @param [BaseModel] obj
|
148
|
+
# @param [Symbol] mode :insert or :update
|
149
|
+
# @return [void]
|
127
150
|
def before_save(obj, mode)
|
128
151
|
obj.trigger(:before_save, mode)
|
129
152
|
normalize(obj)
|
@@ -166,6 +189,10 @@ module Spider; module Model
|
|
166
189
|
end
|
167
190
|
end
|
168
191
|
|
192
|
+
# Saves models that obj's model extends (see {BaseModel.extend_model})
|
193
|
+
# @param [BaseModel] obj
|
194
|
+
# @param [Symbol] mode
|
195
|
+
# @return [void]
|
169
196
|
def save_extended_models(obj, mode)
|
170
197
|
if @model.extended_models
|
171
198
|
@model.extended_models.each do |m, el|
|
@@ -179,6 +206,10 @@ module Spider; module Model
|
|
179
206
|
end
|
180
207
|
end
|
181
208
|
|
209
|
+
# Saves objects integrated in obj (see {BaseModel.integrate})
|
210
|
+
# @param [BaseModel] obj
|
211
|
+
# @param [Symbol] mode
|
212
|
+
# @return [void]
|
182
213
|
def save_integrated(obj, mode)
|
183
214
|
@model.elements_array.select{ |el| !el.integrated? && el.attributes[:integrated_model] && !el.attributes[:extended_model] }.each do |el|
|
184
215
|
sub_obj = obj.get(el)
|
@@ -187,44 +218,71 @@ module Spider; module Model
|
|
187
218
|
end
|
188
219
|
|
189
220
|
# Hook to provide custom preprocessing. The default implementation does nothing.
|
221
|
+
#
|
222
|
+
# If needed, override using {BaseModel.with_mapper}
|
223
|
+
# @param [BaseModel] obj
|
224
|
+
# @return [void]
|
190
225
|
def before_insert(obj)
|
191
226
|
end
|
192
227
|
|
193
228
|
# Hook to provide custom preprocessing. The default implementation does nothing.
|
229
|
+
#
|
230
|
+
# If needed, override using {BaseModel.with_mapper}
|
231
|
+
# @param [BaseModel] obj
|
232
|
+
# @return [void]
|
194
233
|
def before_update(obj)
|
195
234
|
end
|
196
235
|
|
197
236
|
# Hook to provide custom preprocessing. Will be passed a QuerySet. The default implementation does nothing.
|
237
|
+
#
|
238
|
+
# If needed, override using {BaseModel.with_mapper}
|
239
|
+
# @param [QuerySet] objects
|
240
|
+
# @return [void]
|
198
241
|
def before_delete(objects)
|
199
242
|
end
|
200
243
|
|
201
244
|
# Called after a succesful save. 'mode' can be :insert or :update.
|
245
|
+
#
|
246
|
+
# If needed, override using {BaseModel.with_mapper}; but make sure to call super, since this method's
|
247
|
+
# implementation is not empty.
|
248
|
+
# Otherwise, override {#save_done}
|
249
|
+
# @param [BaseModel] obj
|
250
|
+
# @param [Symbol] mode :insert or :update
|
251
|
+
# @return [void]
|
202
252
|
def after_save(obj, mode)
|
203
253
|
obj.reset_modified_elements
|
204
254
|
save_associations(obj, mode)
|
205
255
|
|
206
256
|
end
|
207
257
|
|
208
|
-
# Hook called after a succesful save
|
258
|
+
# Hook called after a succesful save, when the object is not in save mode (see {BaseModel#save_mode}) anymore.
|
259
|
+
#
|
260
|
+
# If needed, override using {BaseModel.with_mapper}
|
261
|
+
# @param [BaseModel] obj
|
262
|
+
# @param [Symbol] mode :insert or :update
|
263
|
+
# @return [void]
|
209
264
|
def save_done(obj, mode)
|
210
265
|
end
|
211
266
|
|
212
267
|
# Hook to provide custom preprocessing. Will be passed a QuerySet. The default implementation does nothing.
|
268
|
+
#
|
269
|
+
# If needed, override using {BaseModel.with_mapper}
|
270
|
+
# @param [QuerySet] objects
|
271
|
+
# @return [void]
|
213
272
|
def after_delete(objects)
|
214
273
|
end
|
215
274
|
|
216
275
|
# Saves the object to the storage.
|
276
|
+
# @param [BaseModel] obj
|
277
|
+
# @param [Model::Request] request Save only elements in the fiven request.
|
278
|
+
# @return [true]
|
217
279
|
def save(obj, request=nil)
|
218
280
|
prev_autoload = obj.autoload?
|
219
281
|
obj.save_mode
|
220
282
|
storage.in_transaction
|
221
283
|
save_mode = determine_save_mode(obj)
|
222
284
|
before_save(obj, save_mode)
|
223
|
-
|
224
|
-
# obj.get(el).save if obj.element_modified?(el)
|
225
|
-
# end
|
226
|
-
|
227
|
-
if (save_mode == :update)
|
285
|
+
if save_mode == :update
|
228
286
|
do_update(obj)
|
229
287
|
else
|
230
288
|
do_insert(obj)
|
@@ -241,6 +299,9 @@ module Spider; module Model
|
|
241
299
|
true
|
242
300
|
end
|
243
301
|
|
302
|
+
# Determines whether the object needs to be inserted or updated.
|
303
|
+
# @param [BaseModel] obj
|
304
|
+
# @return [Symbol] :insert or :update
|
244
305
|
def determine_save_mode(obj)
|
245
306
|
if @model.extended_models && !@model.extended_models.empty?
|
246
307
|
is_insert = false
|
@@ -263,6 +324,8 @@ module Spider; module Model
|
|
263
324
|
|
264
325
|
|
265
326
|
# Elements that are associated to this one externally.
|
327
|
+
# @return [Array] An Array of elements for which the storage does not hold keys (see {#have_references?}),
|
328
|
+
# and which must be associated through other ways
|
266
329
|
def association_elements
|
267
330
|
return [] if Spider::Model.unit_of_work_running?
|
268
331
|
els = @model.elements_array.select{ |el|
|
@@ -271,7 +334,8 @@ module Spider; module Model
|
|
271
334
|
els
|
272
335
|
end
|
273
336
|
|
274
|
-
# Saves
|
337
|
+
# Saves externally associated objects (the ones corresponding to elements returned by #association_elements)
|
338
|
+
# @return [void]
|
275
339
|
def save_associations(obj, mode)
|
276
340
|
association_elements.select{ |el| obj.element_has_value?(el) }.each do |el|
|
277
341
|
save_element_associations(obj, el, mode)
|
@@ -279,6 +343,10 @@ module Spider; module Model
|
|
279
343
|
end
|
280
344
|
|
281
345
|
# Deletes all associations from the given object to the element.
|
346
|
+
# @param [BaseModel] obj
|
347
|
+
# @param [Element] element
|
348
|
+
# @param [BaseModel] associated The currently associated objects
|
349
|
+
# @return [void]
|
282
350
|
def delete_element_associations(obj, element, associated=nil)
|
283
351
|
if element.attributes[:junction]
|
284
352
|
condition = {element.attributes[:reverse] => obj.primary_keys}
|
@@ -307,6 +375,10 @@ module Spider; module Model
|
|
307
375
|
end
|
308
376
|
|
309
377
|
# Saves the associations from the given object to the element.
|
378
|
+
# @param [BaseModel] obj
|
379
|
+
# @param [Element] element
|
380
|
+
# @param [Symbol] mode :insert or :update
|
381
|
+
# @return [void]
|
310
382
|
def save_element_associations(obj, element, mode)
|
311
383
|
our_element = element.attributes[:reverse]
|
312
384
|
val = obj.get(element)
|
@@ -380,6 +452,8 @@ module Spider; module Model
|
|
380
452
|
end
|
381
453
|
|
382
454
|
# Saves the given object and all objects reachable from it.
|
455
|
+
# @param [BaseModel] root The root object
|
456
|
+
# @return [void]
|
383
457
|
def save_all(root)
|
384
458
|
UnitOfWork.new do |uow|
|
385
459
|
uow.add(root)
|
@@ -388,6 +462,8 @@ module Spider; module Model
|
|
388
462
|
end
|
389
463
|
|
390
464
|
# Inserts the object in the storage.
|
465
|
+
# @param [BaseModel] obj
|
466
|
+
# @return [void]
|
391
467
|
def insert(obj)
|
392
468
|
prev_autoload = obj.save_mode()
|
393
469
|
storage.in_transaction
|
@@ -399,6 +475,8 @@ module Spider; module Model
|
|
399
475
|
end
|
400
476
|
|
401
477
|
# Updates the object in the storage.
|
478
|
+
# @param [BaseModel] obj
|
479
|
+
# @return [void]
|
402
480
|
def update(obj)
|
403
481
|
prev_autoload = obj.save_mode()
|
404
482
|
storage.in_transaction
|
@@ -409,15 +487,23 @@ module Spider; module Model
|
|
409
487
|
obj.autoload = prev_autoload
|
410
488
|
end
|
411
489
|
|
412
|
-
#
|
413
|
-
|
490
|
+
# @abstract
|
491
|
+
# Executes a mass update for given condition.
|
492
|
+
# @param [Hash] values
|
493
|
+
# @param [Condition] condition
|
494
|
+
# @return [nil]
|
495
|
+
def bulk_update(values, conditon)
|
414
496
|
end
|
415
497
|
|
416
498
|
# Deletes an object, or objects according to a condition.
|
417
499
|
# Will not delete with null condition (i.e. all objects) unless force is true
|
418
|
-
#
|
419
|
-
#
|
420
|
-
#
|
500
|
+
#
|
501
|
+
# @param [BaseModel|Condition] obj_or_condition
|
502
|
+
# @param [bool] force
|
503
|
+
# @param [Hash] options Available options:
|
504
|
+
# * :keep_single_reverse: don't delete associations that have a single reverse.
|
505
|
+
# Useful when an object will be re-inserted with the same keys.
|
506
|
+
# @return [void]
|
421
507
|
def delete(obj_or_condition, force=false, options={})
|
422
508
|
|
423
509
|
def prepare_delete_condition(obj)
|
@@ -484,56 +570,42 @@ module Spider; module Model
|
|
484
570
|
end
|
485
571
|
|
486
572
|
# Deletes all objects from the storage.
|
573
|
+
# @return [void]
|
487
574
|
def delete_all!
|
488
575
|
all = @model.all
|
489
576
|
#all.fetch_window = 100
|
490
577
|
delete(all, true)
|
491
578
|
end
|
492
579
|
|
493
|
-
|
494
|
-
raise MapperError, "Unimplemented"
|
495
|
-
end
|
496
|
-
|
497
|
-
# Actual interaction with the storage. May be implemented by subclasses.
|
498
|
-
def do_delete(obj, force=false)
|
499
|
-
raise MapperError, "Unimplemented"
|
500
|
-
end
|
501
|
-
|
502
|
-
# Actual interaction with the storage. May be implemented by subclasses.
|
503
|
-
def do_insert(obj)
|
504
|
-
raise MapperError, "Unimplemented"
|
505
|
-
end
|
506
|
-
|
507
|
-
# Actual interaction with the storage. May be implemented by subclasses.
|
508
|
-
def do_update(obj)
|
509
|
-
raise MapperError, "Unimplemented"
|
510
|
-
end
|
511
|
-
|
512
|
-
# Actual interaction with the storage. May be implemented by subclasses.
|
513
|
-
def lock(obj=nil, mode=:exclusive)
|
514
|
-
raise MapperError, "Unimplemented"
|
515
|
-
end
|
516
|
-
|
517
|
-
# Actual interaction with the storage. May be implemented by subclasses.
|
518
|
-
def sequence_next(name)
|
519
|
-
raise MapperError, "Unimplemented"
|
520
|
-
end
|
580
|
+
|
521
581
|
|
522
582
|
##############################################################
|
523
583
|
# Load (and find) #
|
524
584
|
##############################################################
|
525
585
|
|
526
586
|
# Loads an element. Other elements may be loaded as well, according to lazy groups.
|
587
|
+
# @param [QuerySet] objects Objects for which to load given element
|
588
|
+
# @param [Element] element
|
589
|
+
# @return [QuerySet]
|
527
590
|
def load_element(objects, element)
|
528
591
|
load(objects, Query.new(nil, [element.name]))
|
529
592
|
end
|
530
593
|
|
531
594
|
# Loads only the given element, ignoring lazy groups.
|
595
|
+
# @param [QuerySet] objects Objects for which to load given element
|
596
|
+
# @param [Element] element
|
597
|
+
# @return [QuerySet]
|
532
598
|
def load_element!(objects, element)
|
533
599
|
load(objects, Query.new(nil, [element.name]), :no_expand_request => true)
|
534
600
|
end
|
535
601
|
|
536
602
|
# Loads elements of given objects according to query.request.
|
603
|
+
#
|
604
|
+
# See also {#find}
|
605
|
+
# @param [QuerySet] objects Objects to expand
|
606
|
+
# @param [Query] query
|
607
|
+
# @param [Hash] options
|
608
|
+
# @return [QuerySet]
|
537
609
|
def load(objects, query, options={})
|
538
610
|
objects = queryset_siblings(objects) unless objects.is_a?(QuerySet)
|
539
611
|
request = query.request
|
@@ -545,6 +617,12 @@ module Spider; module Model
|
|
545
617
|
end
|
546
618
|
|
547
619
|
# Finds objects according to a query, merging the results into a query_set if given.
|
620
|
+
#
|
621
|
+
# @param [Query] query
|
622
|
+
# @param [QuerySet] query_set QuerySet to merge results into, if given
|
623
|
+
# @param [Hash] options Options can be:
|
624
|
+
# * :no_expand_request: don't expand request using lazy loading groups
|
625
|
+
# @return [QuerySet]
|
548
626
|
def find(query, query_set=nil, options={})
|
549
627
|
set = nil
|
550
628
|
Spider::Model.with_identity_mapper do |im|
|
@@ -621,9 +699,166 @@ module Spider; module Model
|
|
621
699
|
end
|
622
700
|
return set
|
623
701
|
end
|
702
|
+
|
703
|
+
# Does a count query on the storage for given condition
|
704
|
+
# @param [Condition]
|
705
|
+
# @return [Fixnum]
|
706
|
+
def count(condition)
|
707
|
+
query = Query.new(condition)
|
708
|
+
result = fetch(query)
|
709
|
+
return result.length
|
710
|
+
end
|
624
711
|
|
712
|
+
|
713
|
+
|
714
|
+
# Returns the siblings, if any, of the object, in its ancestor QuerySet.
|
715
|
+
#
|
716
|
+
# Siblings are objects in the same branch of the object tree.
|
717
|
+
#
|
718
|
+
# This method is used to load related data, avoiding N+1 queries
|
719
|
+
# @param [BaseModel|QuerySet] obj
|
720
|
+
# @return [QuerySet]
|
721
|
+
def queryset_siblings(obj)
|
722
|
+
return QuerySet.new(@model, obj) unless obj._parent
|
723
|
+
orig_obj = obj
|
724
|
+
path = []
|
725
|
+
seen = {obj => true}
|
726
|
+
while (obj._parent && !seen[obj._parent])
|
727
|
+
path.unshift(obj._parent_element) if (obj._parent_element) # otherwise it's a query set
|
728
|
+
obj = obj._parent
|
729
|
+
seen[obj] = true
|
730
|
+
end
|
731
|
+
res = path.empty? ? obj : obj.all_children(path)
|
732
|
+
raise RuntimeError, "Broken object path" if (obj && !path.empty? && res.length < 1)
|
733
|
+
res = QuerySet.new(@model, res) unless res.is_a?(QuerySet)
|
734
|
+
res = res.select{ |obj| obj.primary_keys_set? }
|
735
|
+
return res
|
736
|
+
end
|
625
737
|
|
626
|
-
|
738
|
+
# Prepares a value going to be bound to an insert or update statement
|
739
|
+
# @param [Class] type Value's type
|
740
|
+
# @param [Object] value
|
741
|
+
# @param [Symbol] save_mode :insert, :update, or generically :save
|
742
|
+
# @return [Object]
|
743
|
+
def map_save_value(type, value, save_mode=:save)
|
744
|
+
value = map_value(type, value, :save)
|
745
|
+
return @storage.value_for_save(Model.simplify_type(type), value, save_mode)
|
746
|
+
end
|
747
|
+
|
748
|
+
# Prepares a value for a condition.
|
749
|
+
# @param [Class] type Value's type
|
750
|
+
# @param [Object] value
|
751
|
+
# @param [Symbol] save_mode :insert, :update, or generically :save
|
752
|
+
# @return [Object]
|
753
|
+
def map_condition_value(type, value)
|
754
|
+
if value.is_a?(Range)
|
755
|
+
return Range.new(map_condition_value(type, value.first), map_condition_value(type, value.last))
|
756
|
+
end
|
757
|
+
return value if ( type.class == Class && type.subclass_of?(Spider::Model::BaseModel) )
|
758
|
+
value = map_value(type, value, :condition)
|
759
|
+
return @storage.value_for_condition(Model.simplify_type(type), value)
|
760
|
+
end
|
761
|
+
|
762
|
+
# Calls {Storage#value_to_mapper}. It is repeated in Mapper for easier overriding.
|
763
|
+
# @param [Class] type Value's type
|
764
|
+
# @param [Object] value
|
765
|
+
# @return [Object]
|
766
|
+
def storage_value_to_mapper(type, value)
|
767
|
+
storage.value_to_mapper(type, value)
|
768
|
+
end
|
769
|
+
|
770
|
+
|
771
|
+
# Converts a value into one that is accepted by the storage.
|
772
|
+
# @param [Class] type Value's type
|
773
|
+
# @param [Object] value
|
774
|
+
# @param [Symbol] save_mode :insert, :update, or generically :save
|
775
|
+
# @return [Object]
|
776
|
+
def map_value(type, value, mode=nil)
|
777
|
+
return value if value.nil?
|
778
|
+
if type == Spider::DataTypes::PK
|
779
|
+
value = value.obj if value.is_a?(Spider::DataTypes::PK)
|
780
|
+
elsif type < Spider::DataType
|
781
|
+
value = type.from_value(value) unless value.is_a?(type)
|
782
|
+
value = value.map(self.type)
|
783
|
+
elsif type.class == Class && type.subclass_of?(Spider::Model::BaseModel)
|
784
|
+
value = type.primary_keys.map{ |key| value.send(key.name) }
|
785
|
+
end
|
786
|
+
value
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
# Converts a storage value back to the corresponding base type or DataType.
|
791
|
+
# @param [Class] type Value's type
|
792
|
+
# @param [Object] value
|
793
|
+
# @return [Object]
|
794
|
+
def map_back_value(type, value)
|
795
|
+
value = value[0] if value.class == Array
|
796
|
+
value = storage_value_to_mapper(Model.simplify_type(type), value)
|
797
|
+
|
798
|
+
if type <= Spider::DataTypes::PK
|
799
|
+
value = value.is_a?(Spider::DataTypes::PK) ? value.obj : value
|
800
|
+
elsif type < Spider::DataType && type.maps_back_to
|
801
|
+
type = type.maps_back_to
|
802
|
+
end
|
803
|
+
case type.name
|
804
|
+
when 'Fixnum'
|
805
|
+
return value ? value.to_i : nil
|
806
|
+
when 'Float'
|
807
|
+
return value ? value.to_f : nil
|
808
|
+
end
|
809
|
+
return nil unless value
|
810
|
+
case type.name
|
811
|
+
when 'Date', 'DateTime'
|
812
|
+
return type.parse(value) unless value.is_a?(Date)
|
813
|
+
end
|
814
|
+
if type < Spider::DataType && type.force_wrap?
|
815
|
+
value = type.from_value(value)
|
816
|
+
end
|
817
|
+
return value
|
818
|
+
end
|
819
|
+
|
820
|
+
# Unit of work
|
821
|
+
|
822
|
+
# @abstract
|
823
|
+
# Returns task dependecies for the UnitOfWork. May be implemented by subclasses.
|
824
|
+
# @param [MapperTask] task
|
825
|
+
# @return [Array] Dependencies for the task
|
826
|
+
def get_dependencies(task)
|
827
|
+
return []
|
828
|
+
end
|
829
|
+
|
830
|
+
# @param [BaseModel] obj
|
831
|
+
# @param [Symbol] action UnitOfWork action
|
832
|
+
# @return [Array] Objects to be added to the UnitOfWork when obj is added
|
833
|
+
def children_for_unit_of_work(obj, action)
|
834
|
+
children = []
|
835
|
+
obj.class.elements_array.each do |el|
|
836
|
+
next unless obj.element_has_value?(el)
|
837
|
+
next unless el.model?
|
838
|
+
next unless obj.element_modified?(el)
|
839
|
+
val = obj.get(el)
|
840
|
+
next unless val.modified?
|
841
|
+
children << val
|
842
|
+
end
|
843
|
+
children
|
844
|
+
end
|
845
|
+
|
846
|
+
protected
|
847
|
+
|
848
|
+
# @return [Array] An array of all elements which are handled by the mapper
|
849
|
+
def map_elements
|
850
|
+
@model.elements_array.select{ |el| !@no_map_elements[el.name] }
|
851
|
+
end
|
852
|
+
|
853
|
+
|
854
|
+
# Given a QuerySet and a model object, searches for an object with the same keys
|
855
|
+
# in the QuerySet; if found, merges the object, otherwise, adds the object to the set
|
856
|
+
#
|
857
|
+
# @param [QuerySet] set
|
858
|
+
# @param [BaseModel] obj Object to merge
|
859
|
+
# @param [Model::Request] request Only elements in request will be merged
|
860
|
+
# @return [void]
|
861
|
+
def merge_object(set, obj, request)
|
627
862
|
search = {}
|
628
863
|
@model.primary_keys.each{ |k| search[k.name] = obj.get(k.name) }
|
629
864
|
obj_res = set.find(search) # FIXME: find a better way
|
@@ -637,8 +872,14 @@ module Spider; module Model
|
|
637
872
|
obj
|
638
873
|
end
|
639
874
|
end
|
640
|
-
|
641
|
-
|
875
|
+
|
876
|
+
# Like #find, but also retrieves instances of the object's superclass (assuming it is a BaseModel as well)
|
877
|
+
#
|
878
|
+
# @param [Query] query
|
879
|
+
# @param [QuerySet] set
|
880
|
+
# @param [Hash] options
|
881
|
+
# @return [QuerySet]
|
882
|
+
def find_with_superclass(query, set=nil, options={})
|
642
883
|
q = query.clone
|
643
884
|
polym_request = Request.new
|
644
885
|
polym_condition = Condition.new
|
@@ -656,26 +897,12 @@ module Spider; module Model
|
|
656
897
|
end
|
657
898
|
return set
|
658
899
|
end
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
end
|
666
|
-
|
667
|
-
# Actual interaction with the storage. Should be implemented by subclasses.
|
668
|
-
def fetch(query)
|
669
|
-
raise MapperError, "Unimplemented"
|
670
|
-
end
|
671
|
-
|
672
|
-
|
673
|
-
# Transforms a Storage result into an object. Should be implemented by subclasses.
|
674
|
-
def map(request, result, obj_or_model)
|
675
|
-
raise MapperError, "Unimplemented"
|
676
|
-
end
|
677
|
-
|
678
|
-
# Loads external elements, according to query, and merges them into an object or a QuerySet
|
900
|
+
|
901
|
+
|
902
|
+
# Loads external elements, according to query, and merges them into an object or a QuerySet
|
903
|
+
# @param [QuerySet|BaseModel] objects
|
904
|
+
# @param [Query] query
|
905
|
+
# @return [QuerySet]
|
679
906
|
def get_external(objects, query)
|
680
907
|
objects = queryset_siblings(objects) unless objects.is_a?(QuerySet)
|
681
908
|
return objects if objects.length < 1
|
@@ -711,6 +938,10 @@ module Spider; module Model
|
|
711
938
|
end
|
712
939
|
|
713
940
|
# Loads an external element, according to query, and merges the result into an object or QuerySet.
|
941
|
+
# @param [Element] element
|
942
|
+
# @param [Query] query
|
943
|
+
# @param [QuerySet] result
|
944
|
+
# @return [QuerySet]
|
714
945
|
def get_external_element(element, query, objects)
|
715
946
|
# Spider::Logger.debug("Getting external element #{element.name} for #{@model}")
|
716
947
|
return load_element(objects, element) if have_references?(element)
|
@@ -733,6 +964,10 @@ module Spider; module Model
|
|
733
964
|
|
734
965
|
# Given the results of a query for an element, and a set of objects, associates
|
735
966
|
# the result with the corresponding objects.
|
967
|
+
# @param [Element] element
|
968
|
+
# @param [QuerySet] objects
|
969
|
+
# @param [QuerySet] result
|
970
|
+
# @return [QuerySet]
|
736
971
|
def associate_external(element, objects, result)
|
737
972
|
# result.reindex
|
738
973
|
objects.element_loaded(element.name)
|
@@ -751,92 +986,15 @@ module Spider; module Model
|
|
751
986
|
end
|
752
987
|
return objects
|
753
988
|
end
|
754
|
-
|
755
|
-
# Returns the siblings, if any, of the object, in its ancestor QuerySet.
|
756
|
-
def queryset_siblings(obj)
|
757
|
-
return QuerySet.new(@model, obj) unless obj._parent
|
758
|
-
orig_obj = obj
|
759
|
-
path = []
|
760
|
-
seen = {obj => true}
|
761
|
-
while (obj._parent && !seen[obj._parent])
|
762
|
-
path.unshift(obj._parent_element) if (obj._parent_element) # otherwise it's a query set
|
763
|
-
obj = obj._parent
|
764
|
-
seen[obj] = true
|
765
|
-
end
|
766
|
-
res = path.empty? ? obj : obj.all_children(path)
|
767
|
-
raise RuntimeError, "Broken object path" if (obj && !path.empty? && res.length < 1)
|
768
|
-
res = QuerySet.new(@model, res) unless res.is_a?(QuerySet)
|
769
|
-
res = res.select{ |obj| obj.primary_keys_set? }
|
770
|
-
return res
|
771
|
-
end
|
772
|
-
|
773
|
-
# Prepares a value going to be bound to an insert or update statement
|
774
|
-
def map_save_value(type, value, save_mode=:save)
|
775
|
-
value = map_value(type, value, :save)
|
776
|
-
return @storage.value_for_save(Model.simplify_type(type), value, save_mode)
|
777
|
-
end
|
778
|
-
|
779
|
-
# Prepares a value for a condition.
|
780
|
-
def map_condition_value(type, value)
|
781
|
-
if value.is_a?(Range)
|
782
|
-
return Range.new(map_condition_value(type, value.first), map_condition_value(type, value.last))
|
783
|
-
end
|
784
|
-
return value if ( type.class == Class && type.subclass_of?(Spider::Model::BaseModel) )
|
785
|
-
value = map_value(type, value, :condition)
|
786
|
-
return @storage.value_for_condition(Model.simplify_type(type), value)
|
787
|
-
end
|
788
|
-
|
789
|
-
def storage_value_to_mapper(type, value)
|
790
|
-
storage.value_to_mapper(type, value)
|
791
|
-
end
|
792
|
-
|
793
|
-
|
794
|
-
# Converts a value in one accepted by the storage.
|
795
|
-
def map_value(type, value, mode=nil)
|
796
|
-
return value if value.nil?
|
797
|
-
if type == Spider::DataTypes::PK
|
798
|
-
value = value.obj if value.is_a?(Spider::DataTypes::PK)
|
799
|
-
elsif type < Spider::DataType
|
800
|
-
value = type.from_value(value) unless value.is_a?(type)
|
801
|
-
value = value.map(self.type)
|
802
|
-
elsif type.class == Class && type.subclass_of?(Spider::Model::BaseModel)
|
803
|
-
value = type.primary_keys.map{ |key| value.send(key.name) }
|
804
|
-
end
|
805
|
-
value
|
806
|
-
end
|
807
|
-
|
808
|
-
|
809
|
-
# Converts a storage value back to the corresponding base type or DataType.
|
810
|
-
def map_back_value(type, value)
|
811
|
-
value = value[0] if value.class == Array
|
812
|
-
value = storage_value_to_mapper(Model.simplify_type(type), value)
|
813
989
|
|
814
|
-
if type <= Spider::DataTypes::PK
|
815
|
-
value = value.is_a?(Spider::DataTypes::PK) ? value.obj : value
|
816
|
-
elsif type < Spider::DataType && type.maps_back_to
|
817
|
-
type = type.maps_back_to
|
818
|
-
end
|
819
|
-
case type.name
|
820
|
-
when 'Fixnum'
|
821
|
-
return value ? value.to_i : nil
|
822
|
-
when 'Float'
|
823
|
-
return value ? value.to_f : nil
|
824
|
-
end
|
825
|
-
return nil unless value
|
826
|
-
case type.name
|
827
|
-
when 'Date', 'DateTime'
|
828
|
-
return type.parse(value) unless value.is_a?(Date)
|
829
|
-
end
|
830
|
-
if type < Spider::DataType && type.force_wrap?
|
831
|
-
value = type.from_value(value)
|
832
|
-
end
|
833
|
-
return value
|
834
|
-
end
|
835
|
-
|
836
990
|
##############################################################
|
837
991
|
# Strategy #
|
838
992
|
##############################################################
|
839
993
|
|
994
|
+
# Ensures a Query is ready for being used by the mapper
|
995
|
+
# @param [Query] query
|
996
|
+
# @param [BaseModel] obj Optional object; if passed, will be used to ensure the Query Request corresponds to the object
|
997
|
+
# @return [Query] The prepared query
|
840
998
|
def prepare_query(query, obj=nil)
|
841
999
|
if (query.request.polymorphs?)
|
842
1000
|
conds = split_condition_polymorphs(query.condition, query.request.polymorphs.keys)
|
@@ -852,6 +1010,11 @@ module Spider; module Model
|
|
852
1010
|
return query
|
853
1011
|
end
|
854
1012
|
|
1013
|
+
# Helper method to split conditions for polymorphic elements
|
1014
|
+
# into the correct classes
|
1015
|
+
# @param [Condition] condition
|
1016
|
+
# @param [Array] polymorphs Array of polymorphic model classes
|
1017
|
+
# @return [Array] An array of conditions
|
855
1018
|
def split_condition_polymorphs(condition, polymorphs)
|
856
1019
|
conditions = {}
|
857
1020
|
return conditions if condition.polymorph && polymorphs.include?(condition.polymorph)
|
@@ -884,6 +1047,9 @@ module Spider; module Model
|
|
884
1047
|
|
885
1048
|
|
886
1049
|
# Normalizes a request.
|
1050
|
+
# @param [Request] request
|
1051
|
+
# @param [BaseModel] obj
|
1052
|
+
# @return [void]
|
887
1053
|
def prepare_query_request(request, obj=nil)
|
888
1054
|
@model.primary_keys.each do |key|
|
889
1055
|
request[key] = true
|
@@ -900,7 +1066,11 @@ module Spider; module Model
|
|
900
1066
|
new_requests.each{ |r| request.request(r) }
|
901
1067
|
end
|
902
1068
|
|
903
|
-
# Adds lazy groups to request.
|
1069
|
+
# Adds lazy groups to request. That is, load more data than was requested, to avoid making more
|
1070
|
+
# trips to the storage.
|
1071
|
+
# @param [Request] request
|
1072
|
+
# @param [BaseModel] obj Optional model instance
|
1073
|
+
# @return [void]
|
904
1074
|
def expand_request(request, obj=nil)
|
905
1075
|
lazy_groups = []
|
906
1076
|
request.each do |k, v|
|
@@ -924,6 +1094,8 @@ module Spider; module Model
|
|
924
1094
|
end
|
925
1095
|
|
926
1096
|
# Preprocessing of the condition
|
1097
|
+
# @param [Condition] condition
|
1098
|
+
# @return [Condition] The preprocessed condition
|
927
1099
|
def preprocess_condition(condition)
|
928
1100
|
model = condition.polymorph ? condition.polymorph : @model
|
929
1101
|
condition.simplify
|
@@ -1044,25 +1216,86 @@ module Spider; module Model
|
|
1044
1216
|
end
|
1045
1217
|
return condition
|
1046
1218
|
end
|
1219
|
+
|
1220
|
+
# @abstract
|
1221
|
+
# Returns true if information to find the given element is accessible to the mapper, or to an integrated model's mapper
|
1222
|
+
# (see also {#have_references?}, and {DbMapper#someone_have_references?} for an implementation).
|
1223
|
+
#
|
1224
|
+
# @param [Symbol|Element] element
|
1225
|
+
# @return [bool] True if this mapper, or an integrated model's mapper, has references, false otherwise.
|
1226
|
+
def someone_have_references?(element)
|
1227
|
+
raise MapperError, "Unimplemented"
|
1228
|
+
end
|
1229
|
+
|
1230
|
+
# Abstract methods
|
1231
|
+
|
1232
|
+
# @abstract
|
1233
|
+
# Deletes all data associated to the model from the storage
|
1234
|
+
# @return [void]
|
1235
|
+
def truncate!
|
1236
|
+
raise MapperError, "Unimplemented"
|
1237
|
+
end
|
1047
1238
|
|
1048
|
-
#
|
1049
|
-
|
1050
|
-
|
1239
|
+
# @abstract
|
1240
|
+
# Actual interaction with the storage. May be implemented by subclasses.
|
1241
|
+
# @return [void]
|
1242
|
+
def do_delete(obj, force=false)
|
1243
|
+
raise MapperError, "Unimplemented"
|
1051
1244
|
end
|
1052
1245
|
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1246
|
+
# @abstract
|
1247
|
+
# Actual interaction with the storage. May be implemented by subclasses.
|
1248
|
+
# @return [void]
|
1249
|
+
def do_insert(obj)
|
1250
|
+
raise MapperError, "Unimplemented"
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
# @abstract
|
1254
|
+
# Actual interaction with the storage. May be implemented by subclasses.
|
1255
|
+
# @return [void]
|
1256
|
+
def do_update(obj)
|
1257
|
+
raise MapperError, "Unimplemented"
|
1064
1258
|
end
|
1065
1259
|
|
1260
|
+
# @abstract
|
1261
|
+
# Actual interaction with the storage. May be implemented by subclasses.
|
1262
|
+
# @return [void]
|
1263
|
+
def lock(obj=nil, mode=:exclusive)
|
1264
|
+
raise MapperError, "Unimplemented"
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
# @abstract
|
1268
|
+
# Actual interaction with the storage. May be implemented by subclasses.
|
1269
|
+
# @param [Symbol] name
|
1270
|
+
# @return [void]
|
1271
|
+
def sequence_next(name)
|
1272
|
+
raise MapperError, "Unimplemented"
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
# @abstract
|
1276
|
+
# Actual interaction with the storage. Should be implemented by subclasses.
|
1277
|
+
# @param [Query]
|
1278
|
+
# @return [QuerySet]
|
1279
|
+
def fetch(query)
|
1280
|
+
raise MapperError, "Unimplemented"
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
# @abstract
|
1284
|
+
# Transforms a Storage result into an object. Should be implemented by subclasses.
|
1285
|
+
# @return [BaseModel]
|
1286
|
+
def map(request, result, obj_or_model)
|
1287
|
+
raise MapperError, "Unimplemented"
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
# @abstract
|
1291
|
+
# @param [Element|Symbol] element
|
1292
|
+
# @param [Condition]
|
1293
|
+
# @return [Fixnum] The max value for an element
|
1294
|
+
def max(element, condition=nil)
|
1295
|
+
raise "Unimplemented"
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
|
1066
1299
|
|
1067
1300
|
end
|
1068
1301
|
|
@@ -1070,10 +1303,21 @@ module Spider; module Model
|
|
1070
1303
|
# MapperTask #
|
1071
1304
|
##############################################################
|
1072
1305
|
|
1073
|
-
# The MapperTask is used by the UnitOfWork.
|
1306
|
+
# The MapperTask is used by the UnitOfWork. It represents an action that needs to be done,
|
1307
|
+
# and allows to specify dependences between tasks
|
1074
1308
|
class MapperTask
|
1075
|
-
|
1309
|
+
# @return [Array] Array of MapperTasks this one depends on
|
1310
|
+
attr_reader :dependencies
|
1311
|
+
# @return [BaseModel] The task's subject
|
1312
|
+
attr_reader :object
|
1313
|
+
# @return [Symbol] The task's action
|
1314
|
+
attr_reader :action
|
1315
|
+
# @return [Hash] Params for the task
|
1316
|
+
attr_reader :params
|
1076
1317
|
|
1318
|
+
# @param [BaseModel] object The task's subject
|
1319
|
+
# @param [Symbol] action
|
1320
|
+
# @param [Hash] params
|
1077
1321
|
def initialize(object, action, params={})
|
1078
1322
|
@object = object
|
1079
1323
|
@action = action
|
@@ -1081,17 +1325,23 @@ module Spider; module Model
|
|
1081
1325
|
@dependencies = []
|
1082
1326
|
end
|
1083
1327
|
|
1328
|
+
# Addes a dependency to the Task
|
1329
|
+
# @param [MapperTask] task
|
1330
|
+
# @return [void]
|
1084
1331
|
def <<(task)
|
1085
1332
|
@dependencies << task
|
1086
1333
|
end
|
1087
1334
|
|
1088
|
-
|
1335
|
+
# Makes the objects' mapper run the task
|
1336
|
+
# @return [void]
|
1337
|
+
def execute
|
1089
1338
|
debug_str = "Executing #{@action} on #{@object.inspect}"
|
1090
1339
|
debug_str += " (#{@params.inspect})" unless @params.empty?
|
1091
1340
|
Spider::Logger.debug debug_str
|
1092
1341
|
@object.mapper.execute_action(@action, @object, @params)
|
1093
1342
|
end
|
1094
1343
|
|
1344
|
+
# @return [bool] True if the other task has the same object, action and params, false otherwise
|
1095
1345
|
def eql?(task)
|
1096
1346
|
return false unless task.class == self.class
|
1097
1347
|
return false unless (task.object == self.object && task.action == self.action)
|
@@ -1101,10 +1351,12 @@ module Spider; module Model
|
|
1101
1351
|
return true
|
1102
1352
|
end
|
1103
1353
|
|
1354
|
+
# @return [String] Hash for keying
|
1104
1355
|
def hash
|
1105
1356
|
return @object.hash + @action.hash
|
1106
1357
|
end
|
1107
1358
|
|
1359
|
+
# @return [bool] Same as #eql?
|
1108
1360
|
def ===(task)
|
1109
1361
|
return eql?(task)
|
1110
1362
|
end
|
@@ -1113,6 +1365,7 @@ module Spider; module Model
|
|
1113
1365
|
# "#{@action} on #{@object} (#{object.class})\n"
|
1114
1366
|
# end
|
1115
1367
|
|
1368
|
+
# @return [String] A textual representation of the Task
|
1116
1369
|
def inspect
|
1117
1370
|
if (@action && @object)
|
1118
1371
|
str = "#{@action} on #{@object}##{@object.object_id} (#{object.class})"
|
@@ -1132,25 +1385,16 @@ module Spider; module Model
|
|
1132
1385
|
|
1133
1386
|
end
|
1134
1387
|
|
1135
|
-
|
1136
|
-
# Aggregates #
|
1137
|
-
##############################################################
|
1138
|
-
|
1139
|
-
def max(element, condition=nil)
|
1140
|
-
raise "Unimplemented"
|
1141
|
-
end
|
1142
|
-
|
1388
|
+
|
1143
1389
|
|
1144
1390
|
##############################################################
|
1145
1391
|
# Exceptions #
|
1146
1392
|
##############################################################
|
1147
1393
|
|
1148
1394
|
# Generic Mapper error.
|
1149
|
-
|
1150
1395
|
class MapperError < RuntimeError; end
|
1151
1396
|
|
1152
1397
|
# Generic Mapper error regarding an element.
|
1153
|
-
|
1154
1398
|
class MapperElementError < MapperError
|
1155
1399
|
def initialize(element)
|
1156
1400
|
@element = element
|
@@ -1178,14 +1422,13 @@ module Spider; module Model
|
|
1178
1422
|
end
|
1179
1423
|
|
1180
1424
|
# A required element has no value
|
1181
|
-
|
1182
1425
|
RequiredError = MapperElementError.create_subclass(_("Element %s is required"))
|
1183
1426
|
|
1184
1427
|
# An uniqueness constraint has been violated.
|
1185
|
-
|
1186
1428
|
NotUniqueError = MapperElementError.create_subclass(_("Another item with the same %s is already present"))
|
1187
1429
|
|
1188
1430
|
|
1431
|
+
# Helper module to hold methods overridden by {BaseModel.with_mapper}
|
1189
1432
|
module MapperIncludeModule
|
1190
1433
|
|
1191
1434
|
def self.included(mod)
|