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
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require "rake"
5
+ require "rspec"
6
+ require "rspec/core/rake_task"
7
+
8
+ RSpec::Core::RakeTask.new("spec:unit") do |spec|
9
+ spec.pattern = "spec/unit/**/*_spec.rb"
10
+ end
11
+
12
+ task :spec => [ "spec:unit" ]
13
+ task :default => :spec
data/Readme.md ADDED
@@ -0,0 +1,58 @@
1
+ MA(p) R(educe) BU(ilder)
2
+ ========================
3
+
4
+ Marbu enables you to add mapreduce code-generating functionality to your software within seconds.
5
+
6
+ What does it do?
7
+ ----------------
8
+
9
+ Marbu contains three destinct parts
10
+
11
+ 1. The MapReduceFinalize-Model which acts as an Interface for the MapReduceBuilder
12
+ 2. The map-reduce core functionality: this creates map reduce code for different map reduce systems
13
+ 3. A Sinatra App which lets you instantly play around with the builder and shows you what you can do.
14
+
15
+ Installation
16
+ -----------------
17
+ Install with 'gem install marbu'.
18
+
19
+ For bundler add "gem 'marbu'" to your Gemfile.
20
+
21
+ Stand alone usage
22
+ -----------------
23
+
24
+ Marbu comes complete with a little Sinatra App to help you along. You start the app with 'marbu-web marbu-web-mongo-config.rb'.
25
+ This will start Marbu with the provided MongoDB-Configuration file.
26
+
27
+ If everything is working, you can go to 'localhost:5678' (if you are already running something on 5678 then try 5679) and
28
+ you should see all databases in your local MongoDB installation.
29
+
30
+ After selecting a Database you will see all collections within this database. You select a collection and go into the 'marbu' mode.
31
+ In the 'marbu' mode you can build your map - reduce - finalize functionality. After that run your query and see what you get. Done!
32
+
33
+
34
+ ## Development
35
+
36
+ Clone the repo
37
+
38
+ $ git clone git@github.com:QEDio/qstate.git
39
+
40
+ Update submodules
41
+
42
+ $ git submodule init && git submodule update
43
+
44
+ Install dependencies
45
+
46
+ $ bundle install
47
+
48
+ Run the tests
49
+
50
+ $ rake
51
+
52
+ Running the built-in Sinatra server can be done using the method above or using a standard config.ru file just like any other Rack-based app. Just make sure to provide the config file path through the corresponding environment variable:
53
+
54
+ $ MARBUCONFIG=bin/marbu-web-mongo-config.rb rackup
55
+
56
+ You can also activate the debugger and pick a specific server if you want:
57
+
58
+ $ MARBUCONFIG=bin/marbu-web-mongo-config.rb thin --debug --rackup config.ru start
data/bin/marbu-web ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ begin
5
+ require 'vegas'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'vegas'
9
+ end
10
+ require 'marbu/server'
11
+
12
+
13
+ Vegas::Runner.new(Marbu::Server, 'marbu-web', {
14
+ :before_run => lambda {|v|
15
+ path = (ENV['MARBUCONFIG'] || v.args.first)
16
+ load path.to_s.strip if path
17
+ }
18
+ }) do |runner, opts, app|
19
+
20
+ end
@@ -0,0 +1,8 @@
1
+ require 'mongo'
2
+
3
+ Marbu.uri = '127.0.0.1'
4
+ Marbu.port = '27017'
5
+
6
+ Mongoid.configure do |config|
7
+ config.master = Mongo::Connection.new.db('marbu')
8
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+ def uniq_by
3
+ hash, array = {}, []
4
+ each { |i| hash[yield(i)] ||= (array << i) }
5
+ array
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ module Enumerable
2
+ def mean
3
+ sum / size
4
+ end
5
+
6
+ # returns a new hash/array
7
+ # keeps object references in tact
8
+ # symbolize a hash recursivly
9
+ # if we encounter an array, we will also call symbolize for every hash or array found
10
+ def symbolize_keys_rec
11
+ if is_a?(Hash)
12
+ inject({}) do |options, (key, value)|
13
+ options[(key.to_sym rescue key) || key] = (value.is_a?(Hash)||value.is_a?(Array)) ? value.symbolize_keys_rec : value
14
+ options
15
+ end
16
+ elsif is_a?(Array)
17
+ inject([]) do |options, value|
18
+ options << ((value.is_a?(Hash)||value.is_a?(Array)) ? value.symbolize_keys_rec : value)
19
+ options
20
+ end
21
+ else
22
+ raise Exception.new("Can't do that!")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ class Hash
2
+ def has_nested_key?(key)
3
+ return true if has_key?(key)
4
+ values.select{|v| v.is_a?(Hash)}.each do |value|
5
+ return value.has_nested_key?(key)
6
+ end
7
+ return false
8
+ end
9
+
10
+ def nested_key(key)
11
+ return fetch(key) if has_key?(key)
12
+ values.select{|v| v.is_a?(Hash)}.each do |value|
13
+ return value.nested_key(key)
14
+ end
15
+ return default(key)
16
+ end
17
+
18
+ def deep_merge(other_hash)
19
+ a = self.dup
20
+ other_hash.each do |k, v|
21
+ if v.is_a? Hash
22
+ if a.has_key? k
23
+ a[k] = a[k].deep_merge(v)
24
+ else
25
+ a[k] = v
26
+ end
27
+ else
28
+ a[k] = v
29
+ end
30
+ end
31
+ a
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,11 @@
1
+ class HashWithIndifferentAccess
2
+ def self.deep_transform hash
3
+ a = HashWithIndifferentAccess.new(hash)
4
+ a.each do |k, v|
5
+ if v.is_a? Hash
6
+ v = deep_transform(v)
7
+ end
8
+ end
9
+ a
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ module CallerMethodName
2
+
3
+ private
4
+
5
+ def current_method
6
+ caller[0] =~ /\d:in `([^']+)'/
7
+ $1
8
+ end
9
+
10
+ def caller_method_name
11
+ parse_caller(caller(2).first).last
12
+ end
13
+
14
+ def parse_caller(at)
15
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
16
+ file = Regexp.last_match[1]
17
+ line = Regexp.last_match[2].to_i
18
+ method = Regexp.last_match[3]
19
+ [file, line, method]
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ Object.__send__(:include, CallerMethodName)
@@ -0,0 +1,3 @@
1
+ # monkeypatch openstruct to get the method id working for openstructs
2
+ # which get an id as attribute
3
+ OpenStruct.__send__(:define_method, :id) { @table[:id] }
@@ -0,0 +1,25 @@
1
+ class String
2
+ # Truncates the string after the first +chars+ (100) at word boundary and
3
+ # adds +ending+ ("...").
4
+ def truncate_at_word chars=100, ending="..."
5
+ sub(/^(.{#{chars}}[\w.]*)(.*)/m){ $2.empty? ? $1 : $1 + ending}
6
+ end
7
+
8
+ # returns a url/ascii/old-systems/non-utf8 friendly representation of self
9
+ def callname
10
+ @_callname ||= dup.
11
+ gsub(/[Ää]+/i,'ae').
12
+ gsub(/[Üü]+/i,'ue').
13
+ gsub(/[Öö]+/i,'oe').
14
+ gsub(/[ß]+/i,'ss').
15
+ downcase.
16
+ gsub(/[^a-z0-9]+/i, '-').
17
+ gsub(/(^[-]+|[-]+$)/, '')
18
+ end
19
+
20
+ def to_bool
21
+ return true if match('true')
22
+ return false
23
+ end
24
+ end
25
+
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Marbu
4
+ # the builder class is a lightweight container that holds a MapReduceModel and the requester Builder class
5
+ # its basically a front for the internals of the builders
6
+ class Builder
7
+ attr_accessor :map_reduce_model
8
+
9
+ def initialize(mrm, options = {})
10
+ if mrm.is_a?(Marbu::Models::MapReduceFinalize)
11
+ @map_reduce_model = mrm
12
+ elsif mrm.is_a?(Hash)
13
+ @map_reduce_model = Marbu::Models::MapReduceFinalize.new(mrm)
14
+ else
15
+ raise Exception.new("Parameter mrm was neither of type MapReduceFinalize nor Hash, but #{mrm.class}. Aborting")
16
+ end
17
+
18
+ @builder_clasz = options[:builder] || Marbu::Builder::Mongodb
19
+ @formatter_clasz = options[:formatter] || Marbu::Formatter::Dummy
20
+ end
21
+
22
+ def map(format = :text)
23
+ @formatter_clasz.perform(@builder_clasz.map(@map_reduce_model.map, format))
24
+ end
25
+
26
+ def reduce(format = :text)
27
+ @formatter_clasz.perform(@builder_clasz.reduce(@map_reduce_model.reduce, format))
28
+ end
29
+
30
+ def finalize(format = :text)
31
+ @formatter_clasz.perform(@builder_clasz.finalize(@map_reduce_model.finalize, format))
32
+ end
33
+
34
+ def query
35
+ @map_reduce_model.query.condition
36
+ end
37
+
38
+ def log
39
+ self.to_s
40
+ end
41
+
42
+ def to_s
43
+ puts "Map: #{map}"
44
+ puts "Reduce: #{reduce}"
45
+ puts "Finalize: #{finalize}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,106 @@
1
+ module Marbu
2
+ class Builder
3
+ class Mongodb
4
+ def self.map(map, format)
5
+ case format
6
+ when :text then map_int(map)
7
+ when :mongodb then cleanup(map_int(map))
8
+ end
9
+ end
10
+
11
+ def self.reduce(reduce, format)
12
+ case format
13
+ when :text then self.reduce_int(reduce)
14
+ when :mongodb then cleanup(self.reduce_int(reduce))
15
+ end
16
+ end
17
+
18
+
19
+ def self.finalize(finalize, format)
20
+ case format
21
+ when :text then self.finalize_int(finalize)
22
+ when :mongodb then cleanup(self.finalize_int(finalize))
23
+ end
24
+ end
25
+
26
+ private
27
+ def self.map_int(map)
28
+ <<-JS
29
+ function(){
30
+ #{get_value(:map)}
31
+ #{map.code.text}
32
+ #{get_emit(:map, map.values, map.keys)}
33
+ }
34
+ JS
35
+ end
36
+ def self.reduce_int(reduce)
37
+ <<-JS
38
+ function(key,values){
39
+ #{get_value(:reduce)}
40
+ #{reduce.code.text}
41
+ #{get_emit(:reduce, reduce.values)}
42
+ }
43
+ JS
44
+ end
45
+ def self.finalize_int(finalize)
46
+ <<-JS
47
+ function(key, value){
48
+ #{finalize.code.text}
49
+ #{get_emit(:finalize, finalize.values)}
50
+ }
51
+ JS
52
+ end
53
+
54
+ def self.cleanup(code)
55
+ code.gsub!(/[\n|\r]/,'')
56
+ end
57
+
58
+ def self.get_value(function)
59
+ case function
60
+ when :map then "value=this.value;"
61
+ when :reduce then "value=values[0];"
62
+ else raise Exception.new("Value-foo for #{function} not defined!")
63
+ end
64
+ end
65
+
66
+ def self.get_emit(function, values, keys = nil)
67
+ case function
68
+ when :map then emit_map(keys, values)
69
+ when :reduce then emit_reduce(values)
70
+ when :finalize then emit_finalize(values)
71
+ else raise Exception.new("Emit for #{function} not defined!")
72
+ end
73
+ end
74
+
75
+ def self.emit_map(keys, values)
76
+ emit = "emit( #{emit_keys(keys)}, "
77
+ emit += emit_core(values)
78
+ emit += " );"
79
+ return emit
80
+ end
81
+
82
+ def self.emit_reduce(values)
83
+ "return " + emit_core(values) + ";"
84
+ end
85
+
86
+ def self.emit_finalize(values)
87
+ "return " + emit_core(values) + ";"
88
+ end
89
+
90
+ def self.emit_keys(keys)
91
+ emit_core(keys)
92
+ end
93
+
94
+ def self.emit_core(values)
95
+ core = "{ "
96
+ values.each do |v|
97
+ core += " #{v.name}: #{v.function||v.name},"
98
+ end
99
+ # delete last comma
100
+ core = core[0..core.size-2]
101
+ core += " }"
102
+ return core
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Marbu
4
+ class Exceptions
5
+ class NoEmitKeys < RuntimeError; end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Marbu
2
+ class Formatter
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module Marbu
2
+ class Formatter
3
+ class Dummy < Base
4
+ def self.perform(str)
5
+ str
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ require 'marbu/formatters/base'
2
+ require 'marbu/formatters/dummy'
3
+ require 'marbu/formatters/one_line'
@@ -0,0 +1,9 @@
1
+ module Marbu
2
+ class Formatter
3
+ class OneLine < Base
4
+ def self.perform(str)
5
+ str.gsub(/( |\n)/, '')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ require 'marbu/models/db/mongodb/mongodb'
2
+ require 'marbu/models/db/mongodb/exceptions'
3
+ require 'marbu/models/db/mongodb/structure'
@@ -0,0 +1,59 @@
1
+ module Marbu
2
+ module Models
3
+ module Db
4
+ class MongoDb
5
+ class Exception < StandardError
6
+ REFERENCE_ERROR = 'ReferenceError'
7
+ NS_ERROR = 'NS_ERROR'
8
+ REGEX_DB_COMMAND = { :regex => /(ns doesn't exist)/,
9
+ :mapping => { :function => {:type => :string, :value => 'Buu'},
10
+ :error_type => {:type => :string, :value => NS_ERROR},
11
+ :variable => {:type => :string, :value => 'Buu'},
12
+ :assertion_code => {:type => :string, :value => 'Buu'},
13
+ :error_message => {:type => :string, :value => 'Buu'} }}
14
+ #REGEX_REF_ERROR = { :regex => /(.*?)'(.*?)'(.*?): \((assertion): '(.*?) (.*?): (.*?): (ReferenceError): (.*?)(is.*?)(nofile_b)?:\d'; (assertionCode): '(.*)';.*(errmsg): '(.*?)'/,
15
+ # mapping => { :function => 5, :error_type => 8, :variable => 9, :assertion_code => 13, :error_message => 15 }}}
16
+ REGEXS = [ REGEX_DB_COMMAND ]
17
+
18
+ def self.explain(e, mrf)
19
+ parsed_exception = parse(e)
20
+
21
+ case parsed_exception[:error_type]
22
+ when REFERENCE_ERROR then explain_reference_error(parsed_exception, mrf)
23
+ when NS_ERROR then { :id => NS_ERROR, :message => explain_ns_error(parsed_exception, mrf)}
24
+
25
+ else raise Exception.new("The Error #{e.to_s} is unknown to me. Parsed: #{parsed_exception.inspect}")
26
+ end
27
+ end
28
+
29
+ def self.parse(e)
30
+ {}.tap do |parsed_exception|
31
+ # http://rubular.com/r/MYM9ADhz4Z
32
+ REGEXS.each do |regex|
33
+ e.result["errmsg"] =~ regex[:regex]
34
+ if( $1.present? )
35
+ parsed_exception[:orig_error] = e
36
+ [:function, :error_type, :variable, :assertion_code, :error_message].each do |key|
37
+ case regex[:mapping][key][:type]
38
+ when :string then parsed_exception[key] = regex[:mapping][key][:value]
39
+ else "Unknown type: #{regex[:mapping][key][:type]}"
40
+ end
41
+ end
42
+ break
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.explain_ns_error( parsed_exception, mrf )
49
+ "Either the database #{mrf.misc.database} or the collection #{mrf.misc.input_collection} is unknown."
50
+ end
51
+
52
+ def self.explain_reference_error( parsed_exception, mrf )
53
+ "The variable #{parsed_exception[:variable]} defined in function #{parsed_exception[:function]} is unknown. This either means you are trying to access a datafield that doesn't exist in the collection, or you misspelled a variable name."
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'mongoid'
4
+ require 'uuid'
5
+
6
+ module Marbu
7
+ module Models
8
+ module Db
9
+ class MongoDb
10
+ include Mongoid::Document
11
+ store_in :marbu
12
+
13
+ field :uuid, type: String
14
+ field :name, type: String
15
+ field :map_reduce_finalize, type: MapReduceFinalize
16
+
17
+ def initialize( ext_params = {} )
18
+ super
19
+ params = default_params.merge( ext_params.delete_if{|k,v| v.blank? })
20
+
21
+ self.uuid = params[:uuid]
22
+ self.name = params[:name]
23
+ self.map_reduce_finalize = MapReduceFinalize.new( params[:map_reduce_finalize] )
24
+ end
25
+
26
+ def default_params
27
+ {
28
+ :name => "NoName00",
29
+ :uuid => UUID.new.generate(:compact),
30
+ :map_reduce_finalize => {
31
+ :map => {
32
+ :keys => [{:name => "map_key1"}],
33
+ :values => [{:name => "map_value1"}]
34
+ },
35
+ :reduce => {
36
+ :keys => [{:name => "reduce_key1"}],
37
+ :values => [{:name => "reduce_value1"}]
38
+ },
39
+ :finalize => {
40
+ :keys => [{:name => "finalize_key1"}],
41
+ :values => [{:name => "finalize"}]
42
+ },
43
+ :misc => {
44
+ :database => 'database',
45
+ :input_collection => 'input_collection',
46
+ :output_collection => 'output_collection'
47
+ }
48
+ }
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ module Marbu
2
+ module Models
3
+ module Db
4
+ class MongoDb
5
+ class Structure
6
+ def self.get_collection_structure( collection_info )
7
+ raise "collection_info needs to be Marbu::Models::Misc, but is: #{collection_info.class}" unless collection_info.is_a?(Marbu::Models::Misc)
8
+
9
+ collection = collection_info.collection
10
+ document = collection.find().first()
11
+
12
+ get_collection_structure_int(document)
13
+ end
14
+
15
+ def self.get_first_and_last_document( collection_info )
16
+ raise "collection_info needs to be Marbu::Models::Misc, but is: #{collection_info.class}" unless collection_info.is_a?(Marbu::Models::Misc)
17
+
18
+ collection = collection_info.collection
19
+
20
+ [collection.find().sort([['_id', 1]]).first(), collection.find().sort([['_id', -1]]).first()]
21
+ end
22
+
23
+
24
+ private
25
+ def self.get_collection_structure_int(document)
26
+ {}.tap do |hsh|
27
+ document.each_pair do |key, value|
28
+ if( value.is_a?(Hash) )
29
+ hsh[key] = get_collection_structure_int(value)
30
+ else
31
+ hsh[key] = key
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ module Marbu
2
+ module Models
3
+ class ExceptionLink
4
+ def self.get_exception_fix_link(error, uuid)
5
+ case error
6
+ when 'NS_ERROR' then "#misc"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'marbu/models/mrf/mrf'
2
+ require 'marbu/models/db/db'
3
+ require 'marbu/models/exception_link'