spiderfw 0.6.5 → 0.6.6

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