spiderfw 0.6.21 → 0.6.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/CHANGELOG +33 -0
  2. data/Rakefile +0 -1
  3. data/VERSION +1 -1
  4. data/apps/core/admin/_init.rb +4 -0
  5. data/apps/core/admin/admin.rb +20 -4
  6. data/apps/core/admin/controllers/admin_controller.rb +63 -4
  7. data/apps/core/admin/controllers/app_admin_controller.rb +15 -0
  8. data/apps/core/admin/data/locale/it/LC_MESSAGES/admin.mo +0 -0
  9. data/apps/core/admin/po/admin.pot +33 -0
  10. data/apps/core/admin/po/it/admin.po +34 -0
  11. data/apps/core/admin/public/css/admin.css +13 -0
  12. data/apps/core/admin/public/css/login.css +51 -0
  13. data/apps/core/admin/public/css/sass/admin.css +198 -0
  14. data/apps/core/admin/public/css/sass/bootstrap/bootstrap.css +3107 -0
  15. data/apps/core/admin/public/img/css/header_bg.png +0 -0
  16. data/apps/core/admin/public/img/css/noise.png +0 -0
  17. data/apps/core/admin/public/img/css/side_bg.png +0 -0
  18. data/apps/core/admin/public/img/icons/logout.png +0 -0
  19. data/apps/core/admin/public/img/icons-s845a69dd9f.png +0 -0
  20. data/apps/core/admin/public/js/bootstrap-alerts.js +113 -0
  21. data/apps/core/admin/public/js/bootstrap-buttons.js +62 -0
  22. data/apps/core/admin/public/js/bootstrap-dropdown.js +55 -0
  23. data/apps/core/admin/public/js/bootstrap-modal.js +260 -0
  24. data/apps/core/admin/public/js/bootstrap-popover.js +90 -0
  25. data/apps/core/admin/public/js/bootstrap-scrollspy.js +107 -0
  26. data/apps/core/admin/public/js/bootstrap-tabs.js +80 -0
  27. data/apps/core/admin/public/js/bootstrap-twipsy.js +321 -0
  28. data/apps/core/admin/public/sass/admin.scss +167 -0
  29. data/apps/core/admin/public/sass/bootstrap/bootstrap.scss +29 -0
  30. data/apps/core/admin/public/sass/bootstrap/forms.scss +478 -0
  31. data/apps/core/admin/public/sass/bootstrap/mixins.scss +220 -0
  32. data/apps/core/admin/public/sass/bootstrap/patterns.scss +1062 -0
  33. data/apps/core/admin/public/sass/bootstrap/reset.scss +141 -0
  34. data/apps/core/admin/public/sass/bootstrap/scaffolding.scss +136 -0
  35. data/apps/core/admin/public/sass/bootstrap/tables.scss +224 -0
  36. data/apps/core/admin/public/sass/bootstrap/type.scss +187 -0
  37. data/apps/core/admin/public/sass/bootstrap/variables.scss +60 -0
  38. data/apps/core/admin/public/sass/grid.scss +54 -0
  39. data/apps/core/admin/views/_app_info.shtml +5 -0
  40. data/apps/core/admin/views/admin.layout.shtml +35 -0
  41. data/apps/core/admin/views/index.shtml +1 -1
  42. data/apps/core/admin/views/login.layout.shtml +13 -0
  43. data/apps/core/auth/controllers/mixins/auth_helper.rb +1 -1
  44. data/apps/core/auth/models/super_user.rb +6 -0
  45. data/apps/core/auth/models/user.rb +9 -0
  46. data/apps/core/components/assets.rb +5 -1
  47. data/apps/core/components/data/locale/it/LC_MESSAGES/spider_components.mo +0 -0
  48. data/apps/core/components/po/it/spider_components.po +23 -9
  49. data/apps/core/components/po/spider_components.pot +16 -8
  50. data/apps/core/components/public/css/admin.css +0 -12
  51. data/apps/core/components/public/css/crud.css +16 -19
  52. data/apps/core/components/public/css/table.css +11 -5
  53. data/apps/core/components/public/js/less-1.1.3.min.js +16 -0
  54. data/apps/core/components/public/widgets/table.js +1 -1
  55. data/apps/core/components/widgets/admin/admin.rb +10 -0
  56. data/apps/core/components/widgets/admin/admin.shtml +24 -4
  57. data/apps/core/components/widgets/confirm/confirm.rb +2 -2
  58. data/apps/core/components/widgets/confirm/confirm.shtml +5 -2
  59. data/apps/core/components/widgets/crud/crud.rb +10 -1
  60. data/apps/core/components/widgets/crud/crud.shtml +18 -21
  61. data/apps/core/components/widgets/menu/menu.shtml +1 -2
  62. data/apps/core/components/widgets/switcher/switcher.rb +6 -3
  63. data/apps/core/components/widgets/switcher/templates/default.shtml +3 -1
  64. data/apps/core/components/widgets/table/table.rb +3 -2
  65. data/apps/core/components/widgets/table/table.shtml +44 -25
  66. data/apps/core/forms/data/locale/it/LC_MESSAGES/spider_forms.mo +0 -0
  67. data/apps/core/forms/po/it/spider_forms.po +7 -2
  68. data/apps/core/forms/po/spider_forms.pot +5 -1
  69. data/apps/core/forms/public/css/form.css +3 -3
  70. data/apps/core/forms/public/css/html_area.css +0 -1
  71. data/apps/core/forms/public/date_time.js +4 -3
  72. data/apps/core/forms/public/select.js +5 -4
  73. data/apps/core/forms/tags/element_label.erb +1 -1
  74. data/apps/core/forms/tags/row.erb +1 -1
  75. data/apps/core/forms/widgets/form/form.rb +23 -1
  76. data/apps/core/forms/widgets/form/form.shtml +7 -8
  77. data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +3 -3
  78. data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +3 -3
  79. data/apps/core/forms/widgets/inputs/file_input/file_input.rb +4 -2
  80. data/apps/core/forms/widgets/inputs/file_input/file_input.shtml +1 -1
  81. data/apps/core/forms/widgets/inputs/hidden/hidden.shtml +1 -1
  82. data/apps/core/forms/widgets/inputs/html_area/html_area.shtml +2 -2
  83. data/apps/core/forms/widgets/inputs/password/password.shtml +3 -3
  84. data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +2 -2
  85. data/apps/core/forms/widgets/inputs/select/select.shtml +16 -13
  86. data/apps/core/forms/widgets/inputs/text/text.shtml +3 -3
  87. data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +5 -2
  88. data/apps/core/forms/widgets/inputs/time_span/time_span.shtml +3 -3
  89. data/apps/messenger/_init.rb +10 -2
  90. data/apps/messenger/controllers/messenger_admin_controller.rb +53 -0
  91. data/apps/messenger/controllers/messenger_controller.rb +2 -0
  92. data/apps/messenger/controllers/mixins/messenger_helper.rb +2 -2
  93. data/apps/messenger/models/message.rb +1 -1
  94. data/apps/messenger/public/app_icon.png +0 -0
  95. data/apps/messenger/views/admin/_admin.layout.shtml +26 -0
  96. data/apps/messenger/views/admin/index.shtml +13 -0
  97. data/apps/messenger/views/admin/queue.shtml +28 -0
  98. data/apps/messenger/views/index.shtml +3 -3
  99. data/data/locale/it/LC_MESSAGES/spider.mo +0 -0
  100. data/lib/spiderfw/app.rb +10 -1
  101. data/lib/spiderfw/cache/template_cache.rb +21 -22
  102. data/lib/spiderfw/cmd/commands/app.rb +3 -3
  103. data/lib/spiderfw/cmd/commands/setup.rb +1 -1
  104. data/lib/spiderfw/config/options/spider.rb +18 -2
  105. data/lib/spiderfw/controller/controller.rb +9 -3
  106. data/lib/spiderfw/controller/dispatcher.rb +25 -12
  107. data/lib/spiderfw/controller/home_controller.rb +3 -3
  108. data/lib/spiderfw/controller/http_controller.rb +11 -0
  109. data/lib/spiderfw/controller/mixins/static_content.rb +3 -12
  110. data/lib/spiderfw/controller/mixins/visual.rb +21 -20
  111. data/lib/spiderfw/controller/request.rb +1 -3
  112. data/lib/spiderfw/http/adapters/mongrel.rb +1 -1
  113. data/lib/spiderfw/i18n/gettext.rb +14 -0
  114. data/lib/spiderfw/i18n/shtml_parser.rb +2 -2
  115. data/lib/spiderfw/model/base_model.rb +4 -3
  116. data/lib/spiderfw/model/mappers/db_mapper.rb +137 -79
  117. data/lib/spiderfw/model/mappers/mapper.rb +6 -2
  118. data/lib/spiderfw/model/migrations/drop_element.rb +1 -1
  119. data/lib/spiderfw/model/migrations/previous_model.rb +73 -0
  120. data/lib/spiderfw/model/migrations/rename_element.rb +42 -0
  121. data/lib/spiderfw/model/migrations.rb +14 -1
  122. data/lib/spiderfw/model/mixins/tree.rb +65 -19
  123. data/lib/spiderfw/model/model_hash.rb +9 -5
  124. data/lib/spiderfw/model/query.rb +8 -0
  125. data/lib/spiderfw/model/query_funcs.rb +23 -0
  126. data/lib/spiderfw/model/query_set.rb +1 -1
  127. data/lib/spiderfw/model/request.rb +11 -3
  128. data/lib/spiderfw/model/storage/db/adapters/mysql.rb +28 -1
  129. data/lib/spiderfw/model/storage/db/adapters/oracle.rb +10 -10
  130. data/lib/spiderfw/model/storage/db/db_schema.rb +20 -3
  131. data/lib/spiderfw/model/storage/db/db_storage.rb +39 -17
  132. data/lib/spiderfw/setup/app_manager.rb +69 -31
  133. data/lib/spiderfw/setup/setup_task.rb +76 -8
  134. data/lib/spiderfw/spider.rb +21 -1
  135. data/lib/spiderfw/templates/blocks/text.rb +4 -4
  136. data/lib/spiderfw/templates/blocks/text_domain.rb +25 -0
  137. data/lib/spiderfw/templates/blocks/widget.rb +1 -1
  138. data/lib/spiderfw/templates/layout.rb +160 -92
  139. data/lib/spiderfw/templates/resources/less.rb +10 -2
  140. data/lib/spiderfw/templates/resources/sass.rb +66 -9
  141. data/lib/spiderfw/templates/template.rb +35 -10
  142. data/lib/spiderfw/templates/template_blocks.rb +6 -3
  143. data/lib/spiderfw/utils/logger.rb +20 -0
  144. data/lib/spiderfw/utils/memory.rb +7 -3
  145. data/lib/spiderfw/widget/widget.rb +13 -7
  146. data/lib/spiderfw/widget/widget_attributes.rb +2 -2
  147. data/spider.gemspec +1 -0
  148. metadata +68 -11
  149. data/apps/core/admin/views/spider_admin.layout.shtml +0 -23
@@ -30,9 +30,9 @@ module Spider
30
30
  @app_routes ||= []
31
31
  end
32
32
 
33
- def self.print_app_routes
34
- max_length = app_routes.inject(0){ |m, r| m > r[0].length ? m : r[0].length }
35
- app_routes.map{ |r| "#{r[0].ljust(max_length+3)} -> #{r[1]}"}.sort.join("\n")
33
+ def self.print_app_routes(routes=app_routes)
34
+ max_length = routes.inject(0){ |m, r| m > r[0].length ? m : r[0].length }
35
+ routes.map{ |r| "#{r[0].ljust(max_length+3)} -> #{r[1]}"}.sort.join("\n")
36
36
  end
37
37
 
38
38
  end
@@ -99,6 +99,16 @@ module Spider
99
99
  @request.format = $2.to_sym
100
100
  end
101
101
  # Spider.reload_sources if Spider.conf.get('webserver.reload_sources')
102
+ static_level = Spider.conf.get('log.static_extensions')
103
+ if @request.format && @request.get? && static_level != true
104
+ allowed = Spider.conf.get('log.non_static_extensions_list')
105
+ unless allowed.include?(@request.format.to_s)
106
+ Spider.logger.info("GET #{@request.path}")
107
+ @logger_static_prev = Spider.logger.set_thread_level(static_level)
108
+ end
109
+ end
110
+ Spider::Logger.debug("REQUEST:")
111
+ Spider::Logger.debug(@request.env)
102
112
  Spider.logger.info("Request: #{@request.http_method} #{@request.http_host} #{@request.path}")
103
113
  super(action, *arguments)
104
114
  end
@@ -116,6 +126,7 @@ module Spider
116
126
  action = $1 if (action =~ /(.+)\.(\w+)$/) # strip extension, set format
117
127
  @request.session.persist if @request.session && @request.session.respond_to?(:persist)
118
128
  super(action, *arguments)
129
+ @Spider.logger.set_thread_level(@logger_static_prev) if @logger_static_prev
119
130
  end
120
131
 
121
132
  def ensure(action='', *arguments)
@@ -10,8 +10,8 @@ module Spider; module ControllerMixins
10
10
 
11
11
  def self.included(klass)
12
12
  super
13
- klass.route('public/', :serve_static, :do => lambda{ @serving_static = true })
14
- klass.route('w/', :serve_widget_static, :do => lambda{ @serving_static = true })
13
+ @static_content_route ||= 'public/'
14
+ klass.route(@static_content_route, :serve_static, :do => lambda{ @serving_static = true })
15
15
  if (klass < Visual)
16
16
  klass.no_layout('public')
17
17
  klass.no_layout('serve_static')
@@ -61,14 +61,6 @@ module Spider; module ControllerMixins
61
61
  output_static(full_path)
62
62
  end
63
63
 
64
- def serve_widget_static(path)
65
- path = sanitize_path(path)
66
- parts = path.split('/public/', 2)
67
- raise Spider::Controller::NotFound.new(path) unless parts[1]
68
- full_path = self.class.app.widgets_path+'/'+parts[0]+'/public/'+parts[1]
69
- output_static(full_path)
70
- end
71
-
72
64
  def output_static(full_path, file_name=nil)
73
65
  file_name ||= File.basename(full_path)
74
66
  @request.misc[:is_static] = true
@@ -89,11 +81,10 @@ module Spider; module ControllerMixins
89
81
  begin
90
82
  if_modified = Time.httpdate(@request.env['HTTP_IF_MODIFIED_SINCE'])
91
83
  rescue ArgumentError # Passenger with IE6 has this header wrong
92
- if_modified = 0
93
84
  end
94
85
  max_age = nil
95
86
  fresh = true
96
- if fresh && mtime <= if_modified
87
+ if fresh && if_modified && mtime <= if_modified
97
88
  debug("Not modified since #{if_modified}: #{full_path}")
98
89
  #@response.status = Spider::HTTP::NOT_MODIFIED
99
90
  @response.headers.delete("Content-Type")
@@ -43,6 +43,16 @@ module Spider; module ControllerMixins
43
43
  if Spider.runmode != 'devel' && File.exists?(File.join(Spider.paths[:tmp], 'maintenance.txt'))
44
44
  raise Spider::Controller::Maintenance
45
45
  end
46
+ n_route = dispatch_next(action)
47
+ obj = n_route.obj if n_route
48
+ if obj.is_a?(Visual) && !(obj.respond_to?(:serving_static?) && obj.serving_static?(n_route.path))
49
+ set_layout = @layout || @dispatcher_layout
50
+ if set_layout
51
+ set_layout = [set_layout] unless set_layout.is_a?(Array)
52
+ set_layout.map{ |l| self.class.load_layout(l) }
53
+ obj.dispatcher_layout = set_layout
54
+ end
55
+ end
46
56
  super
47
57
  end
48
58
 
@@ -210,13 +220,12 @@ module Spider; module ControllerMixins
210
220
  end
211
221
  init_widgets(template, layout)
212
222
  return template if done?
213
- Spider::GetText.in_domain(self.class.app.short_name){
214
- if layout
215
- layout.render(scene)
216
- else
217
- template.render(scene)
218
- end
219
- }
223
+
224
+ if layout
225
+ layout.render(scene)
226
+ else
227
+ template.render(scene)
228
+ end
220
229
  return template
221
230
  end
222
231
 
@@ -236,21 +245,12 @@ module Spider; module ControllerMixins
236
245
 
237
246
 
238
247
 
239
- def dispatched_object(route)
240
- obj = super
241
- if (obj.is_a?(Visual))
242
- set_layout = @layout || @dispatcher_layout
243
- if set_layout
244
- set_layout = [set_layout] unless set_layout.is_a?(Array)
245
- set_layout.map{ |l| self.class.load_layout(l) }
246
- obj.dispatcher_layout = set_layout
247
- end
248
- end
249
- return obj
250
- end
251
248
 
252
249
  def try_rescue(exc)
253
- exc.uuid = UUIDTools::UUID.random_create.to_s if exc.respond_to?(:uuid=)
250
+ if exc.respond_to?(:uuid=)
251
+ return super if exc.uuid # Error page already outputted
252
+ exc.uuid = UUIDTools::UUID.random_create.to_s
253
+ end
254
254
  format = self.class.output_format(:error) || :html
255
255
  unless exc.is_a?(Spider::Controller::Maintenance) || exc.is_a?(Spider::Controller::NotFound)
256
256
  return super unless @executed_format == :html
@@ -555,6 +555,7 @@ module Spider; module ControllerMixins
555
555
  path = Spider::Layout.named_layouts[path]
556
556
  end
557
557
  resource = Spider::Template.find_resource(path+'.layout', layout_path, self)
558
+ raise "Layout #{path} not found" unless resource && resource.path
558
559
  layout = Spider::Layout.new(resource.path)
559
560
  layout.definer_class = resource.definer
560
561
  layout.asset_set = params[:assets] if params[:assets]
@@ -21,10 +21,8 @@ module Spider
21
21
 
22
22
 
23
23
  def initialize(env)
24
- Spider::Logger.debug("REQUEST:")
25
- Spider::Logger.debug(env)
26
24
  @env = env
27
- @locale = Locale.current[0]
25
+ @locale = Spider.locale
28
26
  @misc = {}
29
27
  @params = {}
30
28
  @action = ""
@@ -24,7 +24,7 @@ module Spider; module HTTP
24
24
 
25
25
 
26
26
  def shutdown_server
27
- @server.stop
27
+ @server.stop if @server
28
28
  end
29
29
 
30
30
 
@@ -12,6 +12,7 @@ module Spider
12
12
 
13
13
  module GetText
14
14
 
15
+ # Executes a block of code in the given text_domain
15
16
  def self.in_domain(domain, &block)
16
17
  prev_text_domain = FastGettext.text_domain
17
18
  FastGettext.text_domain = domain if FastGettext.translation_repositories.key?(domain)
@@ -19,6 +20,19 @@ module Spider
19
20
  FastGettext.text_domain = prev_text_domain
20
21
  v
21
22
  end
23
+
24
+ # Sets the current text_domain; return the previous domain
25
+ def self.set_domain(domain)
26
+ prev_text_domain = FastGettext.text_domain
27
+ FastGettext.text_domain = domain if FastGettext.translation_repositories.key?(domain)
28
+ prev_text_domain
29
+ end
30
+
31
+ # Sets the current text_domain; assumes the domain was already set before, so skips any
32
+ # check for domain validity
33
+ def self.restore_domain(domain)
34
+ FastGettext.text_domain = domain
35
+ end
22
36
 
23
37
  end
24
38
 
@@ -21,13 +21,13 @@ module Spider; module I18n
21
21
  str =~ Spider::TemplateBlocks::GettextRegexp
22
22
  found = false
23
23
  ary.each do |msg|
24
- if (msg[0] == $1)
24
+ if (msg[0] == $2)
25
25
  msg << "#{file}:#{cnt}"
26
26
  found = true
27
27
  break
28
28
  end
29
29
  end
30
- ary << [$1, "#{file}:#{cnt}"] unless found
30
+ ary << [$2, "#{file}:#{cnt}"] unless found
31
31
  end
32
32
  end
33
33
  f.close
@@ -1174,8 +1174,8 @@ module Spider; module Model
1174
1174
  qs.autoload = true
1175
1175
  qs.where(&proc)
1176
1176
  else
1177
- condition = Condition.and(params[0])
1178
- request = Request.new(params[1])
1177
+ condition = params[0].is_a?(Condition) ? params[0] : Condition.and(params[0])
1178
+ request = params[1].is_a?(Request) ? params[1] : Request.new(params[1])
1179
1179
  query = Query.new(condition, request)
1180
1180
  qs = QuerySet.new(self, query)
1181
1181
  end
@@ -1852,6 +1852,7 @@ module Spider; module Model
1852
1852
  @_modified_elements[key] = true
1853
1853
  el = self.class.elements[key.to_sym]
1854
1854
  next unless el
1855
+ next if el.attributes[:unmapped]
1855
1856
  if el.integrated? && sub = self.get(el.integrated_from)
1856
1857
  sub.set_modified(el.integrated_from_element)
1857
1858
  end
@@ -1872,7 +1873,7 @@ module Spider; module Model
1872
1873
  val.modified = false if val.is_a?(QuerySet)
1873
1874
  end
1874
1875
  self.class.elements_array.select{ |el| el.attributes[:integrated_model] && self.element_has_value?(el) }.each do |el|
1875
- self.get(el).reset_modified_elements(elements)
1876
+ self.get(el).reset_modified_elements(*elements)
1876
1877
  end
1877
1878
 
1878
1879
  nil
@@ -90,43 +90,46 @@ module Spider; module Model; module Mappers
90
90
  @model.each_element do |element|
91
91
  next if !mapped?(element) || element.integrated?
92
92
  next if save_mode == :update && !obj.element_modified?(element)
93
- if (save_mode == :insert)
93
+ if save_mode == :insert
94
94
  if element.attributes[:autoincrement] && !schema.attributes(element.name)[:autoincrement]
95
95
  obj.set(element.name, @storage.sequence_next(schema.sequence(element.name)))
96
96
  end
97
97
  end
98
- if (!element.multiple?)
99
- next if (save_mode == :update && element.primary_key?)
100
- next if (element.model? && !schema.has_foreign_fields?(element.name))
101
- element_val = obj.get(element)
102
- # next if (element.model? && (!(element_val = obj.get(element)) || !))
103
- next if (element.integrated?)
104
- element_val = nil if element.model? && element_val.is_a?(BaseModel) && !element_val.primary_keys_set?
105
- if (element.model?)
106
- element.model.primary_keys.each do |key|
107
- # FIXME! only works with one primary key
108
- if (key.model?)
109
- key_type = key.model.primary_keys[0].type
110
- key_value = element_val ? element_val.get(key.name).get(key.model.primary_keys[0]) : nil
111
- else
112
- key_type = key.model? ? key.model.primary_keys[0].type : key.type
113
- key_value = element_val ? element_val.get(key.name) : nil
114
- end
115
- store_key = schema.foreign_key_field(element.name, key.name)
116
- next if store_key.is_a?(FieldExpression)
117
- values[store_key] = map_save_value(key_type, key_value, save_mode)
118
- end
119
- else
120
- store_key = schema.field(element.name)
121
- values[store_key] = map_save_value(element.type, element_val, save_mode)
122
- end
123
- end
98
+ next if element.multiple?
99
+ next if save_mode == :update && element.primary_key?
100
+ next if element.model? && !schema.has_foreign_fields?(element.name)
101
+
102
+ element_val = obj.get(element)
103
+ prepare_save_value(element, element_val, save_mode, values)
124
104
  end
125
105
  end
126
106
  return {
127
107
  :values => values
128
108
  }
129
109
  end
110
+
111
+ def prepare_save_value(element, element_val, save_mode, values={})
112
+ element_val = nil if element.model? && element_val.is_a?(BaseModel) && !element_val.primary_keys_set?
113
+ if element.model?
114
+ element.model.primary_keys.each do |key|
115
+ # FIXME! only works with one primary key
116
+ if (key.model?)
117
+ key_type = key.model.primary_keys[0].type
118
+ key_value = element_val ? element_val.get(key.name).get(key.model.primary_keys[0]) : nil
119
+ else
120
+ key_type = key.model? ? key.model.primary_keys[0].type : key.type
121
+ key_value = element_val ? element_val.get(key.name) : nil
122
+ end
123
+ store_key = schema.foreign_key_field(element.name, key.name)
124
+ next if store_key.is_a?(FieldExpression)
125
+ values[store_key] = map_save_value(key_type, key_value, save_mode)
126
+ end
127
+ else
128
+ store_key = schema.field(element.name)
129
+ values[store_key] = map_save_value(element.type, element_val, save_mode)
130
+ end
131
+ values
132
+ end
130
133
 
131
134
  # Insert preprocessing
132
135
  def prepare_insert(obj) #:nodoc:
@@ -152,27 +155,29 @@ module Spider; module Model; module Mappers
152
155
  end
153
156
 
154
157
  # Updates according to a condition, storing the values, which must passed as a Hash.
158
+ # Condition may be nil.
155
159
  def bulk_update(values, condition)
156
160
  db_values = {}
157
161
  joins = []
158
162
  integrated = {}
159
- condition = preprocess_condition(condition)
163
+ condition = preprocess_condition(condition) if condition
160
164
  values.each do |key, val|
161
165
  element = @model.elements[key]
162
- if (element.integrated?)
166
+ if element.integrated?
163
167
  integrated[element.integrated_from] ||= {}
164
168
  integrated[element.integrated_from][key] = val
165
169
  next
166
170
  end
167
171
  next if !mapped?(element)
168
- next if element.model? && val != nil
169
- store_key = schema.field(element.name)
170
- next unless store_key
171
- if (val.is_a?(Spider::QueryFuncs::Expression))
172
+ next if element.multiple?
173
+ next if element.model? && !schema.has_foreign_fields?(element.name)
174
+ if val.is_a?(Spider::QueryFuncs::Expression)
172
175
  joins += prepare_expression(val)
176
+ store_key = schema.field(element.name)
177
+ next unless store_key
173
178
  db_values[store_key] = val
174
179
  else
175
- db_values[store_key] = map_save_value(element.type, val, :update)
180
+ prepare_save_value(element, val, :update, db_values)
176
181
  end
177
182
  end
178
183
  integrated.each do |i_el, i_values|
@@ -181,11 +186,13 @@ module Spider; module Model; module Mappers
181
186
  end
182
187
  return if db_values.empty?
183
188
  save = {:table => schema.table, :values => db_values}
184
- condition, c_joins = prepare_condition(condition)
185
- joins += c_joins
186
- save[:condition] = condition
189
+ if condition
190
+ condition, c_joins = prepare_condition(condition)
191
+ joins += c_joins
192
+ save[:condition] = condition
193
+ end
187
194
  save[:joins] = prepare_joins(joins)
188
- sql, bind_vars = @storage.sql_update(save)
195
+ sql, bind_vars = @storage.sql_update(save, true)
189
196
  return @storage.execute(sql, *bind_vars)
190
197
  end
191
198
 
@@ -252,7 +259,11 @@ module Spider; module Model; module Mappers
252
259
  end
253
260
  model = obj_or_model.is_a?(Class) ? obj_or_model : obj_or_model.model
254
261
  data = {}
262
+ extra_data = {}
255
263
  request.keys.each do |element_name|
264
+ if element_name.is_a?(QueryFuncs::SelectFunction)
265
+ extra_data[element_name.as] = result[element_name.as.to_s]
266
+ end
256
267
  element = @model.elements[element_name]
257
268
  result_value = nil
258
269
  next if !element || element.integrated? || !have_references?(element)
@@ -282,6 +293,9 @@ module Spider; module Model; module Mappers
282
293
  return nil
283
294
  end
284
295
  data.keys.each{ |el| obj.element_loaded(el) }
296
+ extra_data.each do |k, v|
297
+ obj[k] = v
298
+ end
285
299
  if (request.polymorphs)
286
300
  request.polymorphs.each do |model, polym_request|
287
301
  polym_result = {}
@@ -315,7 +329,9 @@ module Spider; module Model; module Mappers
315
329
  # Generates a select hash description based on the query.
316
330
  def prepare_select(query) #:nodoc:
317
331
  condition, joins = prepare_condition(query.condition)
318
- elements = query.request.keys.select{ |k| mapped?(k) }
332
+ elements = query.request.keys.select{ |k| !k.is_a?(QueryFuncs::SelectFunction) && mapped?(k) }
333
+ select_functions = query.request.keys.select{ |k| k.is_a?(QueryFuncs::SelectFunction) }
334
+
319
335
  keys = []
320
336
  primary_keys = []
321
337
  types = {}
@@ -331,6 +347,8 @@ module Spider; module Model; module Mappers
331
347
  oj[:as] ||= "ORD#{cnt+=1}" if joins.select{ |j| j[:to] == oj[:to] }.length > 0
332
348
  end
333
349
  joins += order_joins if order_joins
350
+ group_by, group_by_joins = prepare_group_by(query)
351
+ joins += group_by_joins if group_by_joins
334
352
  seen_fields = {}
335
353
  model_pks = []
336
354
  @model.primary_keys.each do |pk|
@@ -363,15 +381,11 @@ module Spider; module Model; module Mappers
363
381
  end
364
382
  end
365
383
  sub_request = query.request[element.name]
366
- # if (can_join?(element) && sub_request.is_a?(Request) &&
367
- # sub_request.select{|k, v| !element.model.elements[k].primary_key?}.length > 0)
368
- # sub_request = element.mapper.prepare_query_request(sub_request).reject{ |name, req| element.reverse == name }
369
- # sub_select = element.mapper.prepare_select(Query.new(nil, sub_request))
370
- # keys += sub_select[:keys]
371
- # joins << get_join(element)
372
- # end
373
384
  end
374
385
  end
386
+ select_functions.each do |f|
387
+ keys << prepare_queryfunc(f)
388
+ end
375
389
  if (query.request.polymorphs? || !query.condition.polymorphs.empty?)
376
390
  only_conditions = {:conj => 'or', :values => []} if (query.request.only_polymorphs?)
377
391
  polymorphs = (query.request.polymorphs.keys + query.condition.polymorphs).uniq
@@ -427,6 +441,7 @@ module Spider; module Model; module Mappers
427
441
  :condition => condition,
428
442
  :joins => joins,
429
443
  :order => order,
444
+ :group_by => group_by,
430
445
  :offset => query.offset,
431
446
  :limit => query.limit
432
447
  }
@@ -580,9 +595,10 @@ module Spider; module Model; module Mappers
580
595
  cond[:values] << [field, comp, v]
581
596
  joins += field.joins
582
597
  if is_having
583
- cond[:group_by_fields] += k.inner_elements.map{ |el_name, owner_func|
584
- owner_func.mapper_fields[el_name.to_s]
585
- }
598
+ k.inner_elements.each do |el_name, owner_func|
599
+ next if owner_func.is_a?(QueryFuncs::AggregateFunction)
600
+ cond[:group_by_fields] <<= owner_func.mapper_fields[el_name.to_s]
601
+ end
586
602
  end
587
603
  next
588
604
  end
@@ -600,6 +616,7 @@ module Spider; module Model; module Mappers
600
616
  element_cond = {:conj => 'AND', :values => [], :is_having => is_having}
601
617
  v.each_with_comparison do |el_k, el_v, el_comp|
602
618
  field = model_schema.foreign_key_field(element.name, el_k)
619
+ next if field.is_a?(FixedExpression)
603
620
  el_comp ||= '='
604
621
  op = el_comp
605
622
  field_cond = [field, op, map_condition_value(element.model.elements[el_k.to_sym].type, el_v)]
@@ -609,50 +626,56 @@ module Spider; module Model; module Mappers
609
626
  cond[:values] << element_cond
610
627
  else
611
628
  if element.storage == model.mapper.storage
612
- join_type = join_info[element.name.to_s] ? :inner : :left
613
- sub_join = model.mapper.get_join(element, join_type)
614
- # FIXME! cleanup, and apply the check to joins acquired in other places, too (maybe pass the current joins to get_join)
615
- existent = joins.select{ |j| j[:to] == sub_join[:to] }
616
- j_cnt = nil
617
- had_join = false
618
- existent.each do |j|
619
- if sub_join[:to] == j[:to] && sub_join[:keys] == j[:keys] && sub_join[:conditions] == j[:conditions]
620
- # if any condition allows a left join, then a left join should be used here as well
621
- j[:type] = :left if sub_join[:type] == :left
622
- sub_join = j
623
- had_join = true
624
- break
625
- else
626
- j_cnt ||= 0; j_cnt += 1
627
- end
628
- end
629
- sub_join[:as] = "#{sub_join[:to]}#{j_cnt}" if j_cnt
630
- joins << sub_join unless had_join
631
629
 
630
+ needs_join = true
632
631
  if v.nil? && comp == '='
633
632
  el_model_schema = model_schema
634
633
  element_cond = {:conj => 'AND', :values => [], :is_having => is_having}
635
634
  if model.mapper.have_references?(element.name)
636
635
  el_name = element.name
637
636
  el_model = element.model
637
+ needs_join = false
638
638
  elsif element.junction?
639
639
  el_model = element.type
640
640
  el_model_schema = element.model.mapper.schema
641
641
  el_name = element.attributes[:junction_their_element]
642
642
  else
643
- el_model = element.type
644
- el_model_schema = el_model.mapper.schema
643
+ el_model = @model
644
+ el_model_schema = element.type.mapper.schema
645
645
  el_name = element.reverse
646
646
  end
647
647
  el_model.primary_keys.each do |k|
648
648
  field = el_model_schema.foreign_key_field(el_name, k.name)
649
- field_cond = [field, comp, map_condition_value(element.model.elements[k.name].type, nil)]
649
+ next if field.is_a?(FixedExpression)
650
+ field_cond = [field, comp, map_condition_value(el_model.elements[k.name].type, nil)]
650
651
  element_cond[:values] << field_cond
651
652
  element_cond[:is_having] = is_having
652
653
  cond[:group_by_fields] << field if is_having
653
654
  end
654
655
  cond[:values] << element_cond
655
- elsif v
656
+ end
657
+ if needs_join
658
+ join_type = join_info[element.name.to_s] ? :inner : :left
659
+ sub_join = model.mapper.get_join(element, join_type)
660
+ # FIXME! cleanup, and apply the check to joins acquired in other places, too (maybe pass the current joins to get_join)
661
+ existent = joins.select{ |j| j[:to] == sub_join[:to] }
662
+ j_cnt = nil
663
+ had_join = false
664
+ existent.each do |j|
665
+ if sub_join[:to] == j[:to] && sub_join[:keys] == j[:keys] && sub_join[:conditions] == j[:conditions]
666
+ # if any condition allows a left join, then a left join should be used here as well
667
+ j[:type] = :left if sub_join[:type] == :left
668
+ sub_join = j
669
+ had_join = true
670
+ break
671
+ else
672
+ j_cnt ||= 0; j_cnt += 1
673
+ end
674
+ end
675
+ sub_join[:as] = "#{sub_join[:to]}#{j_cnt}" if j_cnt
676
+ joins << sub_join unless had_join
677
+ end
678
+ if v
656
679
  sub_condition, sub_joins = element.mapper.prepare_condition(v, :table => sub_join[:as],
657
680
  :joins => joins, :join_info => el_join_info, :is_having => is_having || nil)
658
681
  sub_condition[:table] = sub_join[:as] if sub_join[:as]
@@ -667,7 +690,7 @@ module Spider; module Model; module Mappers
667
690
  end
668
691
  elsif(model_schema.field(element.name))
669
692
  field = model_schema.field(element.name)
670
- field = FieldInAliasedTable(field, options[:table]) if options[:table]
693
+ field = FieldInAliasedTable.new(field, options[:table]) if options[:table]
671
694
  op = comp ? comp : '='
672
695
  if (v.is_a?(Spider::QueryFuncs::Expression))
673
696
  v_joins = prepare_expression(v)
@@ -815,6 +838,7 @@ module Spider; module Model; module Mappers
815
838
  owner_func.mapper_fields[el_name.to_s] = el_model.mapper.schema.field(el.name)
816
839
  end
817
840
  f = FieldFunction.new(storage.function(func), schema.table, joins)
841
+ f.as = func.as if func.is_a?(QueryFuncs::SelectFunction)
818
842
  f.aggregate = true if func.has_aggregates?
819
843
  return f
820
844
  end
@@ -850,7 +874,9 @@ module Spider; module Model; module Mappers
850
874
  if el.model?
851
875
  if el_model.mapper.have_references?(el) || el.model.storage != storage
852
876
  el.model.primary_keys.each do |pk|
853
- fields << [el_model.mapper.schema.foreign_key_field(el.name, pk.name), direction]
877
+ field = el_model.mapper.schema.foreign_key_field(el.name, pk.name)
878
+ next if field.is_a?(FixedExpression)
879
+ fields << [field, direction]
854
880
  end
855
881
  else
856
882
  el.model.primary_keys.each do |pk|
@@ -868,6 +894,34 @@ module Spider; module Model; module Mappers
868
894
  return [fields, joins]
869
895
  end
870
896
 
897
+ def prepare_group_by(query)
898
+ return nil if !query.group_by_elements
899
+ joins = []
900
+ fields = []
901
+ query.group_by_elements.each do |gb|
902
+ el_model = @model
903
+ el_joins, el_model, el = get_deep_join(gb)
904
+ # FIXME: this is almost identical to prepare_order
905
+ if el.model?
906
+ if el_model.mapper.have_references?(el) || el.model.storage != storage
907
+ el.model.primary_keys.each do |pk|
908
+ fields << el_model.mapper.schema.foreign_key_field(el.name, pk.name)
909
+ end
910
+ else
911
+ el.model.primary_keys.each do |pk|
912
+ fields << el.model.mapper.schema.field(pk.name)
913
+ end
914
+ end
915
+ else
916
+ raise "Order on unmapped element #{el_model.name}.#{el.name}" unless el_model.mapper.mapped?(el)
917
+ field = el_model.mapper.schema.field(el.name)
918
+ fields << field
919
+ end
920
+ joins += el_joins
921
+ end
922
+ return [fields, joins]
923
+ end
924
+
871
925
  # Returns a type accepted by the storage for type.
872
926
  def map_type(type)
873
927
  st = type
@@ -1060,16 +1114,20 @@ module Spider; module Model; module Mappers
1060
1114
  end
1061
1115
  end
1062
1116
 
1063
- # Autogenerates schema. Returns a DbSchema.
1064
- def generate_schema(schema=nil)
1065
- had_schema = schema ? true : false
1066
- schema ||= DbSchema.new
1117
+ def schema_table_name
1067
1118
  n = @model.name.sub('::Models', '')
1068
1119
  app = @model.app
1069
1120
  app_name = app.name if app
1070
1121
  short_prefix = app.short_prefix if app
1071
1122
  n.sub!(app_name, short_prefix) if short_prefix
1072
- schema.table ||= @model.attributes[:db_table] || @storage.table_name(n)
1123
+ @storage.table_name(n)
1124
+ end
1125
+
1126
+ # Autogenerates schema. Returns a DbSchema.
1127
+ def generate_schema(schema=nil)
1128
+ had_schema = schema ? true : false
1129
+ schema ||= DbSchema.new
1130
+ schema.table ||= @model.attributes[:db_table] || schema_table_name
1073
1131
  integrated_pks = []
1074
1132
  @model.each_element do |element|
1075
1133
  if element.integrated?
@@ -604,7 +604,8 @@ module Spider; module Model
604
604
  merged[merged_obj.object_id] = true
605
605
  end
606
606
  query.request.keys.each do |k, v|
607
- set.element_loaded(k) if have_references?(k)
607
+ # k may be a SelectFunction
608
+ set.element_loaded(k) if !k.is_a?(QueryFuncs::SelectFunction) && have_references?(k)
608
609
  end
609
610
  set.each_current do |obj|
610
611
  next if merged[obj.object_id]
@@ -1054,7 +1055,10 @@ module Spider; module Model
1054
1055
  obj.class.elements_array.each do |el|
1055
1056
  next unless obj.element_has_value?(el)
1056
1057
  next unless el.model?
1057
- children << obj.get(el)
1058
+ next unless obj.element_modified?(el)
1059
+ val = obj.get(el)
1060
+ next unless val.modified?
1061
+ children << val
1058
1062
  end
1059
1063
  children
1060
1064
  end
@@ -12,7 +12,7 @@ module Spider; module Migrations
12
12
  field = @options[:field_name]
13
13
  if !field
14
14
  schema_field = @model.mapper.schema.field(@element)
15
- field = schema_field.name
15
+ field = schema_field.name if schema_field
16
16
  end
17
17
  field ||= @model.mapper.storage.column_name(@element)
18
18
  desc = @model.mapper.storage.describe_table(@model.mapper.schema.table)