rbbt-rest 1.1.4 → 1.1.5

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 (73) hide show
  1. checksums.yaml +8 -8
  2. data/config.ru +3 -0
  3. data/lib/rbbt/rest/client.rb +9 -10
  4. data/lib/rbbt/rest/common/cache.rb +32 -7
  5. data/lib/rbbt/rest/common/forms.rb +33 -0
  6. data/lib/rbbt/rest/common/locate.rb +4 -0
  7. data/lib/rbbt/rest/common/misc.rb +24 -0
  8. data/lib/rbbt/rest/common/render.rb +26 -9
  9. data/lib/rbbt/rest/common/resources.rb +1 -1
  10. data/lib/rbbt/rest/common/table.rb +87 -26
  11. data/lib/rbbt/rest/common/users.rb +5 -1
  12. data/lib/rbbt/rest/entity.rb +300 -95
  13. data/lib/rbbt/rest/entity/action_controller.rb +6 -1
  14. data/lib/rbbt/rest/entity/entity_list_card.rb +1 -1
  15. data/lib/rbbt/rest/entity/entity_map_card.rb +15 -0
  16. data/lib/rbbt/rest/entity/favourites.rb +57 -1
  17. data/lib/rbbt/rest/entity/finder.rb +1 -1
  18. data/lib/rbbt/rest/entity/helpers.rb +40 -7
  19. data/lib/rbbt/rest/entity/list.rb +8 -0
  20. data/lib/rbbt/rest/entity/locate.rb +114 -0
  21. data/lib/rbbt/rest/entity/map.rb +99 -0
  22. data/lib/rbbt/rest/entity/render.rb +25 -0
  23. data/lib/rbbt/rest/entity/rest.rb +33 -4
  24. data/lib/rbbt/rest/main.rb +27 -4
  25. data/lib/rbbt/rest/workflow.rb +7 -1
  26. data/lib/rbbt/rest/workflow/jobs.rb +21 -1
  27. data/share/views/compass/app.sass +17 -9
  28. data/share/views/compass/entity_card.sass +16 -3
  29. data/share/views/compass/favourites.sass +44 -2
  30. data/share/views/compass/form.sass +11 -0
  31. data/share/views/compass/list_container.sass +1 -1
  32. data/share/views/compass/reveal.sass +31 -0
  33. data/share/views/compass/style.sass +29 -1
  34. data/share/views/compass/style_mixins.sass +18 -6
  35. data/share/views/compass/table.sass +19 -3
  36. data/share/views/compass/topbar.sass +8 -2
  37. data/share/views/entity_map/Default.haml +5 -0
  38. data/share/views/entity_partials/action_card.haml +2 -0
  39. data/share/views/entity_partials/action_controller.haml +8 -1
  40. data/share/views/entity_partials/entity_list_card.haml +8 -6
  41. data/share/views/entity_partials/entity_map_card.haml +63 -0
  42. data/share/views/entity_partials/list_container.haml +3 -2
  43. data/share/views/error.haml +14 -10
  44. data/share/views/help.haml +34 -0
  45. data/share/views/help/entity.haml +193 -0
  46. data/share/views/help/workflow.haml +77 -0
  47. data/share/views/job_result/tsv.haml +3 -2
  48. data/share/views/layout.haml +33 -3
  49. data/share/views/layout/favourites.haml +14 -4
  50. data/share/views/layout/header.haml +4 -0
  51. data/share/views/partials/form.haml +3 -1
  52. data/share/views/partials/table.haml +16 -52
  53. data/share/views/partials/table/column.haml +10 -0
  54. data/share/views/partials/table/files.haml +4 -0
  55. data/share/views/partials/table/filters.haml +11 -0
  56. data/share/views/partials/table/page.haml +37 -0
  57. data/share/views/public/favicon.gif +0 -0
  58. data/share/views/public/js/_ajax_replace.js +76 -4
  59. data/share/views/public/js/_fix_tablesorter_science.js +13 -0
  60. data/share/views/public/js/actions.js +8 -2
  61. data/share/views/public/js/base.js +12 -1
  62. data/share/views/public/js/entities.js +39 -7
  63. data/share/views/public/js/favourites.js +308 -77
  64. data/share/views/public/js/helpers.js +9 -5
  65. data/share/views/public/js/lists.js +12 -12
  66. data/share/views/public/js/maps.js +17 -0
  67. data/share/views/public/js/reveal.js +48 -0
  68. data/share/views/public/js/tables.js +110 -51
  69. data/share/views/public/js/workflow.js +4 -16
  70. data/share/views/public/plugins/underscore/js/underscore.js +1227 -0
  71. data/share/views/tasks.haml +12 -1
  72. data/share/views/wait.haml +7 -0
  73. metadata +17 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjY4NGQ1Mjg4YTIyYmRjYWQ0OWFkMjQ4NmQxMjU3NmIwYjIzN2M1Yw==
4
+ YmU4OTJhZGMwZTZkYzY0MTcxZjVlNjNlM2QyYjc2YTMyOWIyYjc0MA==
5
5
  data.tar.gz: !binary |-
6
- MTQ3MGVlMzE3NjU2ZDc3ZmI4YjM4MDA1MjY0ZDI4NjFkYTY5ZDAxOQ==
6
+ YjIwNDcyMzgzMWViOGY4NTI1ZTM5YjkzZTA5Y2Q1MzdlYjJmNzk4Nw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- OTg5MGEwZmQ5YmYyOWRmNzM2YjQ4OTZkNzI1YjRiZGQ5ZjkzYWRhMjJjNTVj
10
- ZTliMjJlYWE0YjJhOGI1ZTdhZWVkNzM4MjFmODllZGRhZDA2OGE3NjQ1ZDQ5
11
- ZTBmYTkyOTY2ZDAxNzlmMGYzYzFiZjY2NmFlN2Q5M2M1OWM1NmU=
9
+ NmZjNDljMWQxYTc3NTEwYjJkOTgyNWFhNjcyZjI3ODA2YTk4NzYzNTdiZDA1
10
+ ZjY0MTE1YmQxZDdhMWI2MmRkMmJmNzMxMWQ0MTIyMThlOGY3ODc0Njg4NGY0
11
+ NzgyYTU4MDcyOTkyYmM3OGE2YjM4MDQ3MTM2ZTkwM2YxYzJkNWE=
12
12
  data.tar.gz: !binary |-
13
- ZTQ3Y2Q5YTFjOGQ4YTlmMzlhMmJhNTUwZTFlODUxNjhjYjEwNzBmZDg2YmNk
14
- OWI2OGQzNzI1MzNmYTQzYzVjYWJlYmZiNWY2NDY5NGRmYWE2OWQ4ZjE4YjBm
15
- MDliZDczODJmNjdlYWNiODZiOGY3Mzk3MTFjZGFkMDMzZjBhYTU=
13
+ ZjVlOGQxMTBkZGY3N2IxYTgwODU3MDc5NmE4OGIyYzMyY2I1NDdhNDc5ODZk
14
+ NzFlM2FlMDhjMzhlNDU0Y2QzZjJmZjUxOGQyMGM2NmNhN2U2ZWIzMzNmN2Uz
15
+ MGE0MDZlNDRlMWVjY2U2NTE2ZjRkM2Y3NGE5MGI4OTY2ZTI3OTk=
data/config.ru CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'zurb-foundation'
2
2
  require 'modular-scale'
3
3
 
4
+ require 'sinatra'
5
+
4
6
  require 'rbbt'
5
7
  require 'rbbt/rest/main'
6
8
  require 'rbbt/rest/entity'
@@ -95,5 +97,6 @@ end
95
97
 
96
98
  #{{{ RUN
97
99
  $title = "RbbtRest"
100
+
98
101
  use Rack::Deflater
99
102
  run RbbtRest
@@ -36,7 +36,11 @@ class WorkflowRESTClient
36
36
  def self.get_json(url, params = {})
37
37
  Log.debug("RestClient get_json #{}: #{ url } - #{params.inspect}")
38
38
  params = params.merge({ :_format => 'json' })
39
- res = RestClient.get(url, :params => params)
39
+ begin
40
+ res = RestClient.get(url, :params => params)
41
+ rescue => e
42
+ raise JSON.parse(e.response)["message"]
43
+ end
40
44
  begin
41
45
  JSON.parse(res)
42
46
  rescue
@@ -175,6 +179,10 @@ class WorkflowRESTClient
175
179
  name
176
180
  end
177
181
 
182
+ def workflow_description
183
+ WorkflowRESTClient.get_raw(File.join(url, 'description'))
184
+ end
185
+
178
186
  def task_info(task)
179
187
  @task_info ||= {}
180
188
  @task_info[task]
@@ -301,12 +309,3 @@ class WorkflowRESTClient
301
309
  end
302
310
  end
303
311
  end
304
-
305
- if __FILE__ == $0
306
- client = WorkflowRESTClient.new("http://darthcaedus:9292/Sequence", "Sequence")
307
-
308
- job = client.job(:genes_at_genomic_positions, "1", :organism => "Hsa", :positions => ["2:198266834:R"]).clean.fork
309
- puts job.join.load.to_s
310
-
311
- end
312
-
@@ -14,6 +14,7 @@ module RbbtRESTHelpers
14
14
  end
15
15
 
16
16
  def cache(name, params = {}, &block)
17
+ @cache_type ||= params[:cache_type] if params[:cache_type]
17
18
  return yield if name.nil? or cache_type.nil? or cache_type == :none
18
19
 
19
20
  send_file = consume_parameter(:_send_file, params)
@@ -29,22 +30,43 @@ module RbbtRESTHelpers
29
30
 
30
31
  step = Step.new(path, task, nil, nil, self)
31
32
 
33
+ halt 200, step.info.to_json if @format == :info
34
+
32
35
  self.instance_variable_set("@step", step)
33
36
 
34
37
  if @fragment
35
38
  fragment_file = step.file(@fragment)
36
39
  if File.exists?(fragment_file)
37
40
  case @format.to_s
38
- when "tsv"
39
- content_type "text/tab-separated-values"
40
- send_file fragment_file
41
41
  when "table"
42
42
  halt 200, tsv2html(fragment_file)
43
43
  when "json"
44
- halt 200, tsv_process(TSV.open(fragment_file)).to_json
44
+ halt 200, tsv_process(load_tsv(fragment_file).first).to_json
45
+ when "tsv"
46
+ content_type "text/tab-separated-values"
47
+ halt 200, tsv_process(load_tsv(fragment_file).first).to_s
48
+ when "entities"
49
+ tsv = tsv_process(load_tsv(fragment_file).first)
50
+ list = tsv.values.flatten
51
+ tsv.prepare_entity(list, tsv.fields.first, tsv.entity_options)
52
+ type = list.annotation_types.last
53
+ list_id = "List of #{type} in table #{ @fragment }"
54
+ list_id << " (#{ @filter })" if @filter
55
+ Entity::List.save_list(type.to_s, list_id, list, user)
56
+ header "Location", Entity::REST.entity_list_url(list_id, type)
57
+ redirect to(Entity::REST.entity_list_url(list_id, type))
58
+ when "map"
59
+ tsv = tsv_process(load_tsv(fragment_file).first)
60
+ type = tsv.keys.annotation_types.last
61
+ column = tsv.fields.first
62
+ map_id = "Map #{type}-#{column} in #{ @fragment }"
63
+ map_id << " (#{ @filter.gsub(';','|') })" if @filter
64
+ Entity::Map.save_map(type.to_s, column, map_id, tsv, user)
65
+ url = Entity::REST.entity_map_url(map_id, type, column)
66
+ redirect to(url)
45
67
  when "excel"
46
68
  require 'rbbt/tsv/excel'
47
- tsv = TSV.open(Open.open(fragment_file))
69
+ tsv = load_tsv(fragment_file).first
48
70
  content_type "text/html"
49
71
  data = nil
50
72
  excel_file = TmpFile.tmp_file
@@ -89,9 +111,12 @@ module RbbtRESTHelpers
89
111
  begin
90
112
  case step.status
91
113
  when :error, :aborted
92
- error_for step, false
114
+ error_for step, !@ajax
93
115
  when :done
94
- if send_file
116
+ case
117
+ when @permalink
118
+ redirect to(permalink(step.path))
119
+ when send_file
95
120
  send_file step.path
96
121
  else
97
122
  step.load
@@ -25,6 +25,39 @@ module RbbtRESTHelpers
25
25
  html_options = consume_parameter(:html_options, extra) || {}
26
26
 
27
27
  case type
28
+ when :multiple
29
+ choices = consume_parameter(:choices, extra)
30
+ default = default.collect{|o| o.to_s} if default
31
+ current = current.collect{|o| o.to_s} if current
32
+ input_label(id, name, description, Array === default ? default * ", " : nil, extra) +
33
+ choices.collect do |choice|
34
+ choice_name = name.to_s + "[#{ choice }]"
35
+
36
+ check_true = (current && current.include?(choice.to_s)) || (default && default.include?(choice.to_s))
37
+
38
+ check_true = false if check_true.nil?
39
+ check_false = ! check_true
40
+
41
+ choice_id = id + "[#{ Misc.snake_case(choice) }]"
42
+ if id
43
+ false_id = choice_id + '_false'
44
+ true_id = choice_id + '_true'
45
+ else
46
+ false_id = nil
47
+ true_id = nil
48
+ end
49
+
50
+ #choice_html = html_tag("input", nil, :type => :hidden, :name => choice_name + "_checkbox_false", :value => "false") +
51
+ # html_tag("input", nil, :type => :checkbox, :checked => check_true, :name => choice_name, :value => "true", :id => choice_id) +
52
+ # input_label(choice_id, choice, nil, nil, extra.merge({:class => :inline}))
53
+
54
+ choice_html = html_tag("input", nil, :type => :checkbox, :checked => check_true, :name => choice_name, :value => "true", :id => choice_id) +
55
+ input_label(choice_id, choice, choice, nil, extra.merge({:class => :inline}))
56
+
57
+ html_tag('span', choice_html, :class => 'choice')
58
+ end * "\n"
59
+
60
+
28
61
  when :boolean
29
62
  current = param2boolean(current) unless current.nil?
30
63
  default = param2boolean(default) unless default.nil?
@@ -26,6 +26,10 @@ module RbbtRESTHelpers
26
26
 
27
27
  #{{{ SASS
28
28
 
29
+ def self.add_sass_load_path(path)
30
+ Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift path
31
+ end
32
+
29
33
  def self.sass_resources
30
34
  @sass_resources ||= [Rbbt.share.views.compass.find(:lib)]
31
35
  end
@@ -5,6 +5,14 @@ module RbbtRESTHelpers
5
5
 
6
6
  PAGE_SIZE = 20
7
7
 
8
+ def log(status, message)
9
+ if @step
10
+ @step.log(status, message)
11
+ else
12
+ Log.debug("[#{ status }] #{ message }")
13
+ end
14
+ end
15
+
8
16
  def production?
9
17
  ENV["RACK_ENV"] == "production"
10
18
  end
@@ -41,6 +49,9 @@ module RbbtRESTHelpers
41
49
  @debug_js = consume_parameter(:_debug_js)
42
50
  @debug_js = false if @debug_js.nil? or @debug_js == "false"
43
51
 
52
+ @debug_css = consume_parameter(:_debug_css)
53
+ @debug_css = false if @debug_css.nil? or @debug_css == "false"
54
+
44
55
  @_ = consume_parameter(:_)
45
56
 
46
57
  @fragment = consume_parameter(:_fragment)
@@ -65,6 +76,8 @@ module RbbtRESTHelpers
65
76
  end
66
77
  end
67
78
 
79
+ @permalink = consume_parameter :_permalink
80
+
68
81
  @clean_params = params.dup
69
82
  end
70
83
 
@@ -91,6 +104,9 @@ module RbbtRESTHelpers
91
104
  when :float
92
105
  value.to_f
93
106
 
107
+ when :multiple
108
+ value.keys
109
+
94
110
  when :boolean
95
111
  param2boolean(value)
96
112
 
@@ -168,4 +184,12 @@ module RbbtRESTHelpers
168
184
  } * "\n"
169
185
  html_tag(:dl, entries, options)
170
186
  end
187
+
188
+ def permalink(path)
189
+ id = Misc.digest(Time.now.to_s)
190
+ dest = File.join(settings.permalink_dir, id)
191
+ FileUtils.mkdir_p settings.permalink_dir unless File.exists? settings.permalink_dir
192
+ FileUtils.ln_s(path, dest)
193
+ "/permalink/#{ id }"
194
+ end
171
195
  end
@@ -3,13 +3,17 @@ require 'rbbt/rest/common/cache'
3
3
 
4
4
  module RbbtRESTHelpers
5
5
  def error_for(job, layout = nil)
6
- layout = @layout if layout.nil?
7
- layout_file = (layout ? locate_template('layout') : nil)
8
- template_file = locate_template('error')
6
+ if @format == :json
7
+ halt 400, {"message" => job.messages[-2], "backtrace" => job.info[:backtrace]}.to_json
8
+ else
9
+ layout = @layout if layout.nil?
10
+ layout_file = (layout ? locate_template('layout') : nil)
11
+ template_file = locate_template('error')
9
12
 
10
- result = render template_file, {:job => job}, layout_file
13
+ result = render template_file, {:job => job}, layout_file
11
14
 
12
- halt 500, result
15
+ halt 400, result
16
+ end
13
17
  end
14
18
 
15
19
  def wait_on(job, layout = nil)
@@ -29,16 +33,16 @@ module RbbtRESTHelpers
29
33
 
30
34
  def render(template_file, locals = {}, layout_file = nil, cache = nil, cache_options = {})
31
35
  if layout_file
32
- Haml::Engine.new(Open.read(layout_file), :filename => layout_file).render(self) do
36
+ Haml::Engine.new(Open.read(layout_file), :filename => layout_file, :ugly => production?).render(self, locals) do
33
37
  cache(cache, locals.merge(:_template_file => template_file, :user => user).merge(cache_options)) do
34
38
  Log.debug("Rendering #{template_file} with layout")
35
- Haml::Engine.new(Open.read(template_file), :filename => template_file).render(self, locals)
39
+ Haml::Engine.new(Open.read(template_file), :filename => template_file, :ugly => production?).render(self, locals)
36
40
  end
37
41
  end
38
42
  else
39
43
  cache(cache, locals.merge(:_template_file => template_file, :user => user).merge(cache_options)) do
40
44
  Log.debug("Rendering #{template_file} without layout")
41
- Haml::Engine.new(Open.read(template_file), :filename => template_file).render(self, locals)
45
+ Haml::Engine.new(Open.read(template_file), :filename => template_file, :ugly => production?).render(self, locals)
42
46
  end
43
47
  end
44
48
  end
@@ -80,7 +84,7 @@ module RbbtRESTHelpers
80
84
  url = remove_GET_param(url, "_update")
81
85
  fragment_url = add_GET_param(url, "_fragment", fragment_code)
82
86
  if link.nil?
83
- html_tag('a', " ", :href => fragment_url, :class => 'fragment')
87
+ html_tag('a', "", :href => fragment_url, :class => 'fragment')
84
88
  else
85
89
  if link =~ / href=/
86
90
  link.sub(/ href=('|")/," href='#{fragment_url}'")
@@ -143,5 +147,18 @@ module RbbtRESTHelpers
143
147
  raise "Type not understood: #{ type }"
144
148
  end
145
149
  end
150
+
151
+ def reveal(text, id = nil, &block)
152
+ id ||= "rbbt_reveal_" << (rand * 10000).to_i.to_s
153
+
154
+ content_html = capture_haml(&block)
155
+
156
+ str = html_tag('a', text.to_s, :href => "#", "data-reveal-id" => 'modal1', 'attr-reveal_id' => id, 'class' => 'rbbt_reveal_trigger') <<
157
+ "\n" <<
158
+ html_tag('div', "\n" << content_html << "\n", :id => id, 'class' => 'rbbt_reveal_content') <<
159
+ "\n"
160
+
161
+ str
162
+ end
146
163
  end
147
164
 
@@ -1,5 +1,4 @@
1
1
  require 'sass'
2
- #require 'yui/compressor'
3
2
  require 'uglifier'
4
3
 
5
4
  module RbbtRESTHelpers
@@ -22,6 +21,7 @@ module RbbtRESTHelpers
22
21
 
23
22
  def link_css(file)
24
23
  file += '.css' unless file =~ /.css$/
24
+ file << "?_update=reload" if @debug_css
25
25
  html_tag('link', nil, :rel => 'stylesheet', :type => 'text/css', :href => file)
26
26
  end
27
27
 
@@ -1,5 +1,24 @@
1
1
  require 'rbbt/entity'
2
2
 
3
+ module Link
4
+ extend Entity
5
+
6
+ def self.tsv_sort(v)
7
+ value = v.last
8
+ value = value.first if Array === value
9
+ if value and value.match(/<a [^>]*>([^>]*)<\/a>/)
10
+ val = $1
11
+ if val =~ /^\s*\d/
12
+ val.to_f
13
+ else
14
+ val
15
+ end
16
+ else
17
+ ""
18
+ end
19
+ end
20
+ end
21
+
3
22
  module NumericValue
4
23
  extend Entity
5
24
 
@@ -20,11 +39,17 @@ t.value
20
39
  t.values
21
40
  adjusted.p.value
22
41
  adjusted.p.values
42
+ Rank
43
+ rank
23
44
  EOF
24
45
  ).split("\n")
25
46
 
26
47
  def <=>(other)
27
- self.to_f <=> other.to_f
48
+ if Float === self
49
+ super(other.to_f)
50
+ else
51
+ self.to_f <=> other.to_f
52
+ end
28
53
  end
29
54
 
30
55
  def self.tsv_sort(v)
@@ -35,6 +60,10 @@ adjusted.p.values
35
60
  value.to_f
36
61
  end
37
62
  end
63
+
64
+ def to_s
65
+ "%.5g" % self.to_f
66
+ end
38
67
  end
39
68
 
40
69
  module RbbtRESTHelpers
@@ -44,13 +73,13 @@ module RbbtRESTHelpers
44
73
  tsv.collect{|id, value| [id, value]}
45
74
  when :list
46
75
  key_field = tsv.key_field
47
- tsv.collect{|id, values| values = NamedArray.setup([id].concat(values), values.fields, id, values.entity_options, values.entity_templates); values.fields = [key_field].concat values.fields if values.respond_to? :fields; values }
76
+ tsv.collect{|id, values| new_values = [id].concat(values); begin NamedArray.setup(new_values, values.fields, id, values.entity_options, values.entity_templates); new_values.fields = [key_field].concat values.fields end if values.respond_to? :fields; new_values }
48
77
  when :flat
49
78
  key_field = tsv.key_field
50
79
  tsv.collect{|id, values| [id, values]}
51
80
  when :double
52
81
  key_field = tsv.key_field
53
- tsv.collect{|id, value_lists| value_lists = NamedArray.setup([id].concat(value_lists), value_lists.fields, id, value_lists.entity_options, value_lists.entity_templates); value_lists.fields = ([key_field].concat value_lists.fields) if value_lists.respond_to? :fields; value_lists }
82
+ tsv.collect{|id, value_lists| new_value_lists = [id].concat(value_lists); begin NamedArray.setup(new_value_lists, value_lists.fields, id, value_lists.entity_options, value_lists.entity_templates); new_value_lists.fields = ([key_field].concat value_lists.fields) end if value_lists.respond_to? :fields; new_value_lists }
54
83
  end
55
84
  end
56
85
 
@@ -78,7 +107,11 @@ module RbbtRESTHelpers
78
107
  reverse = false
79
108
  end
80
109
 
81
- entity = Entity.formats[field]
110
+ if object.entity_templates[field]
111
+ entity = object.entity_templates[field].annotation_types.last
112
+ else
113
+ entity = Entity.formats[field]
114
+ end
82
115
 
83
116
  num = num.to_i
84
117
  size = size.to_i
@@ -99,13 +132,13 @@ module RbbtRESTHelpers
99
132
  column = @column if column.nil?
100
133
 
101
134
  if filter and filter.to_s != "false"
102
- filter.split("|").each do |f|
135
+ filter.split(";;").each do |f|
103
136
  key, value = f.split("~")
104
137
  case
105
138
  when value =~ /^([<>]=?)(.*)/
106
- tsv = tsv.select(key){|k| k.send($1, $2)}
107
- when value =~ /^\/.*\/$/
108
- tsv = tsv.select(key => Regexp.new(value))
139
+ tsv = tsv.select(key){|k| k = k.first if Array === k; k.to_f.send($1, $2.to_f)}
140
+ when value =~ /^\/(.+)\/.{0,2}$/
141
+ tsv = tsv.select(key => Regexp.new($1))
109
142
  else
110
143
  tsv = tsv.select(key => value)
111
144
  end
@@ -119,26 +152,27 @@ module RbbtRESTHelpers
119
152
 
120
153
  def tsv_rows(tsv, page = nil, filter = nil, column = nil)
121
154
  tsv = tsv_process(tsv, filter, column)
155
+ length = tsv.size
122
156
  page = @page if page.nil?
123
157
  if page.nil? or page.to_s == "false"
124
- tsv_rows_full(tsv)
158
+ [tsv_rows_full(tsv), length]
125
159
  else
126
- tsv_rows_full(paginate(tsv, page))
160
+ [tsv_rows_full(paginate(tsv, page)), length]
127
161
  end
128
162
  end
129
163
 
130
164
 
131
165
  def table_value(value, type = nil, options = {})
132
166
  options = {} if options.nil?
133
- return value.list_link :length, options[:list_id] if Array === value and options[:list_id]
167
+ return value.list_link :length, options[:list_id] if Array === value and options[:list_id] and value.respond_to? :list_link
134
168
 
135
169
  entity_options = options[:entity_options]
136
170
 
137
- Misc.prepare_entity(value, type, entity_options) if Entity.formats.include? type
171
+ Misc.prepare_entity(value, type, entity_options) if Entity.formats.include? type and not options[:unnamed]
138
172
 
139
173
  value = value.link if value.respond_to? :link
140
174
 
141
- Array === value ? value * ", " : value
175
+ Array === value ? value.collect{|v| v.to_s} * ", " : value
142
176
  end
143
177
 
144
178
  def header(field, entity_type, entity_options = {})
@@ -151,12 +185,40 @@ module RbbtRESTHelpers
151
185
  @table_filters[field] = type
152
186
  end
153
187
 
188
+ def self.save_tsv(tsv, path)
189
+ Open.write(path, tsv.to_s)
190
+ table_options = {:tsv_entity_options => tsv.entity_options}
191
+ Open.write(path + '.table_options', table_options.to_yaml )
192
+ end
193
+
194
+ def self.load_tsv(file)
195
+ tsv = TSV.open(Open.open(file))
196
+
197
+ table_options = File.exists?(file + '.table_options') ? YAML.load_file(file + '.table_options') : {}
198
+ tsv.entity_options = table_options[:tsv_entity_options]
199
+ headers = table_options[:headers]
200
+ headers.each{|field,p| tsv.entity_templates[field] = Misc.prepare_entity("TEMPLATE", p.first, p.last) } unless headers.nil?
201
+
202
+ [tsv, table_options]
203
+ end
204
+
205
+ def save_tsv(file)
206
+ RbbtRESTHelpers.save_tsv(file)
207
+ end
208
+
209
+
210
+ def load_tsv(file)
211
+ RbbtRESTHelpers.load_tsv(file)
212
+ end
213
+
154
214
  def table(options = {})
155
215
  options = {} if options.nil?
156
216
 
157
217
  tsv = yield
158
218
 
159
- table_code = (rand * 100000).to_i.to_s
219
+ table_code = options[:table_id] || (rand * 100000).to_i.to_s
220
+ table_code = Entity::REST.clean_element(table_code)
221
+ table_code.sub!(/[^\w]/,'_')
160
222
  table_file = @step.file(table_code)
161
223
 
162
224
  url = add_GET_param(@fullpath, "_fragment", File.basename(table_file))
@@ -191,20 +253,19 @@ module RbbtRESTHelpers
191
253
  tsv2html(table_file)
192
254
  end
193
255
 
194
- def load_tsv(file)
195
- tsv
196
- end
197
-
198
- def tsv2html(file)
199
- tsv = TSV.open(Open.open(file))
200
256
 
201
- table_options = File.exists?(file + '.table_options') ? YAML.load_file(file + '.table_options') : {}
202
- tsv.entity_options = table_options[:tsv_entity_options]
203
- headers = table_options[:headers]
204
- filters = table_options[:filters]
205
- headers.each{|field,p| tsv.entity_templates[field] = Misc.prepare_entity("TEMPLATE", p.first, p.last) } unless headers.nil?
257
+ def tsv2html(file, default_table_options = {})
258
+ if TSV === file
259
+ tsv, table_options = file, {}
260
+ table_options[:unnamed] = tsv.unnamed
261
+ else
262
+ tsv, table_options = load_tsv(file)
263
+ end
206
264
 
207
265
  content_type "text/html"
208
- partial_render('partials/table', {:filters => filters, :total_size => tsv.size, :rows => tsv_rows(tsv), :header => tsv.all_fields, :table_options => table_options})
266
+ rows, length = tsv_rows(tsv)
267
+
268
+ table_options = default_table_options.merge(table_options)
269
+ partial_render('partials/table', {:total_size => length, :rows => rows, :header => tsv.all_fields, :table_options => table_options})
209
270
  end
210
271
  end