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.
- data/Rakefile +13 -0
- data/Readme.md +58 -0
- data/bin/marbu-web +20 -0
- data/bin/marbu-web-mongo-config.rb +8 -0
- data/lib/core_ext/array.rb +7 -0
- data/lib/core_ext/enumerable.rb +25 -0
- data/lib/core_ext/hash.rb +35 -0
- data/lib/core_ext/indifferent_access.rb +11 -0
- data/lib/core_ext/object.rb +25 -0
- data/lib/core_ext/open_struct.rb +3 -0
- data/lib/core_ext/string.rb +25 -0
- data/lib/marbu/builder.rb +48 -0
- data/lib/marbu/builders/mongodb.rb +106 -0
- data/lib/marbu/exceptions.rb +7 -0
- data/lib/marbu/formatters/base.rb +6 -0
- data/lib/marbu/formatters/dummy.rb +9 -0
- data/lib/marbu/formatters/formatters.rb +3 -0
- data/lib/marbu/formatters/one_line.rb +9 -0
- data/lib/marbu/models/db/db.rb +3 -0
- data/lib/marbu/models/db/mongodb/exceptions.rb +59 -0
- data/lib/marbu/models/db/mongodb/mongodb.rb +54 -0
- data/lib/marbu/models/db/mongodb/structure.rb +40 -0
- data/lib/marbu/models/exception_link.rb +11 -0
- data/lib/marbu/models/models.rb +3 -0
- data/lib/marbu/models/mrf/base.rb +133 -0
- data/lib/marbu/models/mrf/code.rb +32 -0
- data/lib/marbu/models/mrf/finalize.rb +9 -0
- data/lib/marbu/models/mrf/map.rb +7 -0
- data/lib/marbu/models/mrf/map_reduce_finalize.rb +107 -0
- data/lib/marbu/models/mrf/misc.rb +65 -0
- data/lib/marbu/models/mrf/mrf.rb +8 -0
- data/lib/marbu/models/mrf/query.rb +39 -0
- data/lib/marbu/models/mrf/reduce.rb +7 -0
- data/lib/marbu/server/public/css/style.css +149 -0
- data/lib/marbu/server/public/images/remove.png +0 -0
- data/lib/marbu/server/public/js/builder.js +200 -0
- data/lib/marbu/server/views/_builder_col_code.haml +8 -0
- data/lib/marbu/server/views/_builder_col_emit_keys.haml +13 -0
- data/lib/marbu/server/views/_builder_col_emit_values.haml +12 -0
- data/lib/marbu/server/views/_builder_data_samples.haml +8 -0
- data/lib/marbu/server/views/_builder_misc.haml +20 -0
- data/lib/marbu/server/views/_collection_nested_list.haml +0 -0
- data/lib/marbu/server/views/_footer.haml +2 -0
- data/lib/marbu/server/views/_header.haml +3 -0
- data/lib/marbu/server/views/_header_col.haml +6 -0
- data/lib/marbu/server/views/builder.haml +37 -0
- data/lib/marbu/server/views/layout.haml +19 -0
- data/lib/marbu/server/views/mapreduce.haml +9 -0
- data/lib/marbu/server/views/root.haml +11 -0
- data/lib/marbu/server/views/sample_data.haml +17 -0
- data/lib/marbu/server.rb +199 -0
- data/lib/marbu/server_sinatra.rb +168 -0
- data/lib/marbu/version.rb +3 -0
- data/lib/marbu.rb +65 -0
- data/spec/constants.rb +149 -0
- data/spec/rubymine-server.rb +0 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/unit/builders/builder_spec.rb +5 -0
- data/spec/unit/builders/mongodb_spec.rb +52 -0
- data/spec/unit/models/base_spec.rb +33 -0
- data/spec/unit/models/map_reduce_finalize_spec.rb +28 -0
- data/spec/unit/models/map_spec.rb +22 -0
- data/spec/unit/models/query_spec.rb +23 -0
- 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,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
|
|
File without changes
|
|
@@ -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'
|
data/lib/marbu/server.rb
ADDED
|
@@ -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
|