spiderfw 0.6.5 → 0.6.6

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 (81) hide show
  1. data/CHANGELOG +10 -0
  2. data/VERSION +1 -1
  3. data/apps/app_server/config/options.rb +2 -2
  4. data/apps/core/components/assets.rb +1 -5
  5. data/apps/core/components/widgets/month_calendar/month_calendar.shtml +3 -3
  6. data/apps/core/components/widgets/table/table.rb +2 -0
  7. data/apps/core/components/widgets/tabs/tabs.rb +9 -3
  8. data/apps/core/components/widgets/tabs/tabs.shtml +1 -1
  9. data/apps/master/_init.rb +3 -1
  10. data/apps/master/cmd.rb +1 -1
  11. data/apps/master/controllers/master_controller.rb +107 -103
  12. data/apps/master/controllers/scout_controller.rb +7 -7
  13. data/apps/master/controllers/{servant_controller.rb → server_controller.rb} +3 -3
  14. data/apps/master/data/locale/it/LC_MESSAGES/master.mo +0 -0
  15. data/apps/master/master.rb +4 -4
  16. data/apps/master/models/customer.rb +0 -3
  17. data/apps/master/models/installation.rb +14 -3
  18. data/apps/master/models/remote_log.rb +11 -0
  19. data/apps/master/models/scout_plugin_instance.rb +9 -9
  20. data/apps/master/models/scout_plugin_trigger.rb +2 -2
  21. data/apps/master/models/{servant.rb → server.rb} +6 -6
  22. data/apps/master/po/it/{spider_master.po → master.po} +127 -54
  23. data/apps/master/po/master.pot +23 -23
  24. data/apps/master/templates/email/alert.html.erb +1 -1
  25. data/apps/master/views/customer.shtml +3 -3
  26. data/apps/master/views/index.shtml +1 -2
  27. data/apps/master/views/installation.shtml +28 -16
  28. data/apps/master/views/master.layout.shtml +2 -1
  29. data/apps/master/views/plugin_data.shtml +1 -1
  30. data/apps/master/views/{servant.shtml → server.shtml} +14 -14
  31. data/apps/master/views/servers.shtml +11 -0
  32. data/apps/master/views/site_edit.shtml +1 -1
  33. data/apps/master/views/trigger_edit.shtml +4 -4
  34. data/apps/messenger/controllers/mixins/messenger_helper.rb +3 -3
  35. data/apps/servant/Gemfile +1 -0
  36. data/apps/servant/_init.rb +5 -1
  37. data/apps/servant/cmd.rb +14 -45
  38. data/apps/servant/config/options.rb +1 -0
  39. data/apps/servant/controllers/servant_controller.rb +7 -1
  40. data/apps/servant/lib/client.rb +55 -0
  41. data/apps/servant/servant.appspec +1 -1
  42. data/apps/servant/servant.rb +90 -0
  43. data/apps/worker/cmd.rb +9 -4
  44. data/apps/worker/worker.rb +11 -6
  45. data/blueprints/bin/spider +7 -0
  46. data/blueprints/home/config/config.yml +2 -3
  47. data/blueprints/home/init.rb +1 -1
  48. data/lib/spiderfw/app.rb +50 -2
  49. data/lib/spiderfw/cmd/commands/app.rb +22 -12
  50. data/lib/spiderfw/cmd/commands/config.rb +2 -1
  51. data/lib/spiderfw/config/configuration_editor.rb +7 -4
  52. data/lib/spiderfw/config/options/spider.rb +3 -1
  53. data/lib/spiderfw/controller/helpers/widget_helper.rb +6 -3
  54. data/lib/spiderfw/controller/mixins/http_mixin.rb +2 -1
  55. data/lib/spiderfw/controller/mixins/visual.rb +12 -10
  56. data/lib/spiderfw/env.rb +8 -1
  57. data/lib/spiderfw/home.rb +31 -4
  58. data/lib/spiderfw/http/server.rb +32 -14
  59. data/lib/spiderfw/model/mappers/mapper.rb +2 -2
  60. data/lib/spiderfw/model/mixins/tree.rb +7 -5
  61. data/lib/spiderfw/model/storage/db/adapters/mysql.rb +55 -2
  62. data/lib/spiderfw/model/storage/db/adapters/oracle.rb +80 -3
  63. data/lib/spiderfw/model/storage/db/connectors/jdbc_oracle.rb +3 -2
  64. data/lib/spiderfw/model/storage/db/connectors/oci8.rb +3 -2
  65. data/lib/spiderfw/model/storage/db/db_storage.rb +22 -0
  66. data/lib/spiderfw/setup/app_manager.rb +6 -2
  67. data/lib/spiderfw/site.rb +3 -1
  68. data/lib/spiderfw/spider.rb +145 -18
  69. data/lib/spiderfw/templates/blocks/parent_context.rb +3 -1
  70. data/lib/spiderfw/templates/template.rb +14 -5
  71. data/lib/spiderfw/widget/widget.rb +1 -0
  72. data/lib/spiderfw/widget/widget_attributes.rb +2 -2
  73. metadata +16 -16
  74. data/apps/master/po/spider_master.pot +0 -331
  75. data/apps/master/views/servants.shtml +0 -11
  76. data/apps/servant/lib/commands/discovery.rb +0 -0
  77. data/apps/servant/lib/resource.rb +0 -14
  78. data/apps/servant/lib/resources/db/mysql.rb +0 -35
  79. data/apps/servant/lib/resources/db.rb +0 -55
  80. data/apps/servant/lib/servant.rb +0 -88
  81. data/apps/servant/var/log/error.log +0 -1
data/lib/spiderfw/app.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'tsort'
2
3
 
3
4
  module Spider
4
5
 
@@ -244,6 +245,7 @@ module Spider
244
245
  @#{name} ||= #{options[:default].inspect}
245
246
  @#{name}
246
247
  end
248
+ alias :#{name}= :#{name}
247
249
  END_OF_EVAL
248
250
  class_eval(str)
249
251
  end
@@ -268,6 +270,7 @@ END_OF_EVAL
268
270
  array_attribute :authors
269
271
  array_attribute :depends
270
272
  array_attribute :depends_optional
273
+ array_attribute :load_after
271
274
  array_attribute :can_use
272
275
  array_attribute :gems
273
276
  array_attribute :gems_optional
@@ -315,20 +318,65 @@ END_OF_EVAL
315
318
  h
316
319
  end
317
320
 
318
- def to_json
321
+ def to_json(opts=nil)
319
322
  to_h.to_json
320
323
  end
321
324
 
322
325
  def self.parse_hash(h)
323
326
  spec = self.new
324
327
  h.each do |key, value|
325
- spec.send(:"#{key}", value)
328
+ if value.is_a?(Array)
329
+ spec.send(:"#{key}", *value)
330
+ else
331
+ spec.send(:"#{key}", value)
332
+ end
326
333
  end
327
334
  spec
328
335
  end
336
+
337
+ def load_after(*vals)
338
+ @load_after = vals unless vals.empty?
339
+ unless @load_after
340
+ return self.depends + self.depends_optional
341
+ end
342
+ end
329
343
 
330
344
  end
331
345
 
346
+ class RuntimeSort
347
+
348
+ def initialize
349
+ @apps = []
350
+ @apps_hash = {}
351
+ end
352
+
353
+ def add(app)
354
+ @apps << app
355
+ if app.is_a?(AppSpec)
356
+ @apps_hash[app.app_id] = app
357
+ else
358
+ @apps_hash[app] = app
359
+ end
360
+ end
361
+
362
+ def tsort_each_node(&block)
363
+ @apps.each(&block)
364
+ end
365
+
366
+ def tsort_each_child(node, &block)
367
+ return unless node.is_a?(AppSpec)
368
+ node.load_after.map{ |a| @apps_hash[a] }.each(&block)
369
+ end
370
+
371
+ def tsort
372
+ sorted = super
373
+ sorted.map{ |a| a.is_a?(AppSpec) ? a.app_id : a }
374
+ end
375
+
376
+ include TSort
377
+
378
+ end
379
+
332
380
  end
333
381
 
334
382
  end
@@ -39,17 +39,19 @@ class AppCommand < CmdParse::Command
39
39
  end
40
40
  list.set_execution_block do |args|
41
41
  if @installed
42
- require 'spiderfw'
42
+ require 'spiderfw/home'
43
+ home = Spider::Home.new(Dir.pwd)
43
44
  installed = {}
44
- Spider.home.list_apps.each do |app|
45
+ Spider.init_base
46
+ active = Spider.config.get('apps')
47
+ Spider.home.apps.each do |app, info|
45
48
  installed[app] = {
46
- :active => Spider.apps_by_path[app] ? true : false
49
+ :active => active.include?(app)
47
50
  }
48
- if appspec = Dir.glob("#{Spider.paths[:apps]}/#{app}/*.appspec")[0]
49
- info = Spider::App::AppSpec.load(appspec)
51
+ if spec = info[:spec]
50
52
  installed[app].merge!({
51
- :version => info.version
52
- })
53
+ :version => spec.version
54
+ })
53
55
  end
54
56
  end
55
57
  end
@@ -71,19 +73,19 @@ class AppCommand < CmdParse::Command
71
73
  end
72
74
  if @installed
73
75
  puts
74
- puts "*** INSTALLED APPS ***"
76
+ puts "*** "+_('INSTALLED APPS')+" ***"
75
77
  puts
76
78
  installed.keys.sort.each do |app|
77
79
  details = installed[app]
78
80
  str = app
79
81
  str += " #{details[:version]}" if details[:version]
80
- str += " (#{_('active')})" if details[:active]
82
+ str += " (#{_('not loaded')})" unless details[:active]
81
83
  puts str
82
84
  end
83
85
  end
84
86
  if @remote
85
87
  puts
86
- puts "*** REMOTE APPS ***"
88
+ puts "*** "+_('REMOTE APPS')+" ***"
87
89
  puts
88
90
  remote.keys.sort.each do |app|
89
91
  details = remote[app]
@@ -110,6 +112,7 @@ class AppCommand < CmdParse::Command
110
112
  @no_optional_gems = true
111
113
  }
112
114
  opt.on("--ssh-user [USERNAME]", _("SSH user")){ |s| @ssh_user = s }
115
+ opt.on("--no-activate", _("Don't activate installed apps")){ |s| @no_activate = true }
113
116
  end
114
117
  install.set_execution_block do |args|
115
118
  unless File.exist?('init.rb') && File.directory?('apps')
@@ -127,6 +130,7 @@ class AppCommand < CmdParse::Command
127
130
  puts "Grit not available; install Grit for Git support"
128
131
  end
129
132
  end
133
+
130
134
  apps = args
131
135
  existent = []
132
136
  apps.each do |app|
@@ -135,7 +139,6 @@ class AppCommand < CmdParse::Command
135
139
  existent << app
136
140
  end
137
141
  end
138
- apps -= existent
139
142
  require 'spiderfw/setup/app_manager'
140
143
  specs = []
141
144
  client = Spider::AppServerClient.new(@server_url)
@@ -155,7 +158,14 @@ class AppCommand < CmdParse::Command
155
158
  :no_optional_gems => @no_optional_gems
156
159
  }
157
160
  options[:ssh_user] = @ssh_user if @ssh_user
158
- Spider::AppManager.install(specs, Dir.pwd, options)
161
+ inst_specs = specs.reject!{ |s| existent.include? s.app_id }
162
+ Spider::AppManager.install(inst_specs, Dir.pwd, options)
163
+ unless @no_activate
164
+ require 'spiderfw/spider'
165
+ specs_hash = {}
166
+ specs.each{ |s| specs_hash[s.app_id] = s }
167
+ Spider.activate_apps(deps, specs_hash)
168
+ end
159
169
  end
160
170
  self.add_command(install)
161
171
 
@@ -52,9 +52,10 @@ class ConfigCommand < CmdParse::Command
52
52
  set = CmdParse::Command.new('set', false)
53
53
  set.short_desc = _("Set the value of a configuration option")
54
54
  set.set_execution_block do |args|
55
- require 'spiderfw'
55
+ require 'spiderfw/spider'
56
56
  require 'lib/spiderfw/config/configuration_editor'
57
57
  editor = Spider::ConfigurationEditor.new
58
+ Spider.init_base
58
59
  Spider.config.loaded_files.each do |f|
59
60
  editor.load(f)
60
61
  end
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module Spider
2
4
 
3
5
  class ConfigurationEditor
@@ -80,13 +82,13 @@ module Spider
80
82
 
81
83
  def commit_ml(res, key, val, curr)
82
84
  if curr
85
+ curr.instance_eval("def to_yaml_style; :multiline; end")
83
86
  res << {key => curr}.to_yaml.split("\n")[2..-1].join("\n") + "\n"
84
87
  else
85
88
  res << val
86
89
  end
87
90
 
88
91
  end
89
-
90
92
  @changed.each do |file, data|
91
93
  dirname = File.dirname(file)
92
94
  basename = File.basename(file)
@@ -111,7 +113,7 @@ module Spider
111
113
  end
112
114
  add_next = nil
113
115
  end
114
- if line =~ /(\s*)(\w[^:]+):\s*([\S]+)?$/
116
+ if line =~ /(\s*)(\w[^:]+):\s*(.+)?$/
115
117
  indent = $1
116
118
  key = $2
117
119
  value = $3
@@ -149,6 +151,7 @@ module Spider
149
151
  if value
150
152
  if curr
151
153
  res << indent
154
+ curr.instance_eval("def to_yaml_style; :inline; end")
152
155
  res << {key => curr}.to_yaml.split("\n")[1..-1].join("\n") + "\n"
153
156
  else
154
157
  res << line
@@ -186,8 +189,8 @@ module Spider
186
189
  end
187
190
 
188
191
  res.close
189
- File.mv(file, "#{file}.bak")
190
- File.mv(tmp_file, file)
192
+ FileUtils.mv(file, "#{file}.bak")
193
+ FileUtils.mv(tmp_file, file)
191
194
  end
192
195
  end
193
196
  end
@@ -4,6 +4,8 @@ module Spider
4
4
  :action => Proc.new{ |option| Spider.runmode = option unless Spider.runmode || $SPIDER_RUNMODE}
5
5
  )
6
6
 
7
+ config_option('apps', _('Apps to load'), :type => Array, :yaml_style => :inline)
8
+
7
9
  # Storage
8
10
  # config_option('storage.type', '')
9
11
  # config_option('storage.url', '')
@@ -37,7 +39,7 @@ module Spider
37
39
  config_option 'static_content.auto_publish', _("Automatically publish content to the home's public folder"),
38
40
  :type => Spider::DataTypes::Bool, :default => false
39
41
  # Client
40
- config_option 'client.text_editor', _("The text editor installed on the client")
42
+ config_option 'client.text_editor', _("The text editor installed on the client"), :default => 'textmate'
41
43
 
42
44
  # Templates
43
45
  config_option 'template.cache.disable', _("Refresh template cache every time"), :default => false, :type => Spider::DataTypes::Bool
@@ -26,6 +26,7 @@ module Spider; module Helpers
26
26
  :id_path => w.id_path,
27
27
  :full_id => w.full_id,
28
28
  :param => param_name(w),
29
+ :param_u => param_name(w, true),
29
30
  :pub_path => w.class.pub_url,
30
31
  :css_class => w.css_class,
31
32
  :css_classes => w.css_classes.uniq.join(' ')
@@ -49,7 +50,7 @@ module Spider; module Helpers
49
50
 
50
51
  module SceneMethods
51
52
 
52
- def param_name(widget_desc)
53
+ def param_name(widget_desc, urlencode=false)
53
54
  if (widget_desc.is_a?(Widget))
54
55
  id_path = widget_desc.id_path
55
56
  elsif (widget_desc.is_a?(Hash))
@@ -57,7 +58,9 @@ module Spider; module Helpers
57
58
  else
58
59
  id_path = widget_desc
59
60
  end
60
- pre = id_path.map{ |part| "[#{part}]"}.join('')
61
+ open = urlencode ? '%5B' : '['
62
+ close = urlencode ? '%5D' : ']'
63
+ pre = id_path.map{ |part| "#{open}#{part}#{close}"}.join('')
61
64
  end
62
65
 
63
66
  def params_for(widget_or_id_path, params)
@@ -71,4 +74,4 @@ module Spider; module Helpers
71
74
 
72
75
  end
73
76
 
74
- end; end
77
+ end; end
@@ -12,7 +12,7 @@ module Spider; module ControllerMixins
12
12
  klass.extend(ClassMethods)
13
13
  end
14
14
 
15
- def redirect(url, code=Spider::HTTP::MOVED_PERMANENTLY)
15
+ def redirect(url, code=Spider::HTTP::SEE_OTHER)
16
16
  debug "REDIRECTING TO #{url}"
17
17
  @request.session.persist if @request.session # It might be too late afterwards
18
18
  @response.status = code
@@ -26,6 +26,7 @@ module Spider; module ControllerMixins
26
26
  return '' unless url
27
27
  if (maps = Spider.conf.get('http.proxy_mapping'))
28
28
  maps.each do |proxy, spider|
29
+ spider ||= ''
29
30
  return proxy + url[spider.length..-1] if (spider == "" || url[0..spider.length-1] == spider)
30
31
  end
31
32
  end
@@ -191,18 +191,20 @@ module Spider; module ControllerMixins
191
191
  template = get_template(path, scene, options)
192
192
  end
193
193
  layout = nil
194
- chosen_layouts = options[:layout] || @layout
195
- chosen_layouts = [chosen_layouts] if chosen_layouts && !chosen_layouts.is_a?(Array)
196
- if (chosen_layouts)
197
- t = template
198
- layout = nil
199
- (chosen_layouts.length-1).downto(0) do |i|
200
- layout = init_layout(chosen_layouts[i])
201
- layout.template = t
202
- t = layout
194
+ unless options.key?(:layout) && !options[:layout]
195
+ chosen_layouts = options[:layout] || @layout
196
+ chosen_layouts = [chosen_layouts] if chosen_layouts && !chosen_layouts.is_a?(Array)
197
+ if (chosen_layouts)
198
+ t = template
199
+ layout = nil
200
+ (chosen_layouts.length-1).downto(0) do |i|
201
+ layout = init_layout(chosen_layouts[i])
202
+ layout.template = t
203
+ t = layout
204
+ end
203
205
  end
206
+ layout.init(scene) if layout
204
207
  end
205
- layout.init(scene) if layout
206
208
  init_widgets(template, layout)
207
209
  return template if done?
208
210
  if layout
data/lib/spiderfw/env.rb CHANGED
@@ -14,4 +14,11 @@ $:.push($SPIDER_PATH)
14
14
  # Dir.chdir($SPIDER_RUN_PATH)
15
15
 
16
16
  $SPIDER_RUNMODE ||= ENV['SPIDER_RUNMODE']
17
- $SPIDER_CONFIG_SETS = ENV['SPIDER_CONFIG_SETS'].split(/\s+,\s+/) if ENV['SPIDER_CONFIG_SETS']
17
+ $SPIDER_CONFIG_SETS = ENV['SPIDER_CONFIG_SETS'].split(/\s+,\s+/) if ENV['SPIDER_CONFIG_SETS']
18
+
19
+ $SPIDER_SCRIPT = $0
20
+ if $SPIDER_SCRIPT =~ /Rack|Passenger/
21
+ $SPIDER_RACK = true
22
+ $SPIDER_SCRIPT = ::File.expand_path('./bin/spider')
23
+ $SPIDER_NO_RESPAWN = true
24
+ end
data/lib/spiderfw/home.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  require 'pathname'
2
+ require 'spiderfw/spider'
3
+ require 'spiderfw/app'
4
+
2
5
 
3
6
  module Spider
4
7
 
@@ -21,17 +24,41 @@ module Spider
21
24
  def load_apps(*args)
22
25
  Spider.load_apps(*args)
23
26
  end
27
+
28
+ def apps_path
29
+ @apps_path = Spider.paths[:apps] if Spider.respond_to?(:paths)
30
+ @apps_path ||= File.join(@path, 'apps')
31
+ end
24
32
 
25
33
  def list_apps
26
- apps_path = Spider.respond_to?(:paths) ? Spider.paths[:apps] : File.join(@path, 'apps')
27
- apps_dir = Pathname.new(apps_path)
34
+ apps_dir = Pathname.new(self.apps_path)
35
+ paths = Spider.find_all_apps(self.apps_path)
28
36
  apps = []
29
- Dir.glob("#{apps_path}/**/_init.rb").each do |path|
30
- dir = Pathname.new(File.dirname(path))
37
+ paths.each do |path|
38
+ dir = Pathname.new(path)
31
39
  apps << dir.relative_path_from(apps_dir).to_s
32
40
  end
33
41
  apps
34
42
  end
43
+
44
+ def apps
45
+ apps = {}
46
+ list_apps.each do |path|
47
+ spec_file = Dir.glob(File.join(self.apps_path, path, "*.appspec")).first
48
+ spec = nil
49
+ if spec_file
50
+ spec = Spider::App::AppSpec.load(spec_file)
51
+ app_name = spec.app_id
52
+ else
53
+ app_name = path
54
+ end
55
+ apps[app_name] = {
56
+ :path => path,
57
+ :spec => spec
58
+ }
59
+ end
60
+ apps
61
+ end
35
62
 
36
63
  end
37
64
 
@@ -78,18 +78,18 @@ module Spider; module HTTP
78
78
  :ssl_cert => options[:ssl_cert], :ssl_private_key => options[:ssl_key])
79
79
  end
80
80
  end
81
- do_shutdown = lambda{ |arg|
81
+ do_shutdown = lambda{
82
+ Debugger.post_mortem = false
83
+ # debugger
82
84
  server.shutdown
83
85
  ssl_server.shutdown if ssl_server
84
- Spider.shutdown
85
86
  pid_file = File.join(Spider.paths[:var], 'run/server.pid')
86
87
  begin
87
88
  File.unlink(pid_file)
88
89
  rescue Errno::ENOENT
89
90
  end
90
91
  }
91
- trap('TERM', &do_shutdown)
92
- trap('INT', &do_shutdown)
92
+ Spider.on_shutdown(&do_shutdown)
93
93
 
94
94
  thread.join
95
95
  ssl_thread.join if ssl_thread
@@ -103,7 +103,11 @@ module Spider; module HTTP
103
103
  File.open(pid_file, 'w') do |f|
104
104
  f.write(Process.pid)
105
105
  end
106
+ $SPIDER_SCRIPT ||= $0
106
107
  $0 = process_name
108
+ STDIN.reopen "/dev/null" # Free file descriptors and
109
+ STDOUT.reopen "/dev/null", "a" # point them somewhere sensible
110
+ STDERR.reopen STDOUT # STDOUT/STDERR should go to a logfile
107
111
  start.call
108
112
  end
109
113
  Process.detach(forked)
@@ -125,7 +129,11 @@ module Spider; module HTTP
125
129
  Spider.logger.error("Install 'fssm' gem to enable respawning")
126
130
  end
127
131
  end
128
- start.call unless spawner_started
132
+ unless spawner_started
133
+ Spider.main_process_startup
134
+ Spider.startup
135
+ start.call
136
+ end
129
137
  end
130
138
  end
131
139
 
@@ -148,15 +156,22 @@ module Spider; module HTTP
148
156
  rd, wr = IO.pipe
149
157
  if pid = fork
150
158
  # Spawner
159
+ Spider.logger.debug("Spawner forked")
151
160
  @child_pid = pid
152
- exit_spawner = lambda{
153
- Spider.logger.debug "Spawner exiting"
154
- exit
155
- }
156
- trap('TERM', exit_spawner)
157
- trap('INT', exit_spawner)
158
-
159
- $0 = 'spider-spawner'
161
+
162
+ unless @already_forked
163
+ Spider.main_process_startup
164
+ exit_spawner = lambda{
165
+ Spider.logger.debug "Spawner exiting"
166
+ Process.kill 'KILL', @monitor_thread[:spawner_child_pid]
167
+ }
168
+ Spider.on_main_process_shutdown(&exit_spawner)
169
+ Spider.main_process_startup
170
+ $SPIDER_SCRIPT ||= $0
171
+ $0 = 'spider-spawner'
172
+ end
173
+ @already_forked = true
174
+
160
175
  wr.close
161
176
  @monitor_thread[:spawner_child_pid] = pid
162
177
  # TODO
@@ -166,6 +181,9 @@ module Spider; module HTTP
166
181
 
167
182
  else
168
183
  # Child
184
+ $SPIDER_SPAWNED = true
185
+ trap('TERM'){ }
186
+ trap('INT'){ }
169
187
  rd.close
170
188
  Spider.spawner = wr
171
189
  return unless @actions[action]
@@ -221,4 +239,4 @@ module Spider; module HTTP
221
239
 
222
240
  end
223
241
 
224
- end; end
242
+ end; end
@@ -149,8 +149,8 @@ module Spider; module Model
149
149
  if (!el.integrated? && el.required? && (mode == :insert || obj.element_modified?(el)) && !obj.element_has_value?(el))
150
150
  raise RequiredError.new(el)
151
151
  end
152
- if (el.unique? && !el.integrated? && obj.element_modified?(el))
153
- existent = @model.where(el.name => obj.get(el))
152
+ if (el.unique? && !el.integrated? && obj.element_modified?(el) && curr_val = obj.get(el))
153
+ existent = @model.where(el.name => curr_val)
154
154
  if (mode == :insert && existent.length > 0) || (mode == :update && existent.length > 1)
155
155
  raise NotUniqueError.new(el)
156
156
  end
@@ -17,13 +17,13 @@ module Spider; module Model
17
17
  c = Condition.and
18
18
  c[left_el] = (left..right)
19
19
  q = Query.new(c)
20
- q.order_by(left_el)
20
+ q.order_by(left_el) unless element.attributes[:order] == false
21
21
  res = element.model.find(q)
22
22
  return [] unless res
23
23
  right_stack = []
24
24
  res.each do |obj|
25
25
  if (right_stack.length > 0)
26
- right_stack.pop while (right_stack[right_stack.length-1] < obj.get(right_el))
26
+ right_stack.pop while (right_stack[right_stack.length-1] && right_stack[right_stack.length-1] < obj.get(right_el))
27
27
  obj.set(element.attributes[:tree_depth], right_stack.length)
28
28
  end
29
29
  right_stack << obj.get(right_el)
@@ -47,7 +47,8 @@ module Spider; module Model
47
47
  attributes[:tree_position] ||= :"#{name}_position"
48
48
  choice(attributes[:reverse], self, attributes[:reverse_attributes])
49
49
  element(name, self, attributes)
50
- element(attributes[:tree_left], Fixnum, :hidden => true, :tree_element => name, :order => true)
50
+ order = attributes[:order] == false ? false : true
51
+ element(attributes[:tree_left], Fixnum, :hidden => true, :tree_element => name, :order => order)
51
52
  element(attributes[:tree_right], Fixnum, :hidden => true, :tree_element => name)
52
53
  element(attributes[:tree_depth], Fixnum, :unmapped => true, :hidden => true, :tree_element => name)
53
54
  element(attributes[:tree_position], Fixnum, :unmapped => true, :hidden => true, :tree_element => name)
@@ -162,9 +163,10 @@ module Spider; module Model
162
163
  cnt = 0
163
164
  parent.get(name).each do |sub|
164
165
  cnt += 1
165
- return cnt if sub == self
166
+ break if sub == self
166
167
  end
167
- return nil
168
+ cnt = nil if cnt = 0
169
+ cnt
168
170
  end
169
171
 
170
172
  end
@@ -15,7 +15,7 @@ module Spider; module Model; module Storage; module Db
15
15
  :transactions => true,
16
16
  :foreign_keys => true
17
17
  }
18
- @reserved_keywords = superclass.reserved_keywords
18
+ @reserved_keywords = superclass.reserved_keywords + ['interval']
19
19
  @safe_conversions = DbStorage.safe_conversions.merge({
20
20
  'CHAR' => ['VARCHAR', 'CLOB'],
21
21
  'VARCHAR' => ['CLOB'],
@@ -337,6 +337,7 @@ module Spider; module Model; module Storage; module Db
337
337
  columns = {}
338
338
  primary_keys = []
339
339
  foreign_keys = []
340
+ order = []
340
341
  connection do |c|
341
342
  res = c.query("select * from #{table} where 1=0")
342
343
  fields = res.fetch_fields
@@ -363,6 +364,7 @@ module Spider; module Model; module Storage; module Db
363
364
  col[flag_name] = (flags & flag_val == 0) ? false : true
364
365
  end
365
366
  columns[f.name] = col
367
+ order << f.name
366
368
  primary_keys << f.name if f.is_pri_key?
367
369
  end
368
370
  res = c.query("select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE constraint_schema = '#{@db_name}' and table_name = '#{table}'")
@@ -379,7 +381,7 @@ module Spider; module Model; module Storage; module Db
379
381
  end
380
382
 
381
383
  end
382
- return {:columns => columns, :primary_keys => primary_keys, :foreign_key_constraints => foreign_keys}
384
+ return {:columns => columns, :order => order, :primary_keys => primary_keys, :foreign_key_constraints => foreign_keys}
383
385
  end
384
386
 
385
387
  def table_exists?(table)
@@ -394,6 +396,57 @@ module Spider; module Model; module Storage; module Db
394
396
  end
395
397
  end
396
398
 
399
+ def get_table_create_sql(table)
400
+ sql = nil
401
+ connection do |c|
402
+ res = c.query("SHOW CREATE TABLE #{table}")
403
+ sql = res.fetch_row[1]
404
+ end
405
+ sql
406
+ end
407
+
408
+
409
+ def dump_table_data(table, stream)
410
+ connection do |c|
411
+ res = c.query("select * from #{table}")
412
+ num = res.num_rows
413
+ if num > 0
414
+ fields = res.fetch_fields
415
+ stream << "INSERT INTO `#{table}` (#{fields.map{ |f| "`#{f.name}`"}.join(', ')})\n"
416
+ stream << "VALUES\n"
417
+ cnt = 0
418
+ while row = res.fetch_row
419
+ cnt += 1
420
+ stream << "("
421
+ fields.each_with_index do |f, i|
422
+ stream << dump_value(row[i], f)
423
+ stream << ", " if i < fields.length - 1
424
+ end
425
+ stream << ")"
426
+ if cnt < num
427
+ stream << ",\n"
428
+ else
429
+ stream << ";\n"
430
+ end
431
+ end
432
+ stream << "\n\n"
433
+ end
434
+ end
435
+ end
436
+
437
+ def dump_value(val, field)
438
+ return 'NULL' if val.nil?
439
+ type = self.class.field_types[field.type]
440
+ if ['CHAR', 'VARCHAR', 'BLOB', 'TINY_BLOB', 'MEDIUM_BLOB', 'LONG_BLOB'].include?(type)
441
+ val = val.gsub("'", "''").gsub("\n", '\n').gsub("\r", '\r')
442
+ return "'#{val}'"
443
+ elsif ['DATE', 'TIME', 'DATETIME'].include?(type)
444
+ return "'#{val}'"
445
+ else
446
+ return val.to_s
447
+ end
448
+ end
449
+
397
450
  # Schema methods
398
451
 
399
452
  def table_name(name)