spiderfw 0.6.23 → 0.6.24

Sign up to get free protection for your applications and to get access to all the features.
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|