spiderfw 0.5.10 → 0.5.11

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 (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]