omf_web 0.9.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +31 -0
  3. data/bin/omf_web_server.rb +157 -0
  4. data/doc/screenshot2.png +0 -0
  5. data/doc/widget_detail.png +0 -0
  6. data/example/demo/data_sources/downloads.rb +2 -1
  7. data/example/simple/README.md +12 -13
  8. data/example/simple/create_waveform.rb +29 -0
  9. data/example/simple/introduction.md +17 -0
  10. data/example/simple/sample.sq3 +0 -0
  11. data/example/simple/sample.sql +1008 -0
  12. data/example/simple/simple.yaml +62 -0
  13. data/example/simple/simple_dynamic.yaml +66 -0
  14. data/lib/irods4r/file.rb +15 -14
  15. data/lib/irods4r/icommands.rb +18 -18
  16. data/lib/irods4r.rb +9 -9
  17. data/lib/omf-web/config.ru +41 -16
  18. data/lib/omf-web/content/git_repository.rb +32 -31
  19. data/lib/omf-web/content/irods_repository.rb +34 -33
  20. data/lib/omf-web/content/repository.rb +48 -44
  21. data/lib/omf-web/data_source_proxy.rb +33 -22
  22. data/lib/omf-web/rack/session_authenticator.rb +48 -12
  23. data/lib/omf-web/rack/tab_mapper.rb +30 -36
  24. data/lib/omf-web/rack/websocket_handler.rb +26 -25
  25. data/lib/omf-web/session_store.rb +16 -13
  26. data/lib/omf-web/theme/abstract_page.rb +26 -22
  27. data/lib/omf-web/theme/bright/page.rb +84 -34
  28. data/lib/omf-web/theme/bright/stacked_renderer.rb +20 -19
  29. data/lib/omf-web/theme.rb +14 -9
  30. data/lib/omf-web/thin/runner.rb +38 -36
  31. data/lib/omf-web/thin/server.rb +255 -0
  32. data/lib/omf-web/version.rb +1 -1
  33. data/lib/omf-web/widget/data_widget.rb +6 -6
  34. data/lib/omf-web/widget/text/maruku/helpers.rb +33 -30
  35. data/lib/omf-web/widget/text/maruku/input/parse_block.rb +117 -117
  36. data/lib/omf-web/widget/text/maruku/output/to_html.rb +155 -154
  37. data/lib/omf-web/widget/text/maruku.rb +17 -16
  38. data/omf_web.gemspec +6 -2
  39. data/sample.sq3 +0 -0
  40. data/share/htdocs/graph/js/gauge.js +524 -0
  41. data/share/htdocs/vendor/VERSION_MAP.yaml +3 -3
  42. data/share/htdocs/vendor/backbone-1.0.0/backbone.js +1571 -0
  43. data/share/htdocs/vendor/d3-3.0/LICENSE.brewer.txt +38 -0
  44. data/share/htdocs/vendor/d3-3.0/colorbrewer.js +1 -0
  45. data/share/htdocs/vendor/d3-3.0/d3.js +8810 -0
  46. data/share/htdocs/vendor/d3-3.0/d3.min.js +5 -0
  47. data/share/htdocs/vendor/geo_json/Readme.txt +71 -0
  48. data/share/htdocs/vendor/geo_json/regions.json +41 -0
  49. data/share/htdocs/vendor/geo_json/switzerland.json +24 -0
  50. data/share/htdocs/vendor/geo_json/world.json +497 -0
  51. data/share/htdocs/vendor/nv_d3/js/nv.d3.js +8801 -4447
  52. data/share/htdocs/vendor/spin/jquery.spin.js +46 -0
  53. data/share/htdocs/vendor/spin/spin.js +349 -0
  54. data/share/htdocs/vendor/spin/spin.min.js +1 -0
  55. data/share/htdocs/vendor/underscore-1.4.4/underscore.js +1227 -0
  56. metadata +63 -48
  57. data/example/simple/data_sources/gimi31.sq3 +0 -0
  58. data/example/simple/data_sources/ping_source.rb +0 -56
  59. data/example/simple/simple_viz_server.rb +0 -39
  60. data/example/simple/widgets/charts_tab.yaml +0 -38
  61. data/share/.DS_Store +0 -0
  62. data/share/htdocs/.DS_Store +0 -0
  63. data/share/htdocs/vendor/backbone-0.5.3/backbone.js +0 -1158
  64. data/share/htdocs/vendor/underscore-1.2.1/underscore.js +0 -958
@@ -1,58 +1,59 @@
1
1
 
2
2
  require 'omf-web/theme/bright/widget_chrome'
3
+ require 'omf-web/theme/bright/layout_renderer'
3
4
 
4
5
  module OMF::Web::Theme
5
-
6
- class StackedRenderer
7
-
6
+
7
+ class StackedRenderer < LayoutRenderer
8
+
8
9
  def initialize(stacked_widget, widgets, active_index, opts)
9
10
  @stacked_widget = stacked_widget
10
11
  @widgets = widgets
11
12
  @active_index = active_index
12
13
  @helper = StackedRendererHelper.new(widgets, active_index, opts)
13
14
  @opts = opts
14
- end
15
-
15
+ end
16
+
16
17
  def to_html()
17
18
  wp = "w#{@helper.object_id}"
18
19
  @opts[:menu] = @widgets.each_with_index.map do |w, i|
19
- wc = w.widget_type.split('/').inject([]) do |a, e|
20
- a << (a.empty? ? e : "#{a[-1]}_#{e}")
20
+ wc = w.widget_type.split('/').inject([]) do |a, e|
21
+ a << (a.empty? ? e : "#{a[-1]}_#{e}")
21
22
  end
22
23
  {
23
- :name => w.name,
24
- :class => wc.join(' '),
25
- :is_active => (@active_index == i),
24
+ :name => w.name,
25
+ :class => wc.join(' '),
26
+ :is_active => (@active_index == i),
26
27
  :id => "#{wp}_l_#{i}",
27
28
  :js_function => 'OML.show_widget',
28
29
  :inner_class => wp,
29
30
  :index => i,
30
31
  :widget_id => w.dom_id
31
32
  }
32
- end
33
+ end
33
34
  WidgetChrome.new(@stacked_widget, @helper, @opts).to_html
34
- end
35
- end
35
+ end
36
+ end
36
37
 
37
38
  class StackedRendererHelper < Erector::Widget
38
-
39
+
39
40
  def initialize(widgets, active_index, opts)
40
41
  super opts
41
42
  @widgets = widgets
42
43
  @active_index = active_index
43
- end
44
+ end
44
45
 
45
46
  def content()
46
47
  #widget @active_widget
47
- widgets = @widgets
48
+ widgets = @widgets
48
49
  prefix = "w#{self.object_id}"
49
50
  @widgets.each_with_index do |w, i|
50
51
  style = i == @active_index ? '' : 'display:none'
51
52
  div :id => "#{prefix}_#{i}", :class => prefix, :style => style do
52
- rawtext w.content.to_html
53
+ rawtext w.content.to_html
53
54
  end
54
- end
55
+ end
55
56
  end
56
-
57
+
57
58
  end
58
59
  end
data/lib/omf-web/theme.rb CHANGED
@@ -2,29 +2,34 @@
2
2
 
3
3
  module OMF::Web::Theme
4
4
  extend OMF::Common::Loggable
5
-
5
+
6
6
  DEFAULT_THEME = 'omf-web/theme/bright'
7
7
  @@search_order = [DEFAULT_THEME] # default theme
8
8
  @@loaded = {}
9
9
  @@additional_renderers = {}
10
-
10
+
11
11
  def self.theme=(theme)
12
12
  if theme
13
13
  unless theme.match '.*/'
14
14
  theme = "omf-web/theme/#{theme}" # add default name space
15
15
  end
16
16
  @@loaded = {}
17
- @@search_order = [theme]
17
+ @@search_order = [theme]
18
18
  Kernel::require "#{theme}/init"
19
19
  end
20
20
  end
21
-
21
+
22
+ def self.include_css(css_file)
23
+ ::Kernel.require 'omf-web/theme/abstract_page'
24
+ OMF::Web::Theme::AbstractPage.add_depends_on(:css, "/resource/#{css_file}")
25
+ end
26
+
22
27
  def self.register_renderer(name, klass, theme = DEFAULT_THEME)
23
28
  tr = @@additional_renderers[theme.to_s] ||= {}
24
29
  tr[name] = klass
25
30
  end
26
-
27
- # Set additional themes to search in the given order for
31
+
32
+ # Set additional themes to search in the given order for
28
33
  # implementations of renderes. Allows for partial override
29
34
  # in new themes.
30
35
  #
@@ -32,8 +37,8 @@ module OMF::Web::Theme
32
37
  @@loaded = {}
33
38
  @@search_order = search_order if search_order
34
39
  end
35
-
36
-
40
+
41
+
37
42
  def self.require(name)
38
43
  name = name.to_sym
39
44
  return if @@loaded[name]
@@ -45,7 +50,7 @@ module OMF::Web::Theme
45
50
  Kernel::require "#{theme}/#{name}"
46
51
  end
47
52
  @@loaded[name] = true
48
- debug "Using renderer '#{theme}/#{name}'"
53
+ debug "Using renderer '#{theme}/#{name}'"
49
54
  return
50
55
  rescue LoadError => le
51
56
  # Move on to the next one
@@ -11,26 +11,27 @@ module Thin
11
11
  true # will be verified later
12
12
  end
13
13
  end
14
- end
14
+ end
15
15
 
16
16
  module OMF::Web
17
17
  class Runner < Thin::Runner
18
18
  include OMF::Common::Loggable
19
-
19
+
20
20
  @@instance = nil
21
-
21
+
22
22
  def self.instance
23
23
  @@instance
24
24
  end
25
-
25
+
26
26
  attr_reader :options
27
-
27
+
28
28
  def initialize(argv, opts = {})
29
29
  raise "SINGLETON" if @@instance
30
-
30
+ @@instance = self
31
+
31
32
  @argv = argv
32
33
  sopts = opts.delete(:ssl) # runner has it's own idea of ssl options
33
-
34
+
34
35
  # Default options values
35
36
  app_name = opts[:app_name] || 'omf_web_app'
36
37
  @options = {
@@ -46,68 +47,70 @@ module OMF::Web
46
47
  :max_persistent_conns => Thin::Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
47
48
  :require => [],
48
49
  :wait => Thin::Controllers::Cluster::DEFAULT_WAIT_TIME,
49
-
50
+
50
51
  :rackup => File.dirname(__FILE__) + '/../config.ru',
51
52
  :static_dirs => ["#{File.dirname(__FILE__)}/../../../share/htdocs"],
52
53
  :static_dirs_pre => ["./resources"], # directories to prepend to 'static_dirs'
53
-
54
+
54
55
  :handlers => {} # procs to call at various times of the server's life cycle
55
56
  }.merge(opts)
56
57
  # Search path for resource files is concatination of 'pre' and 'standard' static dirs
57
58
  @options[:static_dirs] = @options[:static_dirs_pre].concat(@options[:static_dirs])
58
-
59
-
60
-
59
+
60
+
61
+
61
62
  print_options = false
62
63
  p = parser
63
64
  p.separator ""
64
- p.separator "OMF options:"
65
- p.on("--theme THEME", "Select web theme") do |t| OMF::Web::Theme.theme = t end
66
-
67
- p.separator ""
68
- p.separator "Testing options:"
69
- p.on("--disable-https", "Run server without SSL") do sopts = nil end
70
- p.on("--print-options", "Print option settings after parsing command lines args") do print_options = true end
71
-
65
+ p.separator "OMF Web options:"
66
+ p.on("--theme THEME", "Select web theme") do |t| OMF::Web::Theme.theme = t end
67
+
72
68
  # Allow application to add it's own parsing options
73
69
  if ph = @options[:handlers][:pre_parse]
74
- ph.call(p)
70
+ ph.arity == 1 ? ph.call(p) : ph.call(p, self)
75
71
  end
76
72
 
73
+ p.separator ""
74
+ p.separator "Testing options:"
75
+ p.on("--disable-https", "Run server without SSL") do sopts = nil end
76
+ p.on("--print-options", "Print option settings after parsing command lines args") do print_options = true end
77
+ p.separator ""
78
+
77
79
  parse!
78
- # WHY IS THIS HERE
79
- # unless life_cycle(:post_parse)
80
- # puts p.to_s
81
- # abort()
82
- # end
80
+
83
81
  if sopts
84
82
  @options[:ssl] = true
85
83
  @options[:ssl_key_file] ||= sopts[:key_file]
86
84
  @options[:ssl_cert_file] ||= sopts[:cert_file]
87
85
  @options[:ssl_verify] ||= sopts[:verify_peer]
88
86
  end
87
+ life_cycle(:post_parse)
89
88
 
90
89
  # Change the name of the root logger so we can apply different logging
91
- # policies depending on environment.
90
+ # policies depending on environment.
92
91
  #
93
92
  OMF::Common::Loggable.set_environment @options[:environment]
94
93
 
94
+ if css = opts[:include_css]
95
+ require 'omf-web/theme'
96
+ OMF::Web::Theme.include_css(css)
97
+ end
98
+
95
99
  if print_options
96
100
  require 'pp'
97
101
  pp @options
98
- end
99
-
100
- @@instance = self
102
+ end
103
+
101
104
  end
102
-
105
+
103
106
  def life_cycle(step, &exception_block)
104
107
  begin
105
108
  if (p = @options[:handlers][step])
106
- p.call()
109
+ p.arity == 0 ? p.call() : p.call(self)
107
110
  end
108
111
  rescue => ex
109
112
  if exception_block
110
- begin
113
+ begin
111
114
  exception_block.call(ex)
112
115
  rescue => ex2
113
116
  error ex2
@@ -118,8 +121,8 @@ module OMF::Web
118
121
  debug "#{ex.backtrace.join("\n")}"
119
122
  end
120
123
  end
121
- end
122
-
124
+ end
125
+
123
126
  def run!
124
127
  if theme = @options[:theme]
125
128
  require 'omf-web/theme'
@@ -131,4 +134,3 @@ module OMF::Web
131
134
  end
132
135
 
133
136
 
134
-
@@ -0,0 +1,255 @@
1
+ require 'json'
2
+ require 'omf_common/lobject'
3
+ require 'omf_web'
4
+
5
+ module OMF::Web
6
+
7
+ # Most of the code to run an OMF Web server from a configuration file
8
+ #
9
+ # USAGE:
10
+ #
11
+ #
12
+ class Server < OMF::Common::LObject
13
+
14
+ def self.start(server_name, description, top_dir, opts = {})
15
+ self.new(server_name, description, top_dir, opts)
16
+ end
17
+
18
+ def initialize(server_name, description, top_dir, opts)
19
+ OMF::Common::Loggable.init_log server_name
20
+
21
+ opts = {
22
+ static_dirs_pre: ["#{top_dir}/htdocs"],
23
+ handlers: {
24
+ pre_parse: lambda do |p, runner|
25
+ p.on("--config CONF_FILE", "File holding description of web site") {|f| runner.options[:omf_config_file] = f}
26
+ p.on("--top-dir DIR", "Directory to start from for relative data paths [directory of config file]") {|td| @top_dir = td }
27
+ end,
28
+ post_parse: lambda { |r| load_environment(r.options) },
29
+ },
30
+ authentication: {
31
+ required: false
32
+ }
33
+ }.merge(opts)
34
+
35
+ @top_dir = top_dir
36
+ @databases = {}
37
+
38
+ OMF::Web.start(opts)
39
+ end
40
+
41
+ def load_environment(opts)
42
+ unless cf = opts[:omf_config_file]
43
+ puts "Missing config file"
44
+ abort
45
+ end
46
+
47
+ unless File.readable? cf
48
+ puts "Can't read config file '#{cf}'"
49
+ abort
50
+ end
51
+
52
+ @top_dir ||= File.dirname(cf)
53
+ cfg = _rec_sym_keys(YAML.load_file(cf))
54
+
55
+ (cfg[:server] || {}).each do |k, v|
56
+ k = k.to_sym
57
+ case k
58
+ when :port
59
+ opts[:port] = v.to_i
60
+ else
61
+ opts[k] = v
62
+ end
63
+ end
64
+ (cfg[:data_sources] || []).each do |ds|
65
+ load_datasource(ds)
66
+ end
67
+ (cfg[:repositories] || []).each do |repo|
68
+ load_repository(repo)
69
+ end
70
+
71
+ unless wa = cfg[:widgets]
72
+ puts "Can't find 'widgets' section in config file '#{cf}' - #{cfg.keys}"
73
+ abort
74
+ end
75
+ wa.each do |w|
76
+ OMF::Web.register_widget w
77
+ end
78
+ end
79
+
80
+ def load_datasource(config)
81
+ unless id = config[:id]
82
+ puts "Missing id in datasource configuration"
83
+ abort
84
+ end
85
+ case type = config[:type] || 'database'
86
+ when 'database'
87
+ load_database(config)
88
+ when 'file'
89
+ load_datasource_file(id, config)
90
+ else
91
+ abort "Unknown datasource type '#{type}'."
92
+ end
93
+ end
94
+
95
+ def load_database(config)
96
+ unless table_name = config[:table]
97
+ puts "Missing 'table' in datasource configuration '#{id}'"
98
+ abort
99
+ end
100
+ unless db_cfg = config[:database]
101
+ puts "Missing database configuration in datasource '#{id}'"
102
+ abort
103
+ end
104
+ db = get_database(db_cfg)
105
+ unless table = db.create_table(table_name)
106
+ puts "Can't find table '#{table_name}' in database '#{db}'"
107
+ abort
108
+ end
109
+ OMF::Web.register_datasource table, name: id
110
+ end
111
+
112
+ def get_database(config)
113
+ require 'omf_oml/table'
114
+ require 'omf_oml/sql_source'
115
+
116
+ if config.is_a? String
117
+ if db = @databases[config]
118
+ return db
119
+ end
120
+ puts "Database '#{config}' not defined - (#{@databases.keys})"
121
+ abort
122
+ end
123
+ unless id = config[:id]
124
+ puts "Missing id in database configuration"
125
+ abort
126
+ end
127
+ # unless id = config[:id]
128
+ # puts "Database '#{config}' not defined - (#{@databases.keys})"
129
+ # abort
130
+ # end
131
+ unless url = config[:url]
132
+ puts "Missing URL for database '#{id}'"
133
+ abort
134
+ end
135
+ if url.start_with?('sqlite://') && ! url.start_with?('sqlite:///')
136
+ # inject top dir
137
+ url.insert('sqlite://'.length, @top_dir + '/')
138
+ end
139
+ puts "URL: #{url}"
140
+ begin
141
+ return @databases[id] = OMF::OML::OmlSqlSource.new(url, :check_interval => 3.0)
142
+ rescue Exception => ex
143
+ puts "Can't connect to database '#{id}' - #{ex}"
144
+ abort
145
+ end
146
+ end
147
+
148
+ # The data to be served as a datasource is contained in a file. We
149
+ # currently support CSV with headers, and JSON which turns into a
150
+ # 1 col by 1 row datasource.
151
+ #
152
+ def load_datasource_file(name, opts)
153
+ unless file = opts[:file]
154
+ puts "Data source file is not defined in '#{opts}'"
155
+ abort
156
+ end
157
+ unless file.start_with? '/'
158
+ file = File.join(@top_dir, file)
159
+ end
160
+ unless File.readable? file
161
+ puts "Can't read file '#{file}'"
162
+ abort
163
+ end
164
+ case content_type = opts[:content_type].to_s
165
+ when 'json'
166
+ ds = JSONDataSource.new(file)
167
+ when 'csv'
168
+ require 'omf_oml/csv_table'
169
+ ds = OMF::OML::OmlCsvTable.create name, file, has_csv_header: true
170
+ else
171
+ puts "Unknown content type '#{content_type}'"
172
+ abort
173
+ end
174
+ OMF::Web.register_datasource ds, name: name
175
+ end
176
+
177
+ def load_repository(config)
178
+ unless id = config[:id]
179
+ puts "Missing id in respository configuration"
180
+ abort
181
+ end
182
+ unless type = config[:type]
183
+ puts "Missing 'type' in respository configuration '#{id}'"
184
+ abort
185
+ end
186
+
187
+ require 'omf-web/content/repository'
188
+ case type
189
+ when 'file'
190
+ unless top_dir = config[:top_dir]
191
+ puts "Missing 'top_dir' in respository configuration '#{id}'"
192
+ abort
193
+ end
194
+ unless top_dir.start_with? '/'
195
+ top_dir = File.join(@top_dir, top_dir)
196
+ end
197
+ OMF::Web::ContentRepository.register_repo(id, type: :file, top_dir: top_dir)
198
+ else
199
+ puts "Unknown repository type '#{type}'. Only supporting 'file'."
200
+ abort
201
+ end
202
+
203
+ end
204
+
205
+ # Recusively Symbolize keys of hash
206
+ #
207
+ def _rec_sym_keys(hash)
208
+ h = {}
209
+ hash.each do |k, v|
210
+ if v.is_a? Hash
211
+ v = _rec_sym_keys(v)
212
+ elsif v.is_a? Array
213
+ v = v.map {|e| e.is_a?(Hash) ? _rec_sym_keys(e) : e }
214
+ end
215
+ h[k.to_sym] = v
216
+ end
217
+ h
218
+ end
219
+
220
+
221
+ # This class simulates a DataSource to transfer a JSON file as a database with one row and column
222
+
223
+
224
+ class JSONDataSource < OMF::Common::LObject
225
+
226
+ def initialize(file)
227
+ raw = File.read(file)
228
+ @content = [[JSON.parse(raw)]]
229
+ end
230
+
231
+ # * rows Returns an array of rows
232
+ # * on_content_changed(lambda{action, rows}) Call provided block with actions :added, :removed
233
+ # * create_sliced_table (optional)
234
+ # * release Not exactly sure when that is being used
235
+ # * schema Schema of row
236
+ # * offset
237
+ def rows
238
+ @content
239
+ end
240
+
241
+ def offset
242
+ 0
243
+ end
244
+
245
+ def schema
246
+ require 'omf_oml/schema'
247
+ OMF::OML::OmlSchema.create([[:content]])
248
+ end
249
+
250
+ def on_content_changed(*args)
251
+ # do nothing
252
+ end
253
+ end
254
+ end # class
255
+ end # module
@@ -1,7 +1,7 @@
1
1
 
2
2
  module OMF
3
3
  module Web
4
- VERSION = '0.9.9'
4
+ VERSION = '1.0.0'
5
5
  # Used for finding the example directory
6
6
  TOP_DIR = File.dirname(File.dirname(File.dirname(__FILE__)))
7
7
  end
@@ -28,8 +28,8 @@ module OMF::Web::Widget
28
28
  opts[:js_url] = "graph/js/#{vizType}.js"
29
29
  opts[:js_class] = "OML.#{vizType}"
30
30
  opts[:base_el] = "\##{dom_id}"
31
- super opts
32
-
31
+ super opts
32
+
33
33
  if (ds = opts.delete(:data_source))
34
34
  # single source
35
35
  data_sources = {:default => ds}
@@ -49,10 +49,10 @@ module OMF::Web::Widget
49
49
  end
50
50
  #puts "DTA_WIDGTE>>> #{opts[:data_sources].inspect}"
51
51
  end
52
-
53
- # This is the DOM id which should be used by the renderer for this widget.
52
+
53
+ # This is the DOM id which should be used by the renderer for this widget.
54
54
  # We need to keep this here as various renderes at various levels may need
55
- # to get a reference to it to allow for such functionalities as
55
+ # to get a reference to it to allow for such functionalities as
56
56
  # hiding, stacking, ...
57
57
  def dom_id
58
58
  "w#{object_id.abs}"
@@ -86,7 +86,7 @@ module OMF::Web::Widget
86
86
  # end
87
87
  # end
88
88
  # end
89
- #
89
+ #
90
90
  # def on_ws_close(ws)
91
91
  # raise "ARE WE STILL NEEDING THIS"
92
92
  # @ws = nil