marbu 0.1.0

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 (64) hide show
  1. data/Rakefile +13 -0
  2. data/Readme.md +58 -0
  3. data/bin/marbu-web +20 -0
  4. data/bin/marbu-web-mongo-config.rb +8 -0
  5. data/lib/core_ext/array.rb +7 -0
  6. data/lib/core_ext/enumerable.rb +25 -0
  7. data/lib/core_ext/hash.rb +35 -0
  8. data/lib/core_ext/indifferent_access.rb +11 -0
  9. data/lib/core_ext/object.rb +25 -0
  10. data/lib/core_ext/open_struct.rb +3 -0
  11. data/lib/core_ext/string.rb +25 -0
  12. data/lib/marbu/builder.rb +48 -0
  13. data/lib/marbu/builders/mongodb.rb +106 -0
  14. data/lib/marbu/exceptions.rb +7 -0
  15. data/lib/marbu/formatters/base.rb +6 -0
  16. data/lib/marbu/formatters/dummy.rb +9 -0
  17. data/lib/marbu/formatters/formatters.rb +3 -0
  18. data/lib/marbu/formatters/one_line.rb +9 -0
  19. data/lib/marbu/models/db/db.rb +3 -0
  20. data/lib/marbu/models/db/mongodb/exceptions.rb +59 -0
  21. data/lib/marbu/models/db/mongodb/mongodb.rb +54 -0
  22. data/lib/marbu/models/db/mongodb/structure.rb +40 -0
  23. data/lib/marbu/models/exception_link.rb +11 -0
  24. data/lib/marbu/models/models.rb +3 -0
  25. data/lib/marbu/models/mrf/base.rb +133 -0
  26. data/lib/marbu/models/mrf/code.rb +32 -0
  27. data/lib/marbu/models/mrf/finalize.rb +9 -0
  28. data/lib/marbu/models/mrf/map.rb +7 -0
  29. data/lib/marbu/models/mrf/map_reduce_finalize.rb +107 -0
  30. data/lib/marbu/models/mrf/misc.rb +65 -0
  31. data/lib/marbu/models/mrf/mrf.rb +8 -0
  32. data/lib/marbu/models/mrf/query.rb +39 -0
  33. data/lib/marbu/models/mrf/reduce.rb +7 -0
  34. data/lib/marbu/server/public/css/style.css +149 -0
  35. data/lib/marbu/server/public/images/remove.png +0 -0
  36. data/lib/marbu/server/public/js/builder.js +200 -0
  37. data/lib/marbu/server/views/_builder_col_code.haml +8 -0
  38. data/lib/marbu/server/views/_builder_col_emit_keys.haml +13 -0
  39. data/lib/marbu/server/views/_builder_col_emit_values.haml +12 -0
  40. data/lib/marbu/server/views/_builder_data_samples.haml +8 -0
  41. data/lib/marbu/server/views/_builder_misc.haml +20 -0
  42. data/lib/marbu/server/views/_collection_nested_list.haml +0 -0
  43. data/lib/marbu/server/views/_footer.haml +2 -0
  44. data/lib/marbu/server/views/_header.haml +3 -0
  45. data/lib/marbu/server/views/_header_col.haml +6 -0
  46. data/lib/marbu/server/views/builder.haml +37 -0
  47. data/lib/marbu/server/views/layout.haml +19 -0
  48. data/lib/marbu/server/views/mapreduce.haml +9 -0
  49. data/lib/marbu/server/views/root.haml +11 -0
  50. data/lib/marbu/server/views/sample_data.haml +17 -0
  51. data/lib/marbu/server.rb +199 -0
  52. data/lib/marbu/server_sinatra.rb +168 -0
  53. data/lib/marbu/version.rb +3 -0
  54. data/lib/marbu.rb +65 -0
  55. data/spec/constants.rb +149 -0
  56. data/spec/rubymine-server.rb +0 -0
  57. data/spec/spec_helper.rb +63 -0
  58. data/spec/unit/builders/builder_spec.rb +5 -0
  59. data/spec/unit/builders/mongodb_spec.rb +52 -0
  60. data/spec/unit/models/base_spec.rb +33 -0
  61. data/spec/unit/models/map_reduce_finalize_spec.rb +28 -0
  62. data/spec/unit/models/map_spec.rb +22 -0
  63. data/spec/unit/models/query_spec.rb +23 -0
  64. metadata +209 -0
@@ -0,0 +1,200 @@
1
+ $(function(){
2
+ function add_remove () {
3
+ var el = $(this);
4
+
5
+ el.find('.add-remove-element:last')
6
+ .after('<div class="add_remove_button_wrapper clearfix"><button type="button" class="add" title="Add row">Add new row</button><div>');
7
+
8
+ var sample_data_button = el.find('a.sample_data');
9
+ if (sample_data_button.length > 0) {
10
+ el.find('.add_remove_button_wrapper:last').prepend(sample_data_button);
11
+ }
12
+
13
+ el.find('.add-remove-element > :last-child')
14
+ .css('margin-right', '0')
15
+ .after('<a href="#remove" class="remove" title="Remove">del</a>');
16
+
17
+ el.find('.add').bind('click', add);
18
+ el.find('.remove').bind('click', remove);
19
+
20
+ show_or_hide_remove_button();
21
+
22
+ function add (e) {
23
+ e.preventDefault();
24
+ var all = $(this).parents('.add-remove:first');
25
+ var my_clone = $(all.find('.add-remove-element:last').clone(true));
26
+ my_clone.find('input').val('');
27
+ all.find('.add-remove-element:last').after(my_clone.hide());
28
+ my_clone.show('fade', function(){$(this).find('input[type=text]:first').focus();});
29
+ show_or_hide_remove_button();
30
+ }
31
+
32
+ function remove (e) {
33
+ e.preventDefault();
34
+ var me = $(this).parents('.add-remove-element:first');
35
+ var all = me.parents('.add-remove:first');
36
+ me.hide('fade', function(){$(this).remove(); show_or_hide_remove_button();});
37
+ }
38
+
39
+ function show_or_hide_remove_button () {
40
+ if (el.find('.add-remove-element').length < 2)
41
+ el.find('a.remove').hide();
42
+ else
43
+ el.find('a.remove').show();
44
+ }
45
+ }
46
+
47
+ function remote_request (e) {
48
+ e.preventDefault();
49
+
50
+ var el = $(this);
51
+ var method = el.attr('method') || el.attr('data-method') || 'GET';
52
+ var url = el.attr('action') || el.attr('href');
53
+ var data_type = el.attr('data-type') || 'text';
54
+ var data = (el.is('form')) ? el.serializeArray():[];
55
+
56
+ $.ajax({
57
+ url: url,
58
+ data: data,
59
+ dataType: data_type,
60
+ type: method.toUpperCase(),
61
+ beforeSend: function (xhr) {
62
+ el.trigger('ajax:loading', xhr);
63
+ },
64
+ success: function (data, status, xhr) {
65
+ el.trigger('ajax:success', [data, status, xhr]);
66
+ },
67
+ complete: function (element, xhr, statusText) {
68
+ el.trigger('ajax:complete', [element, xhr, statusText]);
69
+ },
70
+ error: function (xhr, status, error) {
71
+ el.trigger('ajax:failure', [xhr, status, error]);
72
+ }
73
+ });
74
+ }
75
+
76
+ function pulldown_click (e) {
77
+ $(this).next().toggle('slide', {direction: 'up'});
78
+ }
79
+
80
+
81
+ // $('body').delegate('a[data-ajax="true"]', 'click.remote', remote_request);
82
+ // $('body').delegate('form[data-ajax="true"]', 'submit.remote', remote_request);
83
+ $('.add-remove').each(add_remove);
84
+ $('a[data-type="pulldown"]').bind('click.pulldown', pulldown_click);
85
+ $('#container .pulldown').hide();
86
+
87
+ $('a.delete').live('click', function(event) {
88
+ if ( confirm("Are you sure you want to delete this item?") )
89
+ $('<form method="post" action="' + this.href.replace('/delete', '') + '" />')
90
+ .append('<input type="hidden" name="_method" value="delete" />')
91
+ .appendTo('body')
92
+ .submit();
93
+
94
+ return false;
95
+ });
96
+
97
+
98
+ function sample_data_click (e) {
99
+ var el = $(this);
100
+
101
+ if (!el.data(data_content))
102
+ return false;
103
+
104
+ /* prevent too fast clicks */
105
+ if (sample_data_click.active) {
106
+ return false;
107
+ }
108
+
109
+ sample_data_click.active = true;
110
+ var keys = new Array();
111
+ var input = next_free_input(el);
112
+
113
+ if (!input)
114
+ return sample_data_click.active = false;
115
+
116
+ var data_content = 'key';
117
+
118
+ if (!input)
119
+ return sample_data_click.active = false;
120
+
121
+ while (el.data(data_content)) {
122
+ keys.push(el.data(data_content));
123
+ el = el.parents('.ui-collapsible:first');
124
+ }
125
+
126
+ var value = keys.reverse();
127
+ $(input)
128
+ .val(value[value.length-1])
129
+ .trigger('change');
130
+ $(input)
131
+ .parent()
132
+ .next()
133
+ .find('input')
134
+ .val(value.join('.'))
135
+ .trigger('change');
136
+ $(this).remove();
137
+ window.setTimeout(function(){
138
+ sample_data_click.active = false;
139
+ }, 500);
140
+
141
+ /* search an empty input field. if not exist, add new one */
142
+ function next_free_input (el) {
143
+ var input = $('input#' + el.data('input'));
144
+
145
+ if (!input || input.length < 1)
146
+ return null;
147
+
148
+ var name = input.attr('name');
149
+ var wrapper = $(input).parents('.add-remove:first');
150
+
151
+ input = wrapper.find('input[name="' + name + '"][value=""]:first');
152
+ if (input.val() == '')
153
+ return input;
154
+ wrapper.find('button.add').click();
155
+
156
+ input = wrapper.find('input[name="' + name + '"][value=""]:first');
157
+
158
+ if (input.val() == '')
159
+ return input;
160
+ return false;
161
+ }
162
+ }
163
+ $('#sample_data .tree a').live('click.sample_data', sample_data_click);
164
+ $('#sample_data a.tablink').live('click.tablink', function(e) {
165
+ e.preventDefault();
166
+ e.stopPropagation();
167
+ $('#sample_data .tree').hide();
168
+ var selector = $(this).attr('href');
169
+ $(selector).show();
170
+ });
171
+
172
+
173
+ $('input, select, textarea').each(function(){
174
+ $(this).data('original_val', $(this).val());
175
+ }).live('change', function(){
176
+ if ($(this).val() != $(this).data('original_val')) {
177
+ reload_code_preview();
178
+ try {
179
+ $("#button-save").button('enable');
180
+ } catch (e) {}
181
+ $('#button-execute, #button-delete').addClass('ui-disabled');
182
+ } else {
183
+ try {
184
+ $("#button-save").button('disable');
185
+ } catch (e) {}
186
+ $('#button-execute, #button-delete').removeClass('ui-disabled');
187
+ }
188
+ });
189
+
190
+ function reload_code_preview () {
191
+ var form = $('form#builder_form');
192
+ if (form.length < 1)
193
+ return;
194
+
195
+ var data = form.serialize();
196
+ $.post(form.attr('action') + '/code', data, function(data) {
197
+ $('#code_map').val(data);
198
+ });
199
+ }
200
+ });
@@ -0,0 +1,8 @@
1
+ %a{:data => {:role => 'button', :type => 'pulldown'}} Your Javascript-Code
2
+ .clearfix.row.pulldown
3
+ %label{:for => "code"} Your Javascript-Code:
4
+ %textarea{:type => "text", :name => "#{type}_code", :class => "text", :rows => 10, :cols => 50}= user_code.text
5
+ %a{:data => {:role => 'button', :type => 'pulldown'}} MR-Code for #{type}
6
+ .clearfix.row.pulldown
7
+ %label{:for => "code_#{type}"} MR-Code for #{type}:
8
+ %textarea{:type => "text", :name => "key1", :class => "text", :rows => 10, :cols => 50, :readonly => 'readonly', :id => "code_#{type}"}= generated_code
@@ -0,0 +1,13 @@
1
+ - if not type.eql?('reduce')
2
+ %a{:data => {:role => 'button', :type => 'pulldown'}} Emit-Keys/Emit-Key-Functions
3
+ - emit_keys.each_with_index do |key, i|
4
+ .add-remove.pulldown
5
+ .clearfix.row.add-remove-element
6
+ %div.pair
7
+ %label{:for => "#{type}_key_#{i}"} Emit-Key:
8
+ %input{:type => "text", :name => "#{type}_key_name[]", :class => "text", :value => key.name, :id => "#{type}_key_#{i}"}
9
+ %a{:href => "/builder/sample_data/#{params[:uuid]}/#{type}_key_#{i}", :class => 'sample_data', :data => {:rel => 'dialog', :transition => 'flip', :corners => 'true', :shadow => 'true', :wrapperels => 'span', :role => 'button', :inline => 'true'}}
10
+ = 'Sample Data'
11
+ %div.pair
12
+ %label{:for => "#{type}_key_function_#{i}"} Emit-Key-Function:
13
+ %input{:type => "text", :name => "#{type}_key_function[]", :class => "text", :value => key.function, :id => "#{type}_key_function_#{i}"}
@@ -0,0 +1,12 @@
1
+ %a{:data => {:role => 'button', :type => 'pulldown'}} Emit-Values/Emit-Functions
2
+ .add-remove.pulldown
3
+ - emit_values.each_with_index do |value, i|
4
+ .clearfix.row.add-remove-element
5
+ .pair
6
+ %label{:for => "#{type}_value_#{i}"} Emit-Value:
7
+ %input{:type => "text", :name => "#{type}_value_name[]", :class => "text", :value => value.name, :id => "#{type}_value_#{i}"}
8
+ %a{:href => "/builder/sample_data/#{params[:uuid]}/#{type}_value_#{i}", :class => 'sample_data', :data => {:rel => 'dialog', :transition => 'flip', :corners => 'true', :shadow => 'true', :wrapperels => 'span', :role => 'button', :inline => 'true'}}
9
+ = 'Sample Data'
10
+ .pair
11
+ %label{:for => "#{type}_value_function_#{i}"} Emit-Value-Function:
12
+ %input{:type => "text", :name => "#{type}_value_function[]", :class => "text", :value => value.function, :id => "#{type}_value_function_#{i}"}
@@ -0,0 +1,8 @@
1
+ - if data_samples.present?
2
+ %a{:data => {:role => 'button', :type => 'pulldown'}} Sample Data
3
+ .pulldown
4
+ - data_samples.each_with_index do |document, i|
5
+ .clearfix.row
6
+ .pair
7
+ %ul{:data => {:role => 'listview'}}
8
+ = document_list(document)
@@ -0,0 +1,20 @@
1
+ %div{:id => 'misc', :data => {:role => 'page', :title => "Settings"}}
2
+ = haml :_header_col, :layout => false, :locals => {:title => @mrm.new? ? "New map reduce" : @mrm.name }
3
+ %div{:data => {:role => 'content'}}
4
+ %h2 Settings
5
+ .clearfix.row.add-remove-element
6
+ .pair
7
+ %label{:for => "name"} Name:
8
+ %input{:type => "text", :name => "name", :class => "text", :value => @mrm.name, :id => "name"}
9
+ .pair
10
+ %label{:for => "database"} Database
11
+ %input{:type => "text", :name => "database", :class => "text", :value => @mrf.misc.database, :id => "database"}
12
+ .pair
13
+ %label{:for => "input_collection"} Input Collection:
14
+ %input{:type => "text", :name => "input_collection", :class => "text", :value => @mrf.misc.input_collection, :id => "input_collection"}
15
+ .pair
16
+ %label{:for => "output_collection"} Output Collection:
17
+ %input{:type => "text", :name => "output_collection", :class => "text", :value => @mrf.misc.output_collection, :id => "output_collection"}
18
+ - if @mrm.new?
19
+ %a{:href => "#main", :data => {:role => "button", :icon => 'arrow-r', :iconpos => "right"}} Continue
20
+ = haml :_footer, :layout => false
@@ -0,0 +1,2 @@
1
+ .footer{:data=>{:role => 'footer'}}
2
+ %h4 &copy; Marbu
@@ -0,0 +1,3 @@
1
+ %div.header{:data=>{:role => 'header'}}
2
+ %h1= title || 'Marbu(h)'
3
+ %a{:href => '/builder/new#misc', :data => {:transition => 'slide', :ajax => 'false', :icon => 'plus'}, :class => "ui-btn-right"} New
@@ -0,0 +1,6 @@
1
+ %div.header{:data=>{:position=>'fixed', :role => 'header'}}
2
+ - if defined?(show_home)
3
+ %a{ :href => "/", :data => {:role => "button", :icon => "home", :ajax => 'false'}, :class => 'ui-btn-left'} Home
4
+ - else
5
+ %a{ :href => "#main", :data => {:role => "button", :icon => "grid", :transition => 'reverse slide'}, :class => 'ui-btn-left'} Main
6
+ %h1= title
@@ -0,0 +1,37 @@
1
+ %form{:id => "builder_form", :action => @mrm.new? ? '/builder' : "/builder/#{@mrm.uuid}", :method => "post", :class => 'builder', :data => {:transition => 'none', :ajax => 'false'}}
2
+ - unless @mrm.new?
3
+ %input{:type => "hidden", :name => "_method", :value => "put"}
4
+ %div{:id => 'main', :data => {:role => 'page', :title => @mrm.new? ? "New map reduce object" : @mrm.name}}
5
+ = haml :_header_col, :layout => false, :locals => {:title => @mrm.new? ? "New map reduce object" : @mrm.name, :show_home => true}
6
+ %div{:data => {:role => 'content'}}
7
+ %ul{:data => {:role => 'listview'}}
8
+ %li
9
+ %a{:href => "#misc", :data => {:transition => 'slide'}} Settings
10
+ - ["map", "reduce", "finalize"].each do |type|
11
+ %li
12
+ %a{:href => "##{type}", :data => {:transition => 'slide'}}=type.capitalize
13
+ .row.submit.save_and_back
14
+ %button{:id => 'button-save', :type => "submit", :value => "Save", :class => "button", :data => {:icon => 'check'}} Save
15
+ %a{:id => 'button-execute', :href => "/mapreduce/#{@mrm.uuid}", :data => {:transition => 'slide', :role => 'button', :icon => 'search'}, :class => @mrm.new? ? "ui-disabled" : nil } Execute
16
+ %a{:id => 'button-delete', :href => "/builder/#{@mrm.uuid}/delete", :class => @mrm.new? ? "ui-disabled delete" : "delete", :data => {:ajax => 'false', :role => 'button', :icon => 'delete'}} Delete
17
+
18
+ = haml :_footer, :layout => false
19
+
20
+ - ["map", "reduce", "finalize"].each do |type|
21
+ %div.col{:id => type, :data => {:role => 'page', :title => @mrm.new? ? "New map reduce object" : @mrm.name}}
22
+ = haml :_header_col, :layout => false, :locals => {:title => @mrm.new? ? "New map reduce object" : @mrm.name }
23
+ %div{:data => {:role => 'content'}}
24
+ %h2= type.capitalize
25
+ .builder
26
+ = haml :_builder_col_emit_keys, :locals => {:type => type, :emit_keys => @mrf.send(type.to_sym).keys}
27
+ = haml :_builder_col_emit_values, :locals => {:type => type, :emit_values => @mrf.send(type.to_sym).values}
28
+ = haml :_builder_col_code, :locals => {:type => type, :user_code => @mrf.send(type.to_sym).code, :generated_code => Marbu::Builder.new(@mrf).send(type.to_sym)}
29
+ = haml :_footer, :layout => false
30
+
31
+ = haml :_builder_misc, :layout => false
32
+
33
+ - unless @mrm.new?
34
+ :javascript
35
+ $(function() {
36
+ setTimeout(function() {$("#button-save").button("disable");}, 100);
37
+ });
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ %html(lang="en")
3
+ %head
4
+ %meta(charset="utf-8")
5
+ - if @description
6
+ %meta(name="description" content=@description)
7
+ - if @keywords
8
+ %meta(name="keywords" content=@keywords)
9
+ %meta(name='viewport' content='width=device-width, initial-scale=1')
10
+ %title= @title
11
+ %script(src='http://code.jquery.com/jquery-1.6.4.min.js')
12
+ %script(src='http://code.jquery.com/ui/jquery-ui-git.js')
13
+ %script(src='/js/builder.js')
14
+ %script(src='http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js')
15
+ %link(href='/css/style.css' rel='stylesheet' type='text/css')
16
+ %link(href='http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css' rel='stylesheet' type='text/css')
17
+ %body
18
+ #container
19
+ = yield
@@ -0,0 +1,9 @@
1
+ %div{:data => {:role => 'content'}}
2
+ - if @error.present?
3
+ = @error.to_s
4
+ %a{:href => @fix_link, :data => {:transition => 'slide', :role => 'button'}}= 'Fix it'
5
+ - else
6
+ %textarea{:type => "text", :name => "key1", :class => "text", :rows => 10, :cols => 100}= @res.find().to_a.inspect
7
+ %input{:type => "submit", :value => "Save", :class => "button"}
8
+
9
+ %a{:href => "/builder/#{@mrm.uuid}", :data => {:transition => 'slide', :role => 'button'}}= 'Back'
@@ -0,0 +1,11 @@
1
+ %div{:id => 'root', :data => {:role => 'page'}}
2
+ = haml :_header, :layout => false, :locals => { :title => "Listing map reduce objects" }
3
+ %div{:data => {:role => 'content'}}
4
+ %ul{:data => {:role => 'listview'}}
5
+ - @mrms.each do |mrf|
6
+ - uuid = mrf.uuid
7
+ - name = mrf.name
8
+ %li
9
+ %a{:href => '/builder/' + uuid, :data => {:transition => 'slide', :ajax => 'false'}}=name.capitalize
10
+
11
+ = haml :_footer, :layout => false
@@ -0,0 +1,17 @@
1
+ %div#sample_data{:data => {:role => 'dialog'}}
2
+ %div{:data => {:role => 'header'}}
3
+ %h1='Sample data'
4
+
5
+ %div{:data => {:role => 'content'}}
6
+ - if @data_samples.present?
7
+ %a{:href => "#sample_data_param_1", :class => 'tablink', :data => {:role => 'button'}}
8
+ Param 1
9
+ %a{:href => "#sample_data_param_2", :class => 'tablink', :data => {:role => 'button'}}
10
+ Param 2
11
+ - @data_samples.each_with_index do |document, i|
12
+ %div.tree{:id => "sample_data_param_#{i+1}"}
13
+ %h3="param_#{i+1}"
14
+ = sample_data_tree(document, params[:input_id])
15
+
16
+ %a{:data => {:rel => 'back', :role => 'button'}}
17
+ ='Back'
@@ -0,0 +1,199 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra/reloader'
5
+ require 'haml'
6
+ require 'time'
7
+ require 'marbu'
8
+ require 'uuid'
9
+
10
+ module Marbu
11
+ class Server < Sinatra::Base
12
+ configure :development do
13
+ require 'ruby-debug'
14
+ enable :logging, :dump_errors, :raise_errors
15
+ register Sinatra::Reloader
16
+ end
17
+
18
+ enable :method_override
19
+
20
+ dir = File.dirname(File.expand_path(__FILE__))
21
+ set :views, "#{dir}/server/views"
22
+ set :public_folder, "#{dir}/server/public"
23
+ set :static, true
24
+
25
+ helpers do
26
+ def url_path(*path_parts)
27
+ [ path_prefix, path_parts ].join("/").squeeze('/')
28
+ end
29
+ alias_method :u, :url_path
30
+
31
+ def path_prefix
32
+ request.env['SCRIPT_NAME']
33
+ end
34
+
35
+ def document_list(document)
36
+ document.inject("") do |html, (key, value)|
37
+ if( value.is_a?(Hash) )
38
+ html += "<li>#{key}<ul>#{document_list(value)}</ul></li>"
39
+ else
40
+ html += "<li>#{key}: #{value}</li>"
41
+ end
42
+ end
43
+ end
44
+
45
+ def sample_data_tree(document, input_id)
46
+ document.inject('') do |html, (key, value)|
47
+ if (value.is_a?(Hash))
48
+ html += '<div data-role="collapsible" data-collapsed="true" data-key="' + key + '">'
49
+ html += '<h3> ' + key + '</h3>'
50
+ html += sample_data_tree(value, input_id)
51
+ html +='</div>'
52
+ else
53
+ html += '<a data-role="button" data-type="sample_data" data-input="' + input_id + '" data-key="' + key + '" data-value="' + value.to_s + '">'
54
+ html += key.to_s
55
+ html += '<span class="example">(example: ' + value.to_s + ')</span>';
56
+ html += '</a>';
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ get "/" do
64
+ @mrms = Marbu::Models::Db::MongoDb.all
65
+ show 'root'
66
+ end
67
+
68
+ get "/builder/new" do
69
+ @mrm = Marbu::Models::Db::MongoDb.new
70
+ @mrf = @mrm.map_reduce_finalize
71
+ show 'builder'
72
+ end
73
+
74
+ get '/builder/sample_data/:uuid/:input_id' do
75
+ @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
76
+ @mrf = @mrm.map_reduce_finalize
77
+ @data_samples = Marbu::Models::Db::MongoDb::Structure.get_first_and_last_document(@mrf.misc)
78
+ show 'sample_data'
79
+ end
80
+
81
+ post "/builder" do
82
+ @mrm = build_mrm_from_params(params.merge({:logger => logger}))
83
+ @mrm.save!
84
+ redirect "/builder/#{@mrm.uuid}"
85
+ end
86
+
87
+ get "/builder/:uuid" do
88
+ @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
89
+ @mrf = @mrm.map_reduce_finalize
90
+ @data_samples = Marbu::Models::Db::MongoDb::Structure.get_first_and_last_document(@mrf.misc)
91
+
92
+ show 'builder'
93
+ end
94
+
95
+ put "/builder/:uuid" do
96
+ @mrm = build_mrm_from_params(params.merge({:logger => logger}))
97
+ @mrm.save!
98
+ redirect "/builder/#{@mrm.uuid}"
99
+ end
100
+
101
+ delete "/builder/:uuid" do
102
+ @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
103
+ @mrm.destroy if @mrm.present?
104
+ redirect "/"
105
+ end
106
+
107
+ get "/mapreduce/:uuid" do
108
+ @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
109
+ @mrf = @mrm.map_reduce_finalize
110
+ @builder = Marbu::Builder.new(@mrf)
111
+ @error = nil
112
+
113
+ begin
114
+ # TODO: naturally this has to take the DATABASE and COLLECTION from the mapreducefilter object
115
+ # TODO: don;t take the parameters from the mapreducefilter object if DATABASE or DATABASE and COLLECTION are
116
+ # TODo: defined in the configuration (security)
117
+ @res = @mrf.misc.collection.map_reduce( @builder.map(:mongodb), @builder.reduce(:mongodb),
118
+ {
119
+ :query => @builder.query,
120
+ :out => {:replace => "tmp."+@mrm.map_reduce_finalize.misc.output_collection},
121
+ :finalize => @builder.finalize(:mongodb)
122
+ }
123
+ )
124
+ rescue Mongo::OperationFailure => e
125
+ @parsed_error = Marbu::Models::Db::MongoDb::Exception.explain(e, @mrf)
126
+ @error = @parsed_error[:message]
127
+ @fix_link = Marbu::Models::ExceptionLink.get_exception_fix_link(@parsed_error[:id], params['uuid'])
128
+ end
129
+
130
+ show 'mapreduce'
131
+ end
132
+
133
+ def build_mrm_from_params(params)
134
+ name = 'name'
135
+ function = 'function'
136
+
137
+ if (uuid = params['uuid'])
138
+ mrm = Marbu::Models::Db::MongoDb.find_or_create_by(uuid: uuid)
139
+ else
140
+ mrm = Marbu::Models::Db::MongoDb.new
141
+ end
142
+ mrm.name = params['name']
143
+ mrf = mrm.map_reduce_finalize
144
+
145
+ mrf.map = Marbu::Models::Map.new(
146
+ :code => {:text => params['map_code']}
147
+ )
148
+ mrf.reduce = Marbu::Models::Reduce.new(
149
+ :code => {:text => params['reduce_code']}
150
+ )
151
+ mrf.finalize = Marbu::Models::Finalize.new(
152
+ :code => {:text => params['finalize_code']}
153
+ )
154
+ mrf.query = Marbu::Models::Query.new(
155
+ :condition => params['query_condition'],
156
+ :force_query => params['query_force_query']
157
+ )
158
+ mrf.misc = Marbu::Models::Misc.new(
159
+ :database => params['database'],
160
+ :input_collection => params['input_collection'],
161
+ :output_collection => params['output_collection']
162
+ )
163
+
164
+ # add params to map_new, reduce_new, finalize_new
165
+ ['map', 'reduce', 'finalize'].each do |stage|
166
+ ['key', 'value'].each do |type|
167
+ stage_type_name = params["#{stage}_#{type}_#{name}"]
168
+ stage_type_function = params["#{stage}_#{type}_#{function}"]
169
+
170
+ if( stage_type_name.present?)
171
+ stage_type_name.each_with_index do |n, i|
172
+ case stage
173
+ when 'map' then add(mrf.map, type, n, stage_type_function[i])
174
+ when 'reduce' then add(mrf.reduce, type, n, stage_type_function[i])
175
+ when 'finalize' then add(mrf.finalize, type, n, stage_type_function[i])
176
+ else raise Exception.new("#{stage} in #{k} is unknown")
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ mrm.map_reduce_finalize = mrf
184
+ return mrm
185
+ end
186
+
187
+ def add(model, type, name, function)
188
+ case type
189
+ when 'key' then model.add_key(name, function)
190
+ when 'value' then model.add_value(name, function)
191
+ else raise Exception.new("#{type} is unknown")
192
+ end
193
+ end
194
+
195
+ def show(page)
196
+ haml page.to_sym
197
+ end
198
+ end
199
+ end