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,133 @@
1
+ module Marbu
2
+ module Models
3
+ class Base
4
+ attr_accessor :keys, :values
5
+ attr_reader :code
6
+
7
+ def initialize(ext_options = {})
8
+ # keep_if weeds out nil arguments
9
+ options = default_options.merge( ext_options.keep_if{|k,v| v} )
10
+
11
+ @keys = options[:keys].map{ |k| Key.new(k) }
12
+ @values = options[:values].map{ |v| Value.new(v) }
13
+ self.code = options[:code]
14
+ end
15
+
16
+ def default_options
17
+ {
18
+ :keys => [],
19
+ :values => [],
20
+ :code => Code.new
21
+ }
22
+ end
23
+
24
+ def code=(code)
25
+ if( code.is_a?(Code) )
26
+ @code = code
27
+ elsif( code.is_a?(Hash) )
28
+ @code = Code.new(code)
29
+ else
30
+ raise Exception.new("Don't know this Code type: #{code.class}")
31
+ end
32
+ end
33
+
34
+ def serializable_hash
35
+ {
36
+ :keys => keys.collect(&:serializable_hash),
37
+ :values => values.collect(&:serializable_hash),
38
+ :code => code.serializable_hash
39
+ }.delete_if{|k,v|v.blank?}
40
+ end
41
+
42
+ def add_key(name, function = nil)
43
+ add(:key, name, function)
44
+ end
45
+
46
+ def add_value(name, function = nil)
47
+ add(:value, name, function)
48
+ end
49
+
50
+ def present?
51
+ @keys.present? || code.present?
52
+ end
53
+
54
+ def blank?
55
+ !present?
56
+ end
57
+
58
+ private
59
+ def add(type, name, function)
60
+ function = Misc::DOCUMENT_OFFSET + name.to_s unless function
61
+ case type
62
+ when :key then @keys << Key.new({:name => name, :function => function})
63
+ when :value then @values << Value.new({:name => name, :function => function})
64
+ end
65
+ end
66
+ end
67
+
68
+ class KeyValueBase
69
+ attr_accessor :name, :function
70
+
71
+ def initialize(params)
72
+ #params.keys.each do |k|
73
+ # send("#{k}=".to_sym, params[k]) if respond_to?(k)
74
+ #end
75
+
76
+ self.name = params[:name]
77
+ self.function = params[:function]
78
+ end
79
+
80
+ def name=(name)
81
+ @name = name.to_s
82
+ end
83
+
84
+ def serializable_hash
85
+ # don't return nil values in hash
86
+ {
87
+ :name => @name,
88
+ :function => @function
89
+ }.keep_if{|k,v| v}
90
+ end
91
+
92
+ def present?
93
+ name.present? && function.present?
94
+ end
95
+
96
+ def blank?
97
+ !present?
98
+ end
99
+ end
100
+
101
+ class Key < KeyValueBase
102
+ attr_reader :exchangeable
103
+
104
+ def initialize( ext_options = {} )
105
+ options = default_options.merge( ext_options.delete_if{|k,v|v.nil?} )
106
+
107
+ self.exchangeable = options.delete(:exchangeable)
108
+ super(options)
109
+ end
110
+
111
+ def default_options
112
+ {
113
+ :exchangeable => true
114
+ }
115
+ end
116
+
117
+ def exchangeable=(e)
118
+ # make sure it's a boolean
119
+ if( !!e == e )
120
+ @exchangeable = e
121
+ else
122
+ raise Exception.new("Hey, I need a boolean!")
123
+ end
124
+
125
+ return self
126
+ end
127
+
128
+ end
129
+
130
+ class Value < KeyValueBase
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,32 @@
1
+ module Marbu
2
+ module Models
3
+ class Code
4
+ attr_accessor :text, :type
5
+
6
+ JS = "JS"
7
+
8
+ def initialize( ext_params = {} )
9
+ params = default_params.merge( ext_params.keep_if{|k,v|v} )
10
+ @text = params[:text]
11
+ @type = params[:type]
12
+ end
13
+
14
+ def default_params
15
+ {
16
+ :type => JS
17
+ }
18
+ end
19
+
20
+ def serializable_hash
21
+ {
22
+ :text => text,
23
+ :type => type
24
+ }.delete_if{|k,v|v.blank?}
25
+ end
26
+
27
+ def present?
28
+ text.present?
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Marbu
4
+ module Models
5
+ class Finalize < Base
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Marbu
2
+ module Models
3
+ class Map < Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'mongoid'
4
+
5
+ module Marbu
6
+ module Models
7
+ # represents one complete MapReduce-Model
8
+ # this model can be stored in a database and read again
9
+ # the builder will use MapReduce-Models to build actual mapreduce code
10
+ class MapReduceFinalize
11
+ include Mongoid::Fields::Serializable
12
+ attr_reader :map, :reduce, :finalize, :query, :misc
13
+
14
+ def initialize(ext_params = {})
15
+ # TODO: deep copy necessary why?
16
+ params = default_params.merge( Marshal.load(Marshal.dump(ext_params)) )
17
+
18
+ self.map = params[:map]
19
+ self.reduce = params[:reduce]
20
+ self.finalize = params[:finalize]
21
+ self.query = params[:query]
22
+ self.misc = params[:misc]
23
+ end
24
+
25
+ def default_params
26
+ {
27
+ :map => Map.new,
28
+ :reduce => Reduce.new,
29
+ :finalize => Finalize.new,
30
+ :query => Query.new,
31
+ :misc => Misc.new
32
+ }
33
+ end
34
+
35
+ def map=(map)
36
+ @map = general_setter(map, Marbu::Models::Map)
37
+ end
38
+
39
+ def reduce=(reduce)
40
+ @reduce = general_setter(reduce, Marbu::Models::Reduce)
41
+ end
42
+
43
+ def finalize=(finalize)
44
+ @finalize = general_setter(finalize, Marbu::Models::Finalize)
45
+ end
46
+
47
+ def query=(query)
48
+ @query = general_setter(query, Marbu::Models::Query)
49
+ end
50
+
51
+ def misc=(misc)
52
+ @misc = general_setter(misc, Marbu::Models::Misc)
53
+ end
54
+
55
+ def serializable_hash
56
+ {
57
+ :map => map.serializable_hash,
58
+ :reduce => reduce.serializable_hash,
59
+ :finalize => finalize.serializable_hash,
60
+ :query => query.serializable_hash,
61
+ :misc => misc.serializable_hash
62
+ }.delete_if{|k,v|v.blank?}
63
+ end
64
+
65
+ # return true if we have a map and a reduce function defined
66
+ def mapreduce?
67
+ !(map.nil? && reduce.nil?)
68
+ end
69
+
70
+ def query_only?
71
+ force_query || (!mapreduce? && !query.nil?)
72
+ end
73
+
74
+ def eql?(other)
75
+ serializable_hash == other.serializable_hash
76
+ end
77
+
78
+ def ==(other)
79
+ eql?(other)
80
+ end
81
+
82
+ # TODO: please remove after Mongoid gets non-intrusive de/serialization
83
+ def serialize(object)
84
+ object.serializable_hash
85
+ end
86
+
87
+ def deserialize(object)
88
+ MapReduceFinalize.new(object.symbolize_keys_rec)
89
+ end
90
+
91
+ private
92
+ def general_setter(var, klass)
93
+ if( var.is_a?(klass) )
94
+ ret_val = var
95
+ elsif( var.is_a?( ::Hash ) )
96
+ ret_val = klass.new(var)
97
+ elsif( var.nil? )
98
+ ret_val = klass.new
99
+ else
100
+ raise Exception.new("Unsupported var type: #{var.class}")
101
+ end
102
+
103
+ return ret_val
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,65 @@
1
+ module Marbu
2
+ module Models
3
+ class Misc
4
+ attr_accessor :database, :input_collection, :output_collection, :output_operation
5
+ # if filter_data is true, a query will be created depending on the filter parameters in filter_model
6
+ # if filter_data is false, no query will generated
7
+ # this is necessary for multiple sequentiell map_reduces: some will work on already filtered data (so no filter needed)
8
+ # while others will grab data from other collections which need to be filtered first
9
+ attr_reader :filter_data
10
+ attr_accessor :value, :document_offset
11
+
12
+ VALUE = :value
13
+ DOCUMENT_OFFSET = VALUE.to_s + "."
14
+
15
+ def initialize( ext_params = {} )
16
+ params = default_params.merge( ext_params.delete_if{|k,v|v.blank?} )
17
+
18
+ self.database = params[:database]
19
+ self.input_collection = params[:input_collection]
20
+ self.output_collection = params[:output_collection]
21
+ self.output_operation = params[:output_operation]
22
+ self.filter_data = params[:filter_data]
23
+ end
24
+
25
+ def default_params
26
+ {
27
+ :value => VALUE,
28
+ :document_offset => DOCUMENT_OFFSET,
29
+ :filter_data => false,
30
+ :output_operation => "replace"
31
+ }
32
+ end
33
+
34
+ def filter_data=(fd)
35
+ if( !!fd == fd )
36
+ @filter_data = fd
37
+ else
38
+ raise Exception.new("Need a boolean value!")
39
+ end
40
+ end
41
+
42
+ def present?
43
+ database.present? || input_collection.present? || output_collection.present?
44
+ end
45
+
46
+ def blank?
47
+ !present?
48
+ end
49
+
50
+ def serializable_hash
51
+ {
52
+ :database => database,
53
+ :input_collection => input_collection,
54
+ :output_collection => output_collection,
55
+ :output_operation => output_operation,
56
+ :filter_data => filter_data
57
+ }.delete_if{|k,v|v.blank?}
58
+ end
59
+
60
+ def collection
61
+ Marbu.connection.db(database).collection(input_collection)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ require 'marbu/models/mrf/base'
2
+ require 'marbu/models/mrf/map'
3
+ require 'marbu/models/mrf/reduce'
4
+ require 'marbu/models/mrf/finalize'
5
+ require 'marbu/models/mrf/query'
6
+ require 'marbu/models/mrf/misc'
7
+ require 'marbu/models/mrf/code'
8
+ require 'marbu/models/mrf/map_reduce_finalize'
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Marbu
4
+ module Models
5
+ class Query
6
+ DEFAULT_DATE_FIELDS = [:created_at]
7
+
8
+ attr_accessor :condition, :datetime_fields
9
+
10
+
11
+ def initialize( ext_params = {} )
12
+ params = default_params.merge( ext_params.keep_if{|k,v|v} )
13
+ @condition = params[:condition]
14
+ @datetime_fields = params[:datetime_fields]
15
+ end
16
+
17
+ def default_params
18
+ {
19
+ :datetime_fields => DEFAULT_DATE_FIELDS
20
+ }
21
+ end
22
+
23
+ def present?
24
+ condition.present? || !datetime_fields.eql?(DEFAULT_DATE_FIELDS)
25
+ end
26
+
27
+ def blank?
28
+ !present?
29
+ end
30
+
31
+ def serializable_hash
32
+ {
33
+ :condition => condition,
34
+ :datetime_fields => datetime_fields
35
+ }.delete_if{|k,v|v.blank?}
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ module Marbu
2
+ module Models
3
+ class Reduce < Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,149 @@
1
+ body {
2
+ padding: 0;
3
+ margin: 0;
4
+ }
5
+
6
+ .clearfix:after {
7
+ content: ".";
8
+ display: block;
9
+ clear: both;
10
+ visibility: hidden;
11
+ line-height: 0;
12
+ height: 0;
13
+ }
14
+
15
+ .clearfix {
16
+ display: inline-block;
17
+ }
18
+
19
+ html[xmlns] .clearfix {
20
+ display: block;
21
+ }
22
+
23
+ * html .clearfix {
24
+ height: 1%;
25
+ }
26
+
27
+
28
+
29
+
30
+ /* builder */
31
+ .builder .col {
32
+ display: block;
33
+ }
34
+
35
+ .builder .row {
36
+ display: block;
37
+ margin: 0 0 15px 0;
38
+ border: 1px dotted #777;
39
+ position: relative;
40
+ background: #ddd;
41
+ border-radius: 10px;
42
+ }
43
+
44
+ .builder .row.submit {
45
+ text-align: center;
46
+ padding: 20px 0 0 0;
47
+ border: none;
48
+ border-radius: 0;
49
+ background: none;
50
+ }
51
+
52
+ .builder .col legend {
53
+ font-weight: bold;
54
+ }
55
+
56
+ .builder .col label {
57
+ font-size: 0.8em;
58
+ color: #333;
59
+ }
60
+
61
+ .builder .col .pair {
62
+ /* float: left;*/
63
+ /* margin-right: 2%;*/
64
+ /* width: 45%;*/
65
+ margin: 0 auto 15px auto;
66
+ padding: 5px 10px 5px 5px;
67
+ float: left;
68
+ width: 40%;
69
+ }
70
+
71
+ .builder .col .pair > a:last-child {
72
+ display: none;
73
+ }
74
+
75
+ .builder .col .pair:last-child {
76
+ margin-right: 0;
77
+ margin-bottom: 0;
78
+ }
79
+
80
+ .builder .col .add-remove-element:first-child .pair:first-child > a:last-child {
81
+ display: block;
82
+ }
83
+
84
+
85
+ .add-remove {
86
+ clear: both;
87
+ margin: 0 0 20px 0;
88
+ }
89
+
90
+
91
+ .add-remove a.remove {
92
+ background: url('/images/remove.png') 0 0 no-repeat;
93
+ width: 20px;
94
+ height: 20px;
95
+ padding: 0;
96
+ text-indent: -9999em;
97
+ top: 5px;
98
+ right: 5px;
99
+ display: block;
100
+ position: absolute;
101
+ }
102
+
103
+ #container .pulldown {
104
+ margin: 0 .5em 40px .5em;
105
+ }
106
+
107
+ a[data-type="pulldown"] {
108
+ text-align: left;
109
+ }
110
+
111
+ #sample_data .ui-content .ui-btn,
112
+ #sample_data .ui-content .ui-collapsible {
113
+ cursor: pointer;
114
+ display: block;
115
+ margin: 0.5em 5px;
116
+ padding: 0;
117
+ position: relative;
118
+ text-align: left;
119
+ }
120
+
121
+ #sample_data .ui-content .ui-btn.tablink {
122
+ display: inline-block;
123
+ }
124
+
125
+ #sample_data .ui-collapsible-heading {
126
+ margin: 0;
127
+ }
128
+
129
+ #sample_data .ui-collapsible-content {
130
+ padding-left: 30px;
131
+ }
132
+
133
+ #sample_data a .example {
134
+ font-weight: normal;
135
+ margin-left: 10px;
136
+ font-size: .7em;
137
+ }
138
+
139
+ #sample_data .tree {
140
+ display: none;
141
+ }
142
+
143
+ #sample_data #sample_data_param_1 {
144
+ display: block;
145
+ }
146
+
147
+ .add_remove_button_wrapper > * {
148
+ float: left;
149
+ }