omf_web 0.9.7 → 0.9.8

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 (62) hide show
  1. data/.gitignore +1 -0
  2. data/README.md +1 -1
  3. data/bin/omf_web_demo +3 -0
  4. data/bin/omf_web_demo.sh +0 -0
  5. data/doc/tutorial/tut01/hello_world.rb +27 -0
  6. data/doc/tutorial/tut02/hello_graph.rb +85 -0
  7. data/doc/tutorial/tut03/hello_database.rb +69 -0
  8. data/doc/tutorial/tut03/nmetric.sq3 +0 -0
  9. data/example/bridge/config.ru +2 -1
  10. data/example/demo/data_sources/downloads.csv +96 -1
  11. data/example/demo/demo_viz_server.rb +46 -28
  12. data/example/openflow-gec15/README.md +2 -0
  13. data/example/openflow-gec15/doc/gec15_topo.png +0 -0
  14. data/example/openflow-gec15/exp_source.rb +48 -9
  15. data/example/openflow-gec15/of_viz_server.rb +1 -1
  16. data/example/openflow-gec15/repository/of-exp.rb +117 -12
  17. data/example/openflow-gec15/repository/trema-ctl6.rb +2 -2
  18. data/example/simple/README.md +29 -1
  19. data/example/simple/data_sources/ping_source.rb +2 -2
  20. data/lib/irods4r/directory.rb +49 -0
  21. data/lib/irods4r/file.rb +53 -0
  22. data/lib/irods4r/icommands.rb +63 -0
  23. data/lib/irods4r.rb +34 -0
  24. data/lib/omf-web/content/content_proxy.rb +14 -5
  25. data/lib/omf-web/content/file_repository.rb +36 -75
  26. data/lib/omf-web/content/git_repository.rb +39 -35
  27. data/lib/omf-web/content/irods_repository.rb +191 -0
  28. data/lib/omf-web/content/repository.rb +84 -21
  29. data/lib/omf-web/content/static_repository.rb +61 -0
  30. data/lib/omf-web/data_source_proxy.rb +3 -3
  31. data/lib/omf-web/rack/session_authenticator.rb +67 -35
  32. data/lib/omf-web/rack/tab_mapper.rb +2 -1
  33. data/lib/omf-web/rack/websocket_handler.rb +49 -10
  34. data/lib/omf-web/session_store.rb +9 -8
  35. data/lib/omf-web/theme/bright/page.rb +1 -1
  36. data/lib/omf-web/thin/runner.rb +18 -5
  37. data/lib/omf-web/version.rb +1 -1
  38. data/lib/omf-web/widget/text/maruku/output/to_html.rb +8 -2
  39. data/lib/omf_web.rb +17 -2
  40. data/omf_web.gemspec +0 -1
  41. data/share/htdocs/graph/js/abstract_widget.js +3 -1
  42. data/share/htdocs/graph/js/barchart_brush.js +240 -0
  43. data/share/htdocs/js/data_source2.js +4 -1
  44. data/share/htdocs/js/mustache.js +17 -11
  45. data/share/htdocs/theme/bright/css/bright.css +1 -1
  46. data/share/htdocs/vendor/{bootstrap-2.1.1 → bootstrap-2.3.1}/css/bootstrap-responsive.css +56 -5
  47. data/share/htdocs/vendor/bootstrap-2.3.1/css/bootstrap-responsive.min.css +9 -0
  48. data/share/htdocs/vendor/{bootstrap-2.1.1 → bootstrap-2.3.1}/css/bootstrap.css +856 -472
  49. data/share/htdocs/vendor/bootstrap-2.3.1/css/bootstrap.min.css +9 -0
  50. data/share/htdocs/vendor/{bootstrap-2.1.1 → bootstrap-2.3.1}/img/glyphicons-halflings-white.png +0 -0
  51. data/share/htdocs/vendor/{bootstrap-2.1.1 → bootstrap-2.3.1}/img/glyphicons-halflings.png +0 -0
  52. data/share/htdocs/vendor/{bootstrap-2.1.1 → bootstrap-2.3.1}/js/bootstrap.js +427 -178
  53. data/share/htdocs/vendor/bootstrap-2.3.1/js/bootstrap.min.js +6 -0
  54. data/share/htdocs/vendor/jquery-tipsy/css/tipsy.css +25 -0
  55. data/share/htdocs/vendor/jquery-tipsy/js/jquery.tipsy.js +258 -0
  56. metadata +27 -14
  57. data/bin/omf-web-basic +0 -235
  58. data/share/htdocs/vendor/.DS_Store +0 -0
  59. data/share/htdocs/vendor/bootstrap-2.1.1/css/bootstrap-responsive.min.css +0 -9
  60. data/share/htdocs/vendor/bootstrap-2.1.1/css/bootstrap.min.css +0 -9
  61. data/share/htdocs/vendor/bootstrap-2.1.1/js/bootstrap.min.js +0 -6
  62. data/share/htdocs/vendor/jquery-ui-1.8.23/index.html +0 -383
@@ -14,17 +14,17 @@ module OMF::Web
14
14
  #
15
15
  class GitContentRepository < ContentRepository
16
16
 
17
- @@git_repositories = {}
18
-
19
- # Return the repository which is referenced to by elements in 'opts'.
20
- #
21
- #
22
- def self.[](name)
23
- unless repo = @@git_repositories[name.to_sym]
24
- raise "Unknown git repo '#{name}'"
25
- end
26
- repo
27
- end
17
+ # @@git_repositories = {}
18
+ #
19
+ # # Return the repository which is referenced to by elements in 'opts'.
20
+ # #
21
+ # #
22
+ # def self.[](name)
23
+ # unless repo = @@git_repositories[name.to_sym]
24
+ # raise "Unknown git repo '#{name}'"
25
+ # end
26
+ # repo
27
+ # end
28
28
 
29
29
  # Register an existing GIT repo to the system. It will be
30
30
  # consulted for all content url's strarting with
@@ -32,17 +32,17 @@ module OMF::Web
32
32
  # become the default repo for all newly created content
33
33
  # in this app.
34
34
  #
35
- def self.register_git_repo(name, top_dir, is_primary = false)
36
- name = name.to_sym
37
- if @@git_repositories[name]
38
- warn "Ignoring repeated registration of git rep '#{name}'"
39
- return
40
- end
41
- repo = @@git_repositories[name] = GitContentRepository.new(name, top_dir)
42
- if is_primary
43
- @@primary_repository = repo
44
- end
45
- end
35
+ # def self.register_git_repo(name, top_dir, is_primary = false)
36
+ # name = name.to_sym
37
+ # if @@git_repositories[name]
38
+ # warn "Ignoring repeated registration of git rep '#{name}'"
39
+ # return
40
+ # end
41
+ # repo = @@git_repositories[name] = GitContentRepository.new(name, top_dir)
42
+ # if is_primary
43
+ # @@primary_repository = repo
44
+ # end
45
+ # end
46
46
 
47
47
  # def self.read_content(url, opts)
48
48
  # unless (a = url.split(':')).length == 3
@@ -57,23 +57,21 @@ module OMF::Web
57
57
 
58
58
  attr_reader :name, :top_dir
59
59
 
60
- def initialize(name, top_dir)
61
- @name = name
62
- @top_dir = top_dir
63
- @repo = Grit::Repo.new(top_dir)
64
- @url_prefix = "git:#{name}:"
65
- #@@git_repositories['git:foo'] = self
60
+ def initialize(name, opts)
61
+ super
62
+ @repo = Grit::Repo.new(@top_dir)
63
+ @url_prefix = "git:#{@name}:"
66
64
  end
67
65
 
68
66
  #
69
67
  # Create a URL for a file with 'path' in.
70
68
  # If 'strictly_new' is true, returns nil if 'path' already exists.
71
69
  #
72
- def create_url(path, strictly_new = true)
73
- return "git:"
74
- # TODO: Need to add code to select proper repository
75
- return GitContentRepository.create_url(path, strictly_new)
76
- end
70
+ # def create_url(path, strictly_new = true)
71
+ # return "git:"
72
+ # # TODO: Need to add code to select proper repository
73
+ # return GitContentRepository.create_url(path, strictly_new)
74
+ # end
77
75
 
78
76
 
79
77
  # Load content described by either a hash or a straightforward path
@@ -87,9 +85,9 @@ module OMF::Web
87
85
  def create_content_proxy_for(content_descr)
88
86
  path = _get_path(content_descr)
89
87
  # TODO: Make sure that key is really unique across multiple repositories
90
- descr = descr ? descr.dup : {}
91
88
  url = @url_prefix + path
92
89
  key = Digest::MD5.hexdigest(url)
90
+ descr = {}
93
91
  descr[:url] = url
94
92
  descr[:url_key] = key
95
93
  descr[:path] = path
@@ -119,6 +117,12 @@ module OMF::Web
119
117
  end
120
118
  end
121
119
 
120
+ # Return a URL for a path in this repo
121
+ #
122
+ def get_url_for_path(path)
123
+ @url_prefix + path
124
+ end
125
+
122
126
 
123
127
 
124
128
  #
@@ -148,7 +152,7 @@ module OMF::Web
148
152
  if long_name.match(search_pattern)
149
153
  mt = mime_type_for_file(e.name)
150
154
  path = @url_prefix + long_name
151
- res << {:path => path, :name => e.name,
155
+ res << {:path => path, url => url_for_path(path), :name => e.name,
152
156
  :mime_type => mt,
153
157
  #:id => Base64.encode64(long_name).gsub("\n", ''),
154
158
  :size => e.size, :blob => e.id}
@@ -0,0 +1,191 @@
1
+
2
+ require 'find'
3
+ require 'omf_common/lobject'
4
+ require 'omf_web'
5
+ require 'omf-web/content/content_proxy'
6
+ require 'omf-web/content/repository'
7
+ require 'irods4r'
8
+
9
+ module OMF::Web
10
+
11
+ # This class provides an interface to a directory based repository
12
+ # It retrieves, archives and versions content.
13
+ #
14
+ class IRodsContentRepository < ContentRepository
15
+
16
+ @@irods_repositories = {}
17
+
18
+ # Return the repository which is referenced to by elements in 'opts'.
19
+ #
20
+ #
21
+ def self.[](name)
22
+ unless repo = @@irods_repositories[name.to_sym]
23
+ raise "Unknown iRODS repo '#{name}'"
24
+ end
25
+ repo
26
+ end
27
+
28
+ # Register an existing directory to the system. It will be
29
+ # consulted for all content url's starting with
30
+ # 'irods:_top_dir_:'. If 'is_primary' is set to true, it will
31
+ # become the default repo for all newly created content
32
+ # in this app.
33
+ #
34
+ def self.register_file_repo(name, top_dir, is_primary = false)
35
+ name = name.to_sym
36
+ if @@irods_repositories[name]
37
+ warn "Ignoring repeated registration of iRODS rep '#{name}'"
38
+ return
39
+ end
40
+ repo = @@irods_repositories[name] = self.new(name, top_dir)
41
+ if is_primary
42
+ @@primary_repository = repo
43
+ end
44
+ end
45
+
46
+ attr_reader :name, :top_dir
47
+
48
+ def initialize(name, opts)
49
+ super
50
+ unless @top_dir
51
+ raise "No top_dir defined (#{opts.keys.inspect})"
52
+ end
53
+ @url_prefix = "irods:#{name}:"
54
+ end
55
+
56
+ # Load content described by either a hash or a straightforward path
57
+ # and return a 'ContentProxy' holding it.
58
+ #
59
+ # If descr[:strictly_new] is true, return nil if file for which proxy is requested
60
+ # already exists.
61
+ #
62
+ # @return: Content proxy
63
+ #
64
+ def create_content_proxy_for(content_descr)
65
+ path = _get_path(content_descr)
66
+ # TODO: Make sure that key is really unique across multiple repositories
67
+ descr = descr ? descr.dup : {}
68
+ url = get_url_for_path(path)
69
+ key = Digest::MD5.hexdigest(url)
70
+ descr[:url] = url
71
+ descr[:url_key] = key
72
+ descr[:path] = path
73
+ descr[:name] = url # Should be something human digestable
74
+ if (descr[:strictly_new])
75
+ return nil if IRODS4r.exists?(path)
76
+ end
77
+ proxy = ContentProxy.create(descr, self)
78
+ return proxy
79
+ end
80
+
81
+ def write(content_descr, content, message)
82
+ path = _get_path(content_descr)
83
+ #puts "WRITE PATHS>>> #{path}"
84
+ f = IRODS4r::File.create(path, false)
85
+ f.write(content)
86
+ end
87
+
88
+ def read(content_descr)
89
+ path = _get_path(content_descr)
90
+ #puts "READ PATHS>>> #{path}"
91
+ f = IRODS4r::File.create(path, false)
92
+ f.read()
93
+ end
94
+
95
+ #
96
+ # Return an array of file names which are in the repository and
97
+ # match 'search_pattern'
98
+ #
99
+ def find_files(search_pattern, opts = {})
100
+ begin
101
+ dir = IRODS4r.find(@top_dir)
102
+ rescue IRODS4r::IRODS4rException
103
+ return []
104
+ end
105
+ res = []
106
+ _find_files(search_pattern, dir, res, opts[:mime_type])
107
+ res
108
+ end
109
+
110
+ def _find_files(search_pattern, dir, res, mime_type)
111
+ dir.list.each do |e|
112
+ if e.directory?
113
+ _find_files(search_pattern, e, res, mime_type)
114
+ else
115
+ path = e.path
116
+ if path.match(search_pattern)
117
+ mt = mime_type_for_file(path)
118
+ next if mime_type != nil && mime_type != mt
119
+ res << {:url => get_url_for_path(path), :path => path, #:name => 'foo',
120
+ :mime_type => mt}
121
+ end
122
+ end
123
+ end
124
+ res
125
+ end
126
+
127
+
128
+ # Return a URL for a path in this repo
129
+ #
130
+ def get_url_for_path(path)
131
+ puts "PATH>>>>> '#{path}:#{path.class}'-'#{@top_dir}:#{@top_dir.class}'"
132
+ if m = path.match("#{@top_dir}(.*)")
133
+ path = m[1]
134
+ end
135
+ url = @url_prefix + path
136
+ end
137
+
138
+ # HACK ALERT!!!
139
+ #
140
+ # This method may be called by an entity which wants to access the content
141
+ # directly through the file system. In the absence of a FUSE mounted iRODS
142
+ # repo, we 'iget' the resource to a temporary directory and return that
143
+ # path. The calling entity needs to be aware that any changes to that file
144
+ # will NOT show up in iRODS without an iput.
145
+ #
146
+ # This should really NOT be necessary. Use FUSE
147
+ #
148
+ def absolute_path(content_descr)
149
+ path = _get_path(content_descr)
150
+
151
+ require 'etc'
152
+ tmp_dir = "#{Dir::tmpdir}/LabWiki-#{Etc.getlogin}"
153
+ # unless Dir.exists? tmp_dir
154
+ # Dir.mkdir tmp_dir, 0700
155
+ # end
156
+
157
+ target = File.join(tmp_dir, path)
158
+ IRODS4r::ICommands.export(path, target)
159
+ target
160
+ end
161
+
162
+
163
+ def _get_path(content_descr)
164
+ #puts ">>>GET PATH #{content_descr.inspect}"
165
+ if content_descr.is_a? String
166
+ path = content_descr.to_s
167
+ if path.start_with? 'irods:'
168
+ path = File.join(@top_dir, path.split(':')[2])
169
+ end
170
+ elsif content_descr.is_a? Hash
171
+ descr = content_descr
172
+ unless path = descr[:path]
173
+ if url = descr[:url]
174
+ path = File.join(@top_dir, url.split(':')[2]) # irods:repo_name:path
175
+ end
176
+ end
177
+ unless path
178
+ raise "Missing 'path' or 'url' in content description (#{descr.inspect})"
179
+ end
180
+ path = path.to_s
181
+ else
182
+ raise "Unsupported type '#{content_descr.class}'"
183
+ end
184
+ unless path
185
+ raise "Can't find path information in '#{content_descr.inspect}'"
186
+ end
187
+ return path
188
+ end
189
+
190
+ end # class
191
+ end # module
@@ -22,8 +22,48 @@ module OMF::Web
22
22
  :txt => 'text'
23
23
  }
24
24
 
25
+ REPO_PLUGINS = {
26
+ git: lambda do |name, opts|
27
+ require 'omf-web/content/git_repository'
28
+ return GitContentRepository.new(name, opts)
29
+ end,
30
+ file: lambda do |name, opts|
31
+ require 'omf-web/content/file_repository'
32
+ return FileContentRepository.new(name, opts)
33
+ end,
34
+ irods: lambda do |name, opts|
35
+ require 'omf-web/content/irods_repository'
36
+ return IRodsContentRepository.new(name, opts)
37
+ end,
38
+ static: lambda do |name, opts|
39
+ require 'omf-web/content/static_repository'
40
+ return StaticContentRepository.new(name, opts)
41
+ end
42
+ }
43
+
25
44
  # Repo to be used for all newly created content
26
45
  @@primary_repository = nil
46
+ @@repositories = {}
47
+
48
+ def self.register_repo(name, opts)
49
+ raise "ArgumentMismatch: Expected Hash, but got #{opts}" unless opts.is_a? Hash
50
+
51
+ name = name.to_sym
52
+ if @@repositories[name]
53
+ warn "Ignoring repeated registration of repo '#{name}'"
54
+ return
55
+ end
56
+
57
+ unless type = opts[:type]
58
+ raise "Missing type in repo opts (#{opts})"
59
+ end
60
+ unless repo_creator = REPO_PLUGINS[type.to_sym]
61
+ raise "Unknown repository type '#{type}'"
62
+ end
63
+ @@repositories[name] = r = repo_creator.call(name, opts)
64
+ @@primary_repository = r if opts[:is_primary]
65
+ end
66
+
27
67
 
28
68
  # Load content described by either a hash or a straightforward url
29
69
  # and return a 'ContentProxy' holding it.
@@ -39,7 +79,13 @@ module OMF::Web
39
79
  if url_or_descr.is_a? String
40
80
  url = url_or_descr
41
81
  else
42
- url = url_or_descr[:url]
82
+ if (text = url_or_descr[:text])
83
+ # a bit of a hack for small static text blocks
84
+ # Much better for maintenance is to use a separate file
85
+ url = "static:-"
86
+ else
87
+ url = url_or_descr[:url]
88
+ end
43
89
  end
44
90
  unless url
45
91
  throw "Can't find url in '#{url_or_descr.inspect}"
@@ -49,19 +95,6 @@ module OMF::Web
49
95
  repo.create_content_proxy_for(url_or_descr)
50
96
  end
51
97
 
52
- def self.find_repo_for(url)
53
- parts = url.split(':')
54
- case type = parts[0]
55
- when 'git'
56
- require 'omf-web/content/git_repository'
57
- return GitContentRepository[parts[1]]
58
- when 'file'
59
- require 'omf-web/content/file_repository'
60
- return FileContentRepository[parts[1]]
61
- else
62
- raise "Unknown repo type '#{type}'"
63
- end
64
- end
65
98
 
66
99
  def self.absolute_path_for(url)
67
100
  find_repo_for(url).absolute_path(url)
@@ -71,6 +104,16 @@ module OMF::Web
71
104
  find_repo_for(url).read(url)
72
105
  end
73
106
 
107
+ def self.find_repo_for(url)
108
+ parts = url.split(':')
109
+ name = parts[1]
110
+ unless repo = @@repositories[name.to_sym]
111
+ raise "Unknown repo '#{name}'"
112
+ end
113
+ return repo
114
+ end
115
+
116
+
74
117
  # Find files whose file name matches 'selector'.
75
118
  #
76
119
  # Supported options:
@@ -96,16 +139,29 @@ module OMF::Web
96
139
  end
97
140
 
98
141
 
99
-
100
- attr_reader :top_dir
142
+ attr_reader :name, :top_dir
143
+
144
+ def initialize(name, opts)
145
+ @name = name
146
+ if @top_dir = opts[:top_dir]
147
+ @top_dir = File.expand_path(@top_dir)
148
+ end
149
+ end
101
150
 
102
- def initialize(top_dir)
103
- @top_dir = top_dir
104
- @repo = Grit::Repo.new(top_dir)
151
+ #
152
+ # Return an array of file names which are in the repository and
153
+ # match 'search_pattern'
154
+ #
155
+ def find_files(search_pattern, opts = {})
156
+ raise "Missing implementation"
105
157
  end
106
158
 
107
159
 
108
- def mime_type_for_file(fname)
160
+ def mime_type_for_file(content_descriptor)
161
+ fname = content_descriptor
162
+ if content_descriptor.is_a? Hash
163
+ fname = content_descriptor[:path]
164
+ end
109
165
  ext = fname.split('.')[-1]
110
166
  mt = MIME_TYPE[ext.to_sym] || 'text'
111
167
  end
@@ -126,7 +182,14 @@ module OMF::Web
126
182
  File.join(@top_dir, path)
127
183
  end
128
184
 
185
+ def path(content_descr)
186
+ path = _get_path(content_descr)
187
+ end
129
188
 
130
-
189
+ # Return a URL for a path in this repo
190
+ #
191
+ def get_url_for_path(path)
192
+ raise "Missing implementation"
193
+ end
131
194
  end # class
132
195
  end # module
@@ -0,0 +1,61 @@
1
+
2
+ require 'omf_common/lobject'
3
+ require 'omf_web'
4
+ require 'omf-web/content/content_proxy'
5
+ require 'omf-web/content/repository'
6
+ require 'singleton'
7
+
8
+ module OMF::Web
9
+
10
+ # This class provides an interface to a repository of static,
11
+ # preloaded content.
12
+ #
13
+ class StaticContentRepository < ContentRepository
14
+
15
+
16
+ # Load content described by either a hash or a straightforward path
17
+ # and return a 'ContentProxy' holding it.
18
+ #
19
+ # @return: Content proxy
20
+ #
21
+ def create_content_proxy_for(content_descr)
22
+ debug content_descr
23
+ if content_descr.is_a? String
24
+ content_descr = {text: content_descr}
25
+ end
26
+ descr = content_descr.dup
27
+ unless text = descr.delete(:text)
28
+ raise "Missing ':text' declaraton for static content"
29
+ end
30
+
31
+ key = Digest::MD5.hexdigest(text)
32
+ @content[key] = text
33
+ descr[:url] = url = "static:" + key
34
+ descr[:url_key] = key
35
+ descr[:name] = content_descr[:name] || url # Should be something human digestable
36
+ proxy = ContentProxy.create(descr, self)
37
+ return proxy
38
+ end
39
+
40
+ def read(content_descr)
41
+ debug "READ: #{content_descr}"
42
+ @content[content_descr[:url_key]] || 'Unknown'
43
+ end
44
+
45
+ def write(content_descr, content, message)
46
+ raise "READ ONLY"
47
+ end
48
+
49
+ def mime_type_for_file(content_descriptor)
50
+ content_descriptor[:mime_type] || 'text'
51
+ end
52
+
53
+ def initialize(name, opts)
54
+ super
55
+ @content = {}
56
+ end
57
+
58
+
59
+
60
+ end # class
61
+ end # module
@@ -124,7 +124,7 @@ module OMF::Web
124
124
  block.call :added, rows
125
125
  end
126
126
  @data_source.on_content_changed(block.object_id) do |action, rows|
127
- debug "on_changed: #{action}: #{rows.inspect}"
127
+ #debug "on_changed: #{action}: #{rows.inspect}"
128
128
  block.call action, rows
129
129
  end
130
130
  end
@@ -142,7 +142,7 @@ module OMF::Web
142
142
  sid = Thread.current["sessionID"]
143
143
  opts = opts.dup
144
144
  opts[:name] = @name
145
- opts[:schema] = @data_source.schema
145
+ opts[:schema] = @data_source.schema.describe
146
146
  opts[:update_url] = "/_update/#{@name}?sid=#{sid}"
147
147
  opts[:sid] = sid
148
148
  unless opts[:slice] # don't send any data if this is a sliced one
@@ -150,7 +150,7 @@ module OMF::Web
150
150
  opts[:rows] = []
151
151
  opts[:offset] = @data_source.offset
152
152
  end
153
- #puts "to_java2>>>>> #{opts.to_json.inspect}"
153
+ #puts "to_java2>>>>> #{opts.inspect}"
154
154
 
155
155
  %{
156
156
  OML.data_sources.register(#{opts.to_json});