omf_web 0.9.7 → 0.9.8

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