spiderfw 0.5.10 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGELOG +6 -0
  2. data/Rakefile +4 -1
  3. data/VERSION +1 -0
  4. data/apps/core/auth/controllers/login_controller.rb +10 -1
  5. data/apps/core/auth/controllers/mixins/auth_helper.rb +4 -2
  6. data/apps/core/auth/views/login.shtml +1 -1
  7. data/apps/core/components/_init.rb +1 -1
  8. data/apps/core/components/public/css/spider.css +0 -1
  9. data/apps/core/components/public/js/jquery/plugins/jquery.form.js +2 -1
  10. data/apps/core/components/public/js/list.js +14 -126
  11. data/apps/core/components/public/js/plugins/plugin.js +7 -0
  12. data/apps/core/components/public/js/plugins/sortable.js +124 -0
  13. data/apps/core/components/public/js/spider.js +212 -51
  14. data/apps/core/components/widgets/list/list.shtml +1 -0
  15. data/apps/core/components/widgets/table/table.rb +1 -1
  16. data/apps/core/components/widgets/table/table.shtml +3 -3
  17. data/apps/core/components/widgets/tabs/tabs.rb +70 -22
  18. data/apps/core/components/widgets/tabs/tabs.shtml +8 -2
  19. data/apps/core/forms/widgets/inputs/file_input/file_input.rb +4 -1
  20. data/data/locale/it/LC_MESSAGES/cms.mo +0 -0
  21. data/data/locale/it/LC_MESSAGES/spider.mo +0 -0
  22. data/data/locale/it/LC_MESSAGES/spider_files.mo +0 -0
  23. data/data/locale/it/LC_MESSAGES/spider_images.mo +0 -0
  24. data/lib/spiderfw.rb +3 -1
  25. data/lib/spiderfw/config/configuration.rb +3 -1
  26. data/lib/spiderfw/controller/controller.rb +8 -0
  27. data/lib/spiderfw/controller/mixins/static_content.rb +14 -2
  28. data/lib/spiderfw/controller/mixins/visual.rb +25 -25
  29. data/lib/spiderfw/controller/page_controller.rb +3 -0
  30. data/lib/spiderfw/controller/request.rb +4 -1
  31. data/lib/spiderfw/controller/session.rb +7 -6
  32. data/lib/spiderfw/create.rb +10 -1
  33. data/lib/spiderfw/model/base_model.rb +104 -57
  34. data/lib/spiderfw/model/condition.rb +9 -1
  35. data/lib/spiderfw/model/data_type.rb +15 -0
  36. data/lib/spiderfw/model/datatypes/uuid.rb +5 -0
  37. data/lib/spiderfw/model/extended_models/managed.rb +2 -2
  38. data/lib/spiderfw/model/mappers/db_mapper.rb +46 -21
  39. data/lib/spiderfw/model/mappers/mapper.rb +34 -8
  40. data/lib/spiderfw/model/mixins/list.rb +2 -0
  41. data/lib/spiderfw/model/mixins/tree.rb +2 -1
  42. data/lib/spiderfw/model/mixins/versioned.rb +12 -9
  43. data/lib/spiderfw/model/model.rb +72 -0
  44. data/lib/spiderfw/model/query_set.rb +7 -0
  45. data/lib/spiderfw/model/storage/base_storage.rb +5 -1
  46. data/lib/spiderfw/model/storage/db/adapters/mysql.rb +5 -2
  47. data/lib/spiderfw/model/storage/db/adapters/oci8.rb +9 -3
  48. data/lib/spiderfw/model/storage/db/db_storage.rb +8 -5
  49. data/lib/spiderfw/model/sync.rb +12 -6
  50. data/lib/spiderfw/requires.rb +1 -0
  51. data/lib/spiderfw/templates/blocks/parent_context.rb +26 -0
  52. data/lib/spiderfw/templates/blocks/widget.rb +17 -7
  53. data/lib/spiderfw/templates/layout.rb +11 -1
  54. data/lib/spiderfw/templates/template.rb +36 -3
  55. data/lib/spiderfw/templates/template_blocks.rb +36 -26
  56. data/lib/spiderfw/utils/annotations.rb +2 -1
  57. data/lib/spiderfw/utils/thread_out.rb +15 -0
  58. data/lib/spiderfw/widget/widget.rb +58 -16
  59. data/spider.gemspec +3 -0
  60. metadata +143 -48
@@ -39,6 +39,15 @@ module Spider
39
39
  raise RuntimeError, "Folder #{dest_path} already exists" if File.exist?(dest_path)
40
40
  FileUtils.mkdir_p(dest_path)
41
41
  erb_binding ||= binding
42
+ if File.exists?("#{source_path}/.dirs")
43
+ File.readlines("#{source_path}/.dirs").each do |dir|
44
+ dir.strip!
45
+ replacements.each do |search, replace|
46
+ dir.gsub!(search, replace)
47
+ end
48
+ FileUtils.mkdir_p(dest_path+'/'+dir)
49
+ end
50
+ end
42
51
  Find.find(source_path) do |sp|
43
52
  next if sp == source_path
44
53
  sp =~ /\/([^\/]+)$/
@@ -49,7 +58,7 @@ module Spider
49
58
  dp.gsub!(search, replace)
50
59
  end
51
60
  if (File.directory?(sp))
52
- FileUtils.mkdir(dest_path+'/'+dp)
61
+ FileUtils.mkdir(dest_path+'/'+dp) unless File.directory?(dest_path+'/'+dp)
53
62
  else
54
63
  dst = File.new(dest_path+'/'+dp, 'w')
55
64
  res = ERB.new(IO.read(sp)).result(erb_binding)
@@ -93,6 +93,10 @@ module Spider; module Model
93
93
  subclass.instance_variable_set("@extended_models", @extended_models.clone) if @extended_models
94
94
  end
95
95
 
96
+ def self.subclasses
97
+ @subclasses || []
98
+ end
99
+
96
100
  # Returns the parent Spider::App of the module
97
101
  def self.app
98
102
  return @app if @app
@@ -103,12 +107,6 @@ module Spider; module Model
103
107
  @app = app
104
108
  end
105
109
 
106
- # Returns an instance of the Model with #autoload set to false
107
- def self.static(value=nil)
108
- obj = self.new(value)
109
- obj.autoload = false
110
- return obj
111
- end
112
110
 
113
111
  #######################################
114
112
  # Model definition methods #
@@ -163,6 +161,8 @@ module Spider; module Model
163
161
  # :computed_from:: (array of symbols) the element is not mapped; its value is computed
164
162
  # by the class from the given elements.
165
163
  # :unmapped:: (bool) the element is not mapped.
164
+ # :sortable:: (bool or Array of symbols) specifies that an unmapped element can be used for sorting.
165
+ # The model must provide a meaningful order using the prepare_query method.
166
166
  # :check:: (a Proc, or a Regexp, or a Hash of messages => Regexp|Proc). See #check
167
167
  # :through:: (a BaseModel subclass) model representing the many to many relationship.
168
168
  # :read_only:: (bool) hint to the UI that the element should not be user modifiable.
@@ -170,6 +170,8 @@ module Spider; module Model
170
170
  # :condition:: (hash or Condition) Restricts an association always adding the condition.
171
171
  # :order:: (true or Fixnum) When doing queries, sort by this element. More than one element can have the
172
172
  # :order attribute; if it is a Fixnum, it will mean the position in the ordering.
173
+ # :default:: (Proc or value) default value for the element. If it is a Proc, it will be passed
174
+ # the object.
173
175
  #
174
176
  # Other attributes may be used by DataTypes (see #DataType::ClassMethods.take_attributes), and other code.
175
177
  # See also Element.
@@ -210,6 +212,10 @@ module Spider; module Model
210
212
  if (attributes[:condition] && !attributes[:condition].is_a?(Condition))
211
213
  attributes[:condition] = Condition.new(attributes[:condition])
212
214
  end
215
+ if attributes[:computed_from] && !attributes[:computed_from].is_a?(Enumerable)
216
+ attributes[:computed_from] = [attributes[:computed_from]]
217
+ end
218
+ type.set_element_attributes(attributes) if type < Spider::DataType
213
219
 
214
220
 
215
221
  orig_type = type
@@ -220,7 +226,7 @@ module Spider; module Model
220
226
  (attributes[:has_single_reverse] == false || !attributes[:reverse] || (!type.elements[attributes[:reverse]] || type.elements[attributes[:reverse]].multiple?))))
221
227
  attributes[:anonymous_model] = true
222
228
  attributes[:owned] = true unless attributes[:owned] != nil
223
- first_model = self.first_definer(name)
229
+ first_model = self.first_definer(name, type)
224
230
  assoc_type_name = Spider::Inflector.camelize(name)
225
231
  create_junction = true
226
232
  if (attributes[:through])
@@ -266,6 +272,10 @@ module Spider; module Model
266
272
  orig_type.referenced_by_junctions << [assoc_type, other_name]
267
273
  attributes[:keep_junction] = true if (attributes[:through] && attributes[:keep_junction] != false)
268
274
  attributes[:association_type] = assoc_type
275
+ if attributes[:polymorph]
276
+ assoc_type.elements[attributes[:junction_their_element]].attributes[:polymorph] = attributes[:polymorph]
277
+ attributes.delete(:polymorph)
278
+ end
269
279
  end
270
280
 
271
281
  @elements[name] = Element.new(name, type, attributes)
@@ -282,8 +292,14 @@ module Spider; module Model
282
292
  attributes[:reverse] ||= attributes[:add_reverse][:name]
283
293
  rev = attributes[:add_reverse].merge(:reverse => name, :added_reverse => true,
284
294
  :delete_cascade => attributes[:reverse_delete_cascade])
285
- rev[:through] = assoc_type if assoc_type
286
295
  rev_name = rev.delete(:name)
296
+ if assoc_type
297
+ rev[:junction] = true
298
+ rev[:keep_junction] = false
299
+ rev[:through] = assoc_type
300
+ rev[:junction_their_element] = self_name
301
+ rev[:junction_our_element] = other_name
302
+ end
287
303
  orig_type.element(rev_name, self, rev)
288
304
  end
289
305
  elsif (attributes[:add_multiple_reverse])
@@ -293,6 +309,7 @@ module Spider; module Model
293
309
  :added_reverse => true, :delete_cascade => attributes[:reverse_delete_cascade])
294
310
  rev_name = rev.delete(:name)
295
311
  if assoc_type
312
+ rev[:junction] = true
296
313
  rev[:through] = assoc_type
297
314
  rev[:junction_their_element] = self_name
298
315
  rev[:junction_our_element] = other_name
@@ -304,7 +321,7 @@ module Spider; module Model
304
321
  # if attributes[:primary_key]
305
322
  # attributes[:lazy] = true
306
323
  # els
307
- if (type < BaseModel && attributes[:multiple])
324
+ if (type < BaseModel && (attributes[:multiple] || attributes[:polymorph]))
308
325
  # FIXME: we can load eagerly single relations if we can do a join
309
326
  attributes[:lazy] = true
310
327
  else
@@ -370,9 +387,18 @@ module Spider; module Model
370
387
  if !val && element.model? && (element.multiple? || element.attributes[:extended_model])
371
388
  val = instance_variable_set(ivar, instantiate_element(name))
372
389
  end
390
+ if !val && element.attributes[:default]
391
+ if element.attributes[:default].is_a?(Proc)
392
+ val = element.attributes[:default].call(self)
393
+ else
394
+ val = element.attributes[:default]
395
+ end
396
+ end
373
397
  val.set_parent(self, name) if element.model? && val && !val._parent # FIXME!!!
374
398
  return val
375
399
  end
400
+
401
+ alias_method :"#{name}?", name if type <= Spider::DataTypes::Bool
376
402
 
377
403
  #instance_variable_setter
378
404
  element_methods.send(:define_method, "#{name}=") do |val|
@@ -634,9 +660,7 @@ module Spider; module Model
634
660
  pk_name = @elements[:id] ? :"id_#{self.short_name.downcase}" : :id
635
661
  element(pk_name, Fixnum, :autoincrement => true, :local_pk => true, :hidden => true)
636
662
  end
637
- if (params[:add_polymorphic])
638
- model.polymorphic(self, :through => integrated_name)
639
- end
663
+ model.polymorphic(self, :through => integrated_name)
640
664
  end
641
665
 
642
666
  # Externalizes the superclass elements making the superclass an external integrated element.
@@ -644,7 +668,6 @@ module Spider; module Model
644
668
  # * :name (symbol) name of the created element
645
669
  # * :delete_cascade (bool) delete cascade the superclass instance
646
670
  # * :no_local_pk (bool) do not define an id for this class
647
- # * :add_polymorphic (bool) notify the superclass that it is extended, making polymorphic queries possible
648
671
  def self.class_table_inheritance(params={})
649
672
  self.extend_model(superclass, params)
650
673
  end
@@ -757,6 +780,10 @@ module Spider; module Model
757
780
  _(@label_plural || self.name || '')
758
781
  end
759
782
 
783
+ def self.auto_primary_keys?
784
+ self.primary_keys.select{ |k| !k.autogenerated? }.empty?
785
+ end
786
+
760
787
  ########################################################
761
788
  # Methods returning information about the elements #
762
789
  ########################################################
@@ -791,13 +818,13 @@ module Spider; module Model
791
818
 
792
819
  # Returns the model actually defining element_name; that could be the model
793
820
  # itself, a superclass, or an integrated model.
794
- def self.first_definer(element_name)
795
- if (@extended_models && @extended_models[self.superclass] && self.superclass.elements[element_name])
796
- return self.superclass.first_definer(element_name)
821
+ def self.first_definer(element_name, type)
822
+ if (@extended_models && @extended_models[self.superclass] && self.superclass.elements[element_name] && self.superclass.elements[element_name].type == type)
823
+ return self.superclass.first_definer(element_name, type)
797
824
  end
798
825
  if (self.attributes[:integrated_models])
799
826
  self.attributes[:integrated_models].keys.each do |mod|
800
- return mod.first_definer(element_name) if (mod.elements[element_name])
827
+ return mod.first_definer(element_name, type) if (mod.elements[element_name] && mod.elements[element_name].type == type)
801
828
  end
802
829
  end
803
830
  return self
@@ -939,7 +966,9 @@ module Spider; module Model
939
966
  # Executes #self.where, returning the first result.
940
967
  # See #self.where for parameter syntax.
941
968
  def self.load(*params, &proc)
942
- return self.where(*params, &proc)[0]
969
+ qs = self.where(*params, &proc)
970
+ qs.limit = 1
971
+ return qs[0]
943
972
  end
944
973
 
945
974
  # Returns a queryset without conditions
@@ -1018,31 +1047,41 @@ module Spider; module Model
1018
1047
  @_has_values = true
1019
1048
  Spider::Model.unit_of_work.add(self) if (Spider::Model.unit_of_work)
1020
1049
  end
1021
- if (values)
1022
- if (values.is_a? Hash)
1023
- values.keys.select{ |k|
1024
- k = k.name if k.is_a?(Element)
1025
- self.class.elements[k.to_sym] && self.class.elements[k.to_sym].primary_key?
1026
- }.each do |k|
1027
- set!(k, values[k])
1028
- end
1029
- values.each do |key, val|
1030
- set!(key, val)
1031
- end
1032
- elsif (values.is_a? BaseModel)
1033
- values.each_val do |name, val|
1034
- set(name, val) if self.class.has_element?(name)
1035
- end
1036
- elsif (values.is_a? Array)
1037
- self.class.primary_keys.each_index do |i|
1038
- set(self.class.primary_keys[i], values[i])
1039
- end
1040
- # Single unset key, single value
1041
- elsif ((empty_keys = self.class.primary_keys.select{ |key| !element_has_value?(key) }).length == 1)
1042
- set(empty_keys[0], values)
1043
- else
1044
- raise ArgumentError, "Don't know how to construct a #{self.class} from #{values.inspect}"
1050
+ set_values(values) if values
1051
+ end
1052
+
1053
+ # Returns an instance of the Model with #autoload set to false
1054
+ def self.static(values=nil)
1055
+ obj = self.new
1056
+ obj.autoload = false
1057
+ obj.set_values(values) if values
1058
+ return obj
1059
+ end
1060
+
1061
+ def set_values(values)
1062
+ if (values.is_a? Hash)
1063
+ values.keys.select{ |k|
1064
+ k = k.name if k.is_a?(Element)
1065
+ self.class.elements[k.to_sym] && self.class.elements[k.to_sym].primary_key?
1066
+ }.each do |k|
1067
+ set!(k, values[k])
1068
+ end
1069
+ values.each do |key, val|
1070
+ set!(key, val)
1071
+ end
1072
+ elsif (values.is_a? BaseModel)
1073
+ values.each_val do |name, val|
1074
+ set(name, val) if self.class.has_element?(name)
1045
1075
  end
1076
+ elsif (values.is_a? Array)
1077
+ self.class.primary_keys.each_index do |i|
1078
+ set(self.class.primary_keys[i], values[i])
1079
+ end
1080
+ # Single unset key, single value
1081
+ elsif ((empty_keys = self.class.primary_keys.select{ |key| !element_has_value?(key) }).length == 1)
1082
+ set(empty_keys[0], values)
1083
+ else
1084
+ raise ArgumentError, "Don't know how to construct a #{self.class} from #{values.inspect}"
1046
1085
  end
1047
1086
  end
1048
1087
 
@@ -1067,7 +1106,7 @@ module Spider; module Model
1067
1106
  val = element.type.new
1068
1107
  val.autoload = autoload?
1069
1108
  end
1070
- end
1109
+ end
1071
1110
  return prepare_child(name, val)
1072
1111
  end
1073
1112
 
@@ -1077,18 +1116,24 @@ module Spider; module Model
1077
1116
  element = self.class.elements[name]
1078
1117
  if (element.model?)
1079
1118
  # convert between junction and real type if needed
1080
- if (obj.is_a?(QuerySet) && element.attributes[:junction])
1081
- obj.no_autoload do
1082
- if (element.attributes[:keep_junction] && obj.model == element.type)
1083
- qs = QuerySet.new(element.model)
1084
- obj.each{ |el_obj|
1085
- qs << {element.reverse => self, element.attributes[:junction_their_element] => el_obj}
1086
- }
1087
- obj = qs
1088
- elsif (!element.attributes[:keep_junction] && obj.model == element.model)
1089
- qs = QuerySet.new(element.type, obj.map{ |el_obj| el_obj.get(element.attributes[:junction_their_element])})
1090
- obj = qs
1091
- end
1119
+ if element.attributes[:junction]
1120
+ if obj.is_a?(QuerySet)
1121
+ obj.no_autoload do
1122
+ if (element.attributes[:keep_junction] && obj.model == element.type)
1123
+ qs = QuerySet.new(element.model)
1124
+ obj.each{ |el_obj|
1125
+ qs << {element.reverse => self, element.attributes[:junction_their_element] => el_obj}
1126
+ }
1127
+ obj = qs
1128
+ elsif (!element.attributes[:keep_junction] && obj.model == element.model)
1129
+ qs = QuerySet.new(element.type, obj.map{ |el_obj| el_obj.get(element.attributes[:junction_their_element])})
1130
+ obj = qs
1131
+ end
1132
+ end
1133
+ else
1134
+ if (!element.attributes[:keep_junction] && obj.class == element.model)
1135
+ obj = obj.get(element.attributes[:junction_their_element])
1136
+ end
1092
1137
  end
1093
1138
  end
1094
1139
  self.class.elements_array.select{ |el| el.attributes[:fixed] }.each do |el|
@@ -1268,6 +1313,8 @@ module Spider; module Model
1268
1313
  get(element.integrated_from).set_loaded_value(element.integrated_from_element, value)
1269
1314
  else
1270
1315
  value = prepare_child(element.name, value)
1316
+ current = instance_variable_get("@#{element_name}")
1317
+ current.set_parent(nil, nil) if current && current.is_a?(BaseModel)
1271
1318
  instance_variable_set("@#{element_name}", value)
1272
1319
  end
1273
1320
  value.loaded = true if (value.is_a?(QuerySet))
@@ -1333,8 +1380,8 @@ module Spider; module Model
1333
1380
  # not contain all of the current model's elements.
1334
1381
  def subclass(model)
1335
1382
  obj = model.new
1336
- elements_array.each do |el|
1337
- obj.set(el, self.get(el)) if element_has_value?(el)
1383
+ self.class.elements_array.each do |el|
1384
+ obj.set(el, self.get(el)) if element_has_value?(el) && model.elements[el.name]
1338
1385
  end
1339
1386
  return obj
1340
1387
  end
@@ -294,7 +294,11 @@ module Spider; module Model
294
294
 
295
295
  # True if there are no comparisons and no subconditions.
296
296
  def empty?
297
- return super && @subconditions.empty?
297
+ return false unless super
298
+ @subconditions.each do |sub|
299
+ return false unless sub.empty?
300
+ end
301
+ return true
298
302
  end
299
303
 
300
304
  alias :hash_replace :replace # :nodoc:
@@ -390,6 +394,10 @@ module Spider; module Model
390
394
  return ConditionElement.new(name, @condition_context)
391
395
  end
392
396
 
397
+ def id
398
+ return ConditionElement.new(:id, @condition_context)
399
+ end
400
+
393
401
  def AND(&proc)
394
402
  @condition_context = []
395
403
  instance_eval(&proc)
@@ -45,6 +45,21 @@ module Spider
45
45
  end
46
46
  end
47
47
 
48
+ # Tells the class that this type generates the element's value automatically
49
+ def autogenerated
50
+ @autogenerated = true
51
+ end
52
+
53
+ # Returns whether the given element is autogenerated
54
+ def auto?(element)
55
+ @autogenerated && element.attributes[:auto]
56
+ end
57
+
58
+ # Is called by the BaseModel when creating an element of this type. Can modify the element's attributes.
59
+ def set_element_attributes(attributes)
60
+ attributes[:auto] = true unless attributes[:auto] == false
61
+ end
62
+
48
63
  end
49
64
 
50
65
  # Returns the DataType attributes, as set in the Model Element.
@@ -6,6 +6,7 @@ module Spider; module DataTypes
6
6
 
7
7
  class UUID < String
8
8
  include DataType
9
+ autogenerated
9
10
 
10
11
  def map(mapper_type)
11
12
  self.to_s
@@ -22,6 +23,10 @@ module Spider; module DataTypes
22
23
  def self.generate
23
24
  UUIDTools::UUID.random_create.to_s
24
25
  end
26
+
27
+ def self.auto_value
28
+ generate
29
+ end
25
30
 
26
31
  end
27
32
 
@@ -18,8 +18,8 @@ module Spider; module Model
18
18
  :read_only => true,
19
19
  :element_position => 0
20
20
  }
21
- element :obj_created, DateTime, :hidden => true
22
- element :obj_modified, DateTime, :hidden => true
21
+ element :obj_created, DateTime, :label => _('Created'), :hidden => true
22
+ element :obj_modified, DateTime, :label => _('Modified'), :hidden => true
23
23
 
24
24
 
25
25
  def assign_id(val) #:nodoc:
@@ -86,8 +86,10 @@ module Spider; module Model; module Mappers
86
86
  @model.each_element do |element|
87
87
  next if !mapped?(element) || element.integrated?
88
88
  next if save_mode == :update && !obj.element_modified?(element)
89
- if (save_mode == :insert && element.attributes[:autoincrement] && !schema.attributes(element.name)[:autoincrement])
90
- obj.set(element.name, @storage.sequence_next(schema.sequence(element.name)))
89
+ if (save_mode == :insert)
90
+ if element.attributes[:autoincrement] && !schema.attributes(element.name)[:autoincrement]
91
+ obj.set(element.name, @storage.sequence_next(schema.sequence(element.name)))
92
+ end
91
93
  end
92
94
  if (!element.multiple?)
93
95
  next if (save_mode == :update && element.primary_key?)
@@ -332,9 +334,9 @@ module Spider; module Model; module Mappers
332
334
  next if !element || !element.type || element.integrated?
333
335
  if !element.model?
334
336
  field = schema.field(el)
337
+ primary_keys << field if model_pks.include?(el)
335
338
  unless seen_fields[field.name]
336
339
  keys << field
337
- primary_keys << field if model_pks.include?(el)
338
340
  seen_fields[field.name] = true
339
341
  end
340
342
  elsif !element.attributes[:junction]
@@ -342,9 +344,9 @@ module Spider; module Model; module Mappers
342
344
  element.model.primary_keys.each do |key|
343
345
  field = schema.foreign_key_field(el, key.name)
344
346
  raise "Can't find a foreign key field for key #{key.name} of element #{el} of model #{@model}" unless field
347
+ primary_keys << field if model_pks.include?(el)
345
348
  unless seen_fields[field.name]
346
349
  keys << field
347
- primary_keys << field if model_pks.include?(el)
348
350
  seen_fields[field.name] = true
349
351
  end
350
352
  end
@@ -409,7 +411,7 @@ module Spider; module Model; module Mappers
409
411
  return {
410
412
  :query_type => :select,
411
413
  :keys => keys,
412
- :primary_keys => primary_keys,
414
+ :primary_keys => primary_keys.uniq,
413
415
  :tables => tables,
414
416
  :condition => condition,
415
417
  :joins => joins,
@@ -480,24 +482,40 @@ module Spider; module Model; module Mappers
480
482
  cond[:conj] = condition.conjunction.to_s
481
483
  cond[:values] = []
482
484
 
483
- # find out which elements have non nil conditions to figure out joins
484
- def get_not_nil(model, condition, not_nil)
485
- condition.all_each_with_comparison do |k, v, comp|
485
+
486
+
487
+ def get_join_info(model, condition)
488
+ join_info = {}
489
+ condition.each_with_comparison do |k, v, comp|
486
490
  next unless k.respond_to?(:to_sym)
487
491
  element = model.elements[k.to_sym]
488
492
  next unless element
489
493
  next unless model.mapper.mapped?(element)
490
494
  next unless element.model?
491
- not_nil[k] = {} if !v.nil? || comp != '='
492
- get_not_nil(element.model, v, not_nil[k]) if v.is_a?(Condition)
495
+ join_info[k.to_s] = true if !v.nil? || comp != '='
496
+ if v.is_a?(Spider::Model::Condition)
497
+ el_join_info = get_join_info(element.model, v)
498
+ el_join_info.each do |jk, jv|
499
+ join_info["#{k}.#{jk}"] = jv
500
+ end
501
+ end
493
502
  end
503
+ condition.subconditions.each do |sub_cond|
504
+ sub_join_info = get_join_info(model, sub_cond)
505
+ if condition.conjunction == :or
506
+ join_info.each_key do |k|
507
+ join_info.delete(k) unless sub_join_info[k]
508
+ end
509
+ else
510
+ join_info.merge!(sub_join_info)
511
+ end
512
+ end
513
+ join_info
494
514
  end
495
515
 
496
- not_nil = options[:not_nil]
497
- unless not_nil
498
- not_nil = {}
499
- get_not_nil(@model, condition, not_nil)
500
- end
516
+ join_info = options[:join_info]
517
+ join_info ||= get_join_info(@model, condition)
518
+
501
519
 
502
520
  condition.each_with_comparison do |k, v, comp|
503
521
  if k.is_a?(QueryFuncs::Function)
@@ -509,6 +527,12 @@ module Spider; module Model; module Mappers
509
527
  element = model.elements[k.to_sym]
510
528
  next unless model.mapper.mapped?(element)
511
529
  if (element.model?)
530
+ el_join_info = {}
531
+ join_info.each do |jk, jv|
532
+ if jk.index(k.to_s+'.') == 0
533
+ el_join_info[jk[k.to_s.length..-1]] = jv
534
+ end
535
+ end
512
536
  if (v && model.mapper.have_references?(element.name) && v.select{ |key, value|
513
537
  !element.model.elements[key] || !element.model.elements[key].primary_key? }.empty?)
514
538
  # 1/n <-> 1 with only primary keys
@@ -523,7 +547,7 @@ module Spider; module Model; module Mappers
523
547
  cond[:values] << element_cond
524
548
  else
525
549
  if (element.storage == model.mapper.storage)
526
- join_type = (v.nil? && comp == '=') ? :left : :inner
550
+ join_type = join_info[element.name.to_s] ? :inner : :left
527
551
  sub_join = model.mapper.get_join(element, join_type)
528
552
  # FIXME! cleanup, and apply the check to joins acquired in other places, too (maybe pass the current joins to get_join)
529
553
  existent = joins.select{ |j| j[:to] == sub_join[:to] }
@@ -542,25 +566,26 @@ module Spider; module Model; module Mappers
542
566
  sub_join[:as] = "#{sub_join[:to]}#{j_cnt}" if j_cnt
543
567
  joins << sub_join unless had_join
544
568
 
545
- if v.nil? && comp == '=' && !not_nil[element.name]
569
+ if v.nil? && comp == '='
570
+ el_model_schema = model_schema
546
571
  element_cond = {:conj => 'AND', :values => []}
547
572
  if model.mapper.have_references?(element.name)
548
573
  el_name = element.name
549
574
  el_model = element.model
550
575
  else
551
576
  el_model = element.type
552
- model_schema = element.model.mapper.schema
577
+ el_model_schema = element.model.mapper.schema
553
578
  el_name = element.attributes[:junction_their_element]
554
579
  end
555
580
  el_model.primary_keys.each do |k|
556
- field = model_schema.qualified_foreign_key_field(el_name, k.name)
581
+ field = el_model_schema.qualified_foreign_key_field(el_name, k.name)
557
582
  field_cond = [field, comp, map_condition_value(element.model.elements[k.name].type, nil)]
558
583
  element_cond[:values] << field_cond
559
584
  end
560
585
  cond[:values] << element_cond
561
586
  elsif v
562
587
  v = element.model.mapper.preprocess_condition(v)
563
- sub_condition, sub_joins = element.mapper.prepare_condition(v, :table => sub_join[:as], :joins => joins, :not_nil => not_nil[element.name])
588
+ sub_condition, sub_joins = element.mapper.prepare_condition(v, :table => sub_join[:as], :joins => joins, :join_info => el_join_info)
564
589
  sub_condition[:table] = sub_join[:as] if sub_join[:as]
565
590
  joins = sub_joins
566
591
  cond[:values] << sub_condition
@@ -587,7 +612,7 @@ module Spider; module Model; module Mappers
587
612
  sub_sqls = []
588
613
  sub_bind_values = []
589
614
  condition.subconditions.each do |sub|
590
- sub_res = self.prepare_condition(sub, :joins => joins, :not_nil => not_nil)
615
+ sub_res = self.prepare_condition(sub, :joins => joins, :join_info => join_info)
591
616
  cond[:values] << sub_res[0]
592
617
  joins = sub_res[1]
593
618
  remaining_condition += sub_res[2]