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.
Files changed (74) hide show
  1. data/CHANGELOG +10 -1
  2. data/README.rdoc +1 -1
  3. data/VERSION +1 -1
  4. data/apps/config_editor/_init.rb +1 -2
  5. data/apps/config_editor/controllers/config_editor_controller.rb +1 -7
  6. data/apps/core/admin/controllers/admin_controller.rb +1 -1
  7. data/apps/core/admin/public/css/sass/admin.css +35 -31
  8. data/apps/core/admin/public/sass/admin.scss +6 -1
  9. data/apps/core/components/widgets/crud/crud.shtml +2 -2
  10. data/apps/core/components/widgets/table/table.rb +5 -5
  11. data/apps/core/forms/tags/element_row.erb +15 -10
  12. data/apps/core/forms/widgets/form/form.rb +35 -22
  13. data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +2 -2
  14. data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +2 -2
  15. data/apps/core/forms/widgets/inputs/file_input/file_input.shtml +2 -2
  16. data/apps/core/forms/widgets/inputs/html_area/html_area.shtml +2 -2
  17. data/apps/core/forms/widgets/inputs/input/input.shtml +2 -2
  18. data/apps/core/forms/widgets/inputs/password/password.shtml +2 -2
  19. data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +1 -1
  20. data/apps/core/forms/widgets/inputs/select/select.shtml +2 -2
  21. data/apps/core/forms/widgets/inputs/text/text.shtml +2 -2
  22. data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +2 -2
  23. data/apps/core/forms/widgets/inputs/time_span/time_span.shtml +1 -1
  24. data/blueprints/home/config.ru +8 -0
  25. data/lib/spiderfw/app.rb +416 -224
  26. data/lib/spiderfw/cmd/commands/app.rb +243 -239
  27. data/lib/spiderfw/cmd/commands/cert.rb +421 -417
  28. data/lib/spiderfw/cmd/commands/config.rb +85 -82
  29. data/lib/spiderfw/cmd/commands/console.rb +64 -40
  30. data/lib/spiderfw/cmd/commands/content.rb +29 -25
  31. data/lib/spiderfw/cmd/commands/create.rb +58 -54
  32. data/lib/spiderfw/cmd/commands/model.rb +118 -114
  33. data/lib/spiderfw/cmd/commands/setup.rb +55 -51
  34. data/lib/spiderfw/cmd/commands/test.rb +63 -59
  35. data/lib/spiderfw/cmd/commands/webserver.rb +56 -51
  36. data/lib/spiderfw/config/options/spider.rb +4 -3
  37. data/lib/spiderfw/controller/controller.rb +2 -0
  38. data/lib/spiderfw/controller/http_controller.rb +1 -2
  39. data/lib/spiderfw/controller/mixins/static_content.rb +3 -3
  40. data/lib/spiderfw/controller/mixins/visual.rb +30 -15
  41. data/lib/spiderfw/controller/response.rb +84 -0
  42. data/lib/spiderfw/controller/session/file_session.rb +2 -2
  43. data/lib/spiderfw/http/adapters/rack.rb +12 -13
  44. data/lib/spiderfw/http/server.rb +80 -46
  45. data/lib/spiderfw/i18n/cldr.rb +6 -9
  46. data/lib/spiderfw/model/base_model.rb +103 -23
  47. data/lib/spiderfw/model/condition.rb +110 -25
  48. data/lib/spiderfw/model/mappers/db_mapper.rb +14 -6
  49. data/lib/spiderfw/model/mappers/mapper.rb +440 -197
  50. data/lib/spiderfw/model/model.rb +105 -21
  51. data/lib/spiderfw/model/model_hash.rb +9 -1
  52. data/lib/spiderfw/model/query.rb +50 -9
  53. data/lib/spiderfw/model/query_set.rb +211 -44
  54. data/lib/spiderfw/model/request.rb +28 -21
  55. data/lib/spiderfw/model/storage/base_storage.rb +125 -10
  56. data/lib/spiderfw/model/storage/db/db_storage.rb +7 -4
  57. data/lib/spiderfw/model/storage.rb +8 -1
  58. data/lib/spiderfw/setup/spider_setup_wizard.rb +9 -7
  59. data/lib/spiderfw/spider.rb +270 -43
  60. data/lib/spiderfw/templates/layout.rb +9 -4
  61. data/lib/spiderfw/templates/resources/sass.rb +3 -2
  62. data/lib/spiderfw/templates/template.rb +1 -0
  63. data/lib/spiderfw/utils/annotations.rb +3 -1
  64. data/lib/spiderfw/utils/logger.rb +1 -1
  65. data/lib/spiderfw/utils/monkey/symbol.rb +4 -2
  66. data/lib/spiderfw/utils/shared_store/file_shared_store.rb +2 -2
  67. data/lib/spiderfw/utils/thread_out.rb +3 -1
  68. data/public/css/error_page.css +83 -0
  69. data/public/js/error_page.js +5 -0
  70. data/spider.gemspec +4 -1
  71. data/templates/email/error.erb +9 -0
  72. metadata +28 -12
  73. data/apps/config_editor/widgets/edit_bool/edit_bool.rb +0 -8
  74. data/apps/config_editor/widgets/edit_bool/edit_bool.shtml +0 -5
@@ -2,32 +2,46 @@ require 'spiderfw/model/model_hash'
2
2
 
3
3
  module Spider; module Model
4
4
 
5
- # The Condition behaves like a ModelHash, and as such contains key-value pairs:
5
+ # The Condition behaves like a {ModelHash}, and as such contains key-value pairs:
6
6
  # a simple equality condition can be set with
7
7
  # condition[:element_name] = value
8
8
  # The Condition object also holds comparisons: a comparison different from equality can be set with
9
9
  # condition.set(:element_name, '>', value)
10
10
  # Finally, it contains subconditions, which can be added with
11
11
  # conditions << subcondition
12
- # Subconditions will be created automatically when using #set twice on the same element.
13
- # If you want to change the condition, use #delete and set it again.
12
+ # Subconditions will be created automatically when using {#set} twice on the same element.
13
+ # If you want to change the condition, use {#delete} and set it again.
14
+ #
15
+ # Conditions also support an SQL-like block syntax for setting conditions:
16
+ # books_condition = Condition.new{ |book|
17
+ # ((book.year < 1970) | (book.price > 1000)) & (book.author.name .like 'John')
18
+ # }
14
19
  #
15
20
  # The Condition object, like the Request, doesn't hold a reference to a model; so no check will be made
16
21
  # that the conditions elements are meaningful.
17
22
 
18
23
  class Condition < Hash
19
24
  # The top level conjunction for the Condition (:or or :and; new Conditions are initialized with :or)
25
+ # @return [Symbol] :and | :or
20
26
  attr_accessor :conjunction
21
27
  # Polymorph model: used to tell the mapper the condition is on a subclass of the queried model.
28
+ # @return [Class<BaseModel]
22
29
  attr_accessor :polymorph
23
30
  # An hash of comparisons for each element name
31
+ # @return [Hash]
24
32
  attr_reader :comparisons
25
33
  # An Array of subconditions
34
+ # @return [Array]
26
35
  attr_reader :subconditions
27
- attr_accessor :conjunct # :nodoc: a hack to keep track of which is the last condition in blocks
36
+ # @private
37
+ # A pointer to the last Condition used in condition blocks
38
+ # @return [Condition]
39
+ attr_accessor :conjunct
40
+ # Original hash value assignment
28
41
  alias :hash_set :[]= # :nodoc:
29
42
 
30
43
  # See #ModelHash.get_deep_obj
44
+ # @return [Condition]
31
45
  def get_deep_obj # :nodoc:
32
46
  c = self.class.new
33
47
  c.conjunction = @conjunction
@@ -40,12 +54,18 @@ module Spider; module Model
40
54
  str += Regexp.quote(op)
41
55
  end
42
56
 
57
+ # @private
43
58
  # Regexp to parse comparison operators
44
- def self.comparison_operators_regexp # :nodoc:
59
+ # @return [Regexp]
60
+ def self.comparison_operators_regexp
45
61
  @comparison_operators_regexp
46
62
  end
47
63
 
48
- # Used by and and or methods
64
+ # @private
65
+ # Used by {Condition.and} and {Condition.or} methods
66
+ # @param [Symbol] conjunction
67
+ # @param [Condition] a
68
+ # @param [Condition] b
49
69
  def self.conj(conjunction, a, b) # :nodoc:
50
70
  c = Condition.new
51
71
  c.conjunction = conjunction
@@ -54,7 +74,8 @@ module Spider; module Model
54
74
  end
55
75
 
56
76
  # Instantiates a Condition with :and conjunction.
57
- # See #new for arguments.
77
+ # See {Condition.new} for arguments.
78
+ # @return [Condition]
58
79
  def self.and(*params, &proc)
59
80
  c = self.new(*params, &proc)
60
81
  c.conjunction = :and
@@ -62,7 +83,8 @@ module Spider; module Model
62
83
  end
63
84
 
64
85
  # Instantiates a Condition with :or conjunction.
65
- # See #new for arguments.
86
+ # See {Condition.new} for arguments.
87
+ # @return [Condition]
66
88
  def self.or(*params, &proc)
67
89
  c = self.new(*params, &proc)
68
90
  c.conjunction = :or
@@ -70,7 +92,9 @@ module Spider; module Model
70
92
  end
71
93
 
72
94
  # Instantiates a Condition with no conjunction.
73
- def self.no_conjunction(*params, &proc) # :nodoc:
95
+ # See {Condition.new} for arguments.
96
+ # @return [Condition]
97
+ def self.no_conjunction(*params, &proc)
74
98
  c = self.new(*params, &proc)
75
99
  c.conjunction = nil
76
100
  return c
@@ -101,12 +125,14 @@ module Spider; module Model
101
125
  #
102
126
  # Example:
103
127
  # condition.parse_block{ (element1 == val1) & ( (element2 > 'some string') | (element3 .not nil) ) }
128
+ #
104
129
  # All comparisons must be parenthesized; and/or conjunctions are expressed with a single &/|.
105
130
  #
106
131
  # Available comparisions are: ==, >, <, >=, <=, .not, .like, .ilike (case insensitive like).
107
132
  #
108
133
  # _Note:_ for .like and .ilike comparisons, the SQL '%' syntax must be used.
109
134
  #
135
+ # @param [Proc] proc The condition block
110
136
  def parse_block(&proc)
111
137
  res = nil
112
138
  if proc.arity == 1
@@ -122,6 +148,7 @@ module Spider; module Model
122
148
  @polymorph = res.polymorph
123
149
  end
124
150
 
151
+ # @return [Array] An array of all conditions, expressed as [key, value, comparison]
125
152
  def conditions_array
126
153
  self.hash_clone.map do |k, v|
127
154
  k = k.to_sym if k.respond_to?(:to_sym)
@@ -130,6 +157,7 @@ module Spider; module Model
130
157
  end
131
158
 
132
159
  # Yields each key, value and comparison.
160
+ # @return void
133
161
  def each_with_comparison
134
162
  self.each do |k, v|
135
163
  k = k.to_sym if k.respond_to?(:to_sym)
@@ -138,6 +166,7 @@ module Spider; module Model
138
166
  end
139
167
 
140
168
  # Yields each key, value and comparison, for this condition and its subconditions
169
+ # @return [void]
141
170
  def all_each_with_comparison
142
171
  self.each_with_comparison{ |k, v, c| yield k, v, c }
143
172
  @subconditions.each do |sub|
@@ -145,11 +174,15 @@ module Spider; module Model
145
174
  end
146
175
  end
147
176
 
177
+ # @param [Class<BaseModel] The model on which the Condition is based on.
178
+ # @return [bool] True if the condition has only primary keys for the given model
148
179
  def primary_keys_only?(model)
149
180
  self.select{ |key, value| !model.elements[key] || !model.elements[key].primary_key? }.empty?
150
181
  end
151
182
 
152
183
  # Returns the result of merging the condition with another one (does not modify the original condition).
184
+ # @param [Condition] Condition to merge with
185
+ # @return [Condition] The resulting Condition
153
186
  def +(condition)
154
187
  res = self.clone
155
188
  @subconditions += condition.subconditions
@@ -160,6 +193,8 @@ module Spider; module Model
160
193
  end
161
194
 
162
195
  # Adds a subcondtion.
196
+ # @param [Condition] condition
197
+ # @return [void]
163
198
  def <<(condition)
164
199
  if (condition.class == self.class)
165
200
  @subconditions << condition
@@ -171,7 +206,11 @@ module Spider; module Model
171
206
  end
172
207
  end
173
208
 
174
- # Sets a comparison.
209
+ # Adds a single condition for an element
210
+ # @param [Symbol|String|QueryFuncs::Function] field to add the condition to
211
+ # @param [String] comparison: can be =, <, >, <=, >=, 'between'
212
+ # @param [QuerySet|Array|Range|Object] the value of the condition
213
+ # @return [self]
175
214
  def set(field, comparison, value)
176
215
  if value.is_a?(QuerySet)
177
216
  value = value.to_a
@@ -188,6 +227,7 @@ module Spider; module Model
188
227
  unless field.is_a?(Spider::QueryFuncs::Function)
189
228
  field = field.to_s
190
229
  parts = field.split('.', 2)
230
+ debugger if parts[0].blank?
191
231
  parts[0] = parts[0].to_sym
192
232
  field = field.to_sym unless parts[1]
193
233
  end
@@ -206,10 +246,18 @@ module Spider; module Model
206
246
  end
207
247
 
208
248
  # Sets an equality comparison.
249
+ #
250
+ # Equivalent to {#set}(key, '=', value)
251
+ # @param [String|Symbol|QueryFuncs::Function] key
252
+ # @param [Object] value
253
+ # @return [self]
209
254
  def []=(key, value)
210
255
  set(key, '=', value)
211
256
  end
212
257
 
258
+ # Gets the value of a condition
259
+ # @param [String|Symbol|QueryFuncs::Function] key
260
+ # @return [Object] value
213
261
  def [](key)
214
262
  # TODO: deep
215
263
  key = key.name if key.is_a?(Element)
@@ -218,6 +266,10 @@ module Spider; module Model
218
266
  end
219
267
 
220
268
  # Adds a range condition. This creates a subcondition with >= and <= conditions.
269
+ # @param [String|Symbol|QueryFuncs::Function] key
270
+ # @param [Object] lower
271
+ # @param [Object] upper
272
+ # @return [self]
221
273
  def range(field, lower, upper)
222
274
  c = self.class.and
223
275
  c.set(field, '>=', lower)
@@ -226,6 +278,8 @@ module Spider; module Model
226
278
  end
227
279
 
228
280
  # Deletes a field from the Condition.
281
+ # @param [String|Symbol|QueryFuncs::Function] key
282
+ # @return [Array] A pair containing the deleted value and comparison
229
283
  def delete(field)
230
284
  field = field.to_sym
231
285
  return nil unless self[field] || @comparisons[field]
@@ -235,17 +289,7 @@ module Spider; module Model
235
289
  cur
236
290
  end
237
291
 
238
- # Parses a string comparison.
239
- # TODO: remove?
240
- def parse_comparison(comparison) # :nodoc:
241
- if (comparison =~ Regexp.new("(.+)(#{self.class.comparison_operators_regexp})(.+)"))
242
- val = $3.strip
243
- # strip single and double quotes
244
- val = val[1..-2] if ((val[0] == ?' && val[-1] == ?') || (val[0] == ?" && val[-1] == ?") )
245
- return [$1.strip, $2.strip, val]
246
- end
247
- end
248
-
292
+ # @return [String] A String representation of the condition
249
293
  def inspect
250
294
  str = ""
251
295
  cnt = 0
@@ -269,6 +313,10 @@ module Spider; module Model
269
313
  # Returns the conjunction with another condition.
270
314
  # If this condition already has the required conjunction, the other will be added as a subcondition;
271
315
  # otherwise, a new condition will be created and both will be added to it.
316
+ # @param [Symbol] conjunction :and | :or
317
+ # @param [Condition] other Condition to conjuct to this
318
+ # @param [Proc] proc Block to create the other condition
319
+ # @return [Condition] The resulting Condition (self, or the new Condition created)
272
320
  def conj(conjunction, other=nil, &proc)
273
321
  self.conjunction = conjunction if (!self.conjunction)
274
322
  if (self.conjunction == conjunction)
@@ -287,14 +335,20 @@ module Spider; module Model
287
335
  end
288
336
 
289
337
 
290
- # Joins the condition to another with an "or" conjunction. See #conj.
338
+ # Joins the condition to another with an "or" conjunction. See {#conj}.
339
+ # @param [Condition] other Condition to conjuct to this
340
+ # @param [Proc] proc Block to create the other condition
341
+ # @return [Condition] The resulting Condition (self, or the new Condition created)
291
342
  def or(other=nil, &proc)
292
343
  return conj(:or, other, &proc)
293
344
  end
294
345
  alias :| :or
295
346
  alias :OR :or
296
347
 
297
- # Joins the condition to another with an "and" conjunction. See #conj.
348
+ # Joins the condition to another with an "and" conjunction. See {#conj}.
349
+ # @param [Condition] other Condition to conjuct to this
350
+ # @param [Proc] proc Block to create the other condition
351
+ # @return [Condition] The resulting Condition (self, or the new Condition created)
298
352
  def and(other=nil, &proc)
299
353
  return conj(:and, other, &proc)
300
354
  end
@@ -304,6 +358,7 @@ module Spider; module Model
304
358
  alias :hash_empty? :empty? # :nodoc:
305
359
 
306
360
  # True if there are no comparisons and no subconditions.
361
+ # @return [bool] True if the Condition is empty, false otherwise
307
362
  def empty?
308
363
  return false unless super
309
364
  @subconditions.each do |sub|
@@ -312,17 +367,23 @@ module Spider; module Model
312
367
  return true
313
368
  end
314
369
 
315
- alias :hash_replace :replace # :nodoc:
370
+ # Alias to the original Hash#replace
371
+ alias :hash_replace :replace
316
372
 
317
- # Replace the content of this Condition with another one.
373
+ # Replace all the content of this Condition with another one.
374
+ # @param [Condition] other The other condition
375
+ # @return [self]
318
376
  def replace(other)
319
377
  hash_replace(other)
320
378
  @subconditions = other.subconditions
321
379
  @conjunction = other.conjunction
322
380
  @polymorph = other.polymorph
323
381
  @comparisons = other.comparisons
382
+ self
324
383
  end
325
384
 
385
+ # @param [Condition] other
386
+ # @return [bool] True if the two conditions have the same comparisions and conjunction
326
387
  def ==(other)
327
388
  return false unless other.class == self.class
328
389
  return false unless super
@@ -333,22 +394,28 @@ module Spider; module Model
333
394
  return true
334
395
  end
335
396
 
397
+ # @return [bool] See {#==}
336
398
  def eql?(other)
337
399
  self == other
338
400
  end
339
401
 
402
+ # @return [String] Keying hash
340
403
  def hash
341
404
  ([self.keys, self.values, @comparisons.values, @polymorph] + @subconditions.map{ |s| s.hash}).hash
342
405
  end
343
406
 
344
407
  # Removes duplicate subcondtions.
408
+ # @return self
345
409
  def uniq!
346
410
  @subconditions.uniq!
411
+ self
347
412
  end
348
413
 
414
+ # Alias for Hash#clone
349
415
  alias :hash_clone :clone
350
416
 
351
417
  # Returns a deep copy.
418
+ # @return [Condition] A copy of this Condition
352
419
  def clone
353
420
  c = self.class.new
354
421
  c.conjunction = @conjunction
@@ -363,6 +430,7 @@ module Spider; module Model
363
430
  end
364
431
 
365
432
  # Traverses the tree removing useless conditions.
433
+ # @return [self]
366
434
  def simplify
367
435
  @subconditions.each{ |sub| sub.simplify }
368
436
  if (hash_empty? && @subconditions.length == 1)
@@ -372,6 +440,7 @@ module Spider; module Model
372
440
  return self
373
441
  end
374
442
 
443
+ # @return [Array] An array of polymorphic conditions
375
444
  def polymorphs
376
445
  pol = []
377
446
  pol << @polymorph if @polymorph
@@ -379,6 +448,8 @@ module Spider; module Model
379
448
  end
380
449
 
381
450
  # Returns, from self and subconditions, all those who define a condition for one of the given element names.
451
+ # @param [*Symbol] element_names A list of element names
452
+ # @return [Array] An array of matching conditions
382
453
  def conditions_for(*element_names)
383
454
  conds = []
384
455
  element_names.each do |el|
@@ -390,9 +461,23 @@ module Spider; module Model
390
461
  @subconditions.map{ |s| s.conditions_for(*element_names) }.each{ |c| conds += c }
391
462
  conds
392
463
  end
464
+
465
+ private
466
+
467
+ # Parses a string comparison.
468
+ # TODO: remove?
469
+ def parse_comparison(comparison) # :nodoc:
470
+ if (comparison =~ Regexp.new("(.+)(#{self.class.comparison_operators_regexp})(.+)"))
471
+ val = $3.strip
472
+ # strip single and double quotes
473
+ val = val[1..-2] if ((val[0] == ?' && val[-1] == ?') || (val[0] == ?" && val[-1] == ?") )
474
+ return [$1.strip, $2.strip, val]
475
+ end
476
+ end
393
477
 
394
478
  end
395
479
 
480
+ # Helper module used for Condition block syntax
396
481
  module ConditionMixin # :nodoc:
397
482
 
398
483
  def __el(meth)
@@ -4,15 +4,20 @@ require 'fileutils'
4
4
 
5
5
  module Spider; module Model; module Mappers
6
6
 
7
+ # This is the Mapper subclass for interacting with databases.
7
8
  class DbMapper < Spider::Model::Mapper
8
9
  include Spider::Model::Storage::Db
9
10
 
11
+ # @param [BaseModel] model
12
+ # @param [Storage]
10
13
  def initialize(model, storage)
11
14
  super
12
15
  @type = :db
13
16
  end
14
17
 
15
- def self.write? #:nodoc:
18
+ # Is this mapper writable?
19
+ # @return [true]
20
+ def self.write?
16
21
  true
17
22
  end
18
23
 
@@ -797,7 +802,10 @@ module Spider; module Model; module Mappers
797
802
  # * joins
798
803
  # * final model called
799
804
  # * final element called
800
- def get_deep_join(dotted_element)
805
+ # @param [String] dotted_element: the element to get joins for
806
+ # @param [Symbol] join_type (nil, :inner, :left, :right, :outer): force a join type for all elements
807
+ # @return [Array] [joins, final_model, final_element]
808
+ def get_deep_join(dotted_element, join_type=nil)
801
809
  #return [[], @model, @model.elements[dotted_element]] unless dotted_element.is_a?(String)
802
810
  parts = dotted_element.to_s.split('.').map{ |el| el.to_sym }
803
811
  current_model = @model
@@ -811,17 +819,17 @@ module Spider; module Model; module Mappers
811
819
  raise "Can't find element #{part} in model #{current_model}" unless el
812
820
  next if have_references?(el) && cnt == parts.length
813
821
  if el.integrated?
814
- joins << current_model.mapper.get_join(el.integrated_from)
822
+ joins << current_model.mapper.get_join(el.integrated_from, join_type)
815
823
  current_model = el.integrated_from.type
816
824
  el = current_model.elements[el.integrated_from_element]
817
825
  end
818
826
  if el.model? && can_join?(el)
819
- joins << current_model.mapper.get_join(el)
827
+ joins << current_model.mapper.get_join(el, join_type)
820
828
  current_model = el.model
821
829
  end
822
830
  end
823
831
  while el.integrated? && !have_references?(el)
824
- joins << current_model.mapper.get_join(el.integrated_from)
832
+ joins << current_model.mapper.get_join(el.integrated_from, join_type)
825
833
  # joins << current_model.integrated_from.mapper.get_join(el.integrated_from_element)
826
834
  current_model = el.integrated_from.type
827
835
  el = current_model.elements[el.integrated_from_element]
@@ -871,7 +879,7 @@ module Spider; module Model; module Mappers
871
879
  joins += field.joins
872
880
  fields << [field, direction]
873
881
  else
874
- el_joins, el_model, el = get_deep_join(order_element)
882
+ el_joins, el_model, el = get_deep_join(order_element, :left)
875
883
  if el.model?
876
884
  if el_model.mapper.have_references?(el) || el.model.storage != storage
877
885
  el.model.primary_keys.each do |pk|