noah 0.0.5-jruby → 0.1-jruby
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/.gemtest +0 -0
- data/.gitignore +10 -0
- data/LICENSE +201 -0
- data/README.md +68 -212
- data/Rakefile +65 -41
- data/TODO.md +65 -0
- data/bin/noah +2 -1
- data/bin/noah-watcher.rb +103 -0
- data/config.ru +6 -3
- data/config/warble.rb +18 -0
- data/examples/README.md +116 -0
- data/examples/cluster.ru +2 -0
- data/examples/custom-watcher.rb +10 -0
- data/examples/httpclient-server.rb +7 -0
- data/examples/httpclient.rb +12 -0
- data/examples/httpclient2.rb +28 -0
- data/examples/js/FABridge.js +1452 -0
- data/examples/js/WebSocketMain.swf +830 -0
- data/examples/js/swfobject.js +851 -0
- data/examples/js/web_socket.js +312 -0
- data/examples/logger.rb +11 -0
- data/examples/reconfiguring-sinatra-watcher.rb +11 -0
- data/examples/reconfiguring-sinatra.rb +33 -0
- data/examples/simple-post.rb +17 -0
- data/examples/websocket.html +24 -0
- data/examples/websocket.rb +41 -0
- data/lib/noah.rb +6 -8
- data/lib/noah/app.rb +20 -268
- data/lib/noah/application_routes.rb +70 -0
- data/lib/noah/ark.rb +0 -0
- data/lib/noah/configuration_routes.rb +81 -0
- data/lib/noah/custom_watcher.rb +79 -0
- data/lib/noah/ephemeral_routes.rb +47 -0
- data/lib/noah/helpers.rb +37 -14
- data/lib/noah/host_routes.rb +69 -0
- data/lib/noah/models.rb +86 -5
- data/lib/noah/models/applications.rb +41 -0
- data/lib/noah/models/configurations.rb +49 -0
- data/lib/noah/models/ephemerals.rb +54 -0
- data/lib/noah/models/hosts.rb +56 -0
- data/lib/noah/models/services.rb +54 -0
- data/lib/noah/models/watchers.rb +62 -0
- data/lib/noah/passthrough.rb +11 -0
- data/lib/noah/service_routes.rb +71 -0
- data/lib/noah/validations.rb +1 -0
- data/lib/noah/validations/watcher_validations.rb +48 -0
- data/lib/noah/version.rb +1 -1
- data/lib/noah/watcher_routes.rb +45 -0
- data/noah.gemspec +25 -17
- data/spec/application_spec.rb +30 -30
- data/spec/configuration_spec.rb +78 -14
- data/spec/ephemeral_spec.rb +59 -0
- data/spec/host_spec.rb +21 -21
- data/spec/noahapp_application_spec.rb +6 -6
- data/spec/noahapp_configuration_spec.rb +5 -5
- data/spec/noahapp_ephemeral_spec.rb +115 -0
- data/spec/noahapp_host_spec.rb +3 -3
- data/spec/noahapp_service_spec.rb +10 -10
- data/spec/noahapp_watcher_spec.rb +123 -0
- data/spec/service_spec.rb +27 -27
- data/spec/spec_helper.rb +13 -22
- data/spec/support/db/.keep +0 -0
- data/spec/support/test-redis.conf +8 -0
- data/spec/watcher_spec.rb +62 -0
- data/views/index.haml +21 -15
- metadata +189 -146
- data/Gemfile.lock +0 -83
- data/doc/coverage/index.html +0 -138
- data/doc/coverage/jquery-1.3.2.min.js +0 -19
- data/doc/coverage/jquery.tablesorter.min.js +0 -15
- data/doc/coverage/lib-helpers_rb.html +0 -393
- data/doc/coverage/lib-models_rb.html +0 -1449
- data/doc/coverage/noah_rb.html +0 -2019
- data/doc/coverage/print.css +0 -12
- data/doc/coverage/rcov.js +0 -42
- data/doc/coverage/screen.css +0 -270
- data/lib/noah/applications.rb +0 -46
- data/lib/noah/configurations.rb +0 -49
- data/lib/noah/hosts.rb +0 -54
- data/lib/noah/services.rb +0 -57
- data/lib/noah/watchers.rb +0 -18
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'uri'
|
3
|
+
require 'logger'
|
4
|
+
begin
|
5
|
+
require 'em-hiredis'
|
6
|
+
rescue LoadError
|
7
|
+
puts "Please install: em-hiredis"
|
8
|
+
end
|
9
|
+
|
10
|
+
@log = Logger.new(STDOUT)
|
11
|
+
@log.level = Logger::DEBUG
|
12
|
+
|
13
|
+
require File.join(File.dirname(__FILE__), 'passthrough')
|
14
|
+
|
15
|
+
module Noah
|
16
|
+
|
17
|
+
class CustomWatcher
|
18
|
+
extend Passthrough
|
19
|
+
|
20
|
+
passthrough :redis_host, :pattern, :destination, :run!, :run_watcher
|
21
|
+
|
22
|
+
attr_accessor :my_pattern, :my_destination, :my_redis
|
23
|
+
|
24
|
+
def self.watch(&blk)
|
25
|
+
watcher = Noah::Watcher.new
|
26
|
+
watcher.instance_eval(&blk) if block_given?
|
27
|
+
watcher
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@my_redis ||= ENV['REDIS_URL']
|
32
|
+
@my_pattern ||= '//noah'
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.redis_host(host)
|
36
|
+
@my_redis = host
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.pattern(pattern)
|
40
|
+
@my_pattern = pattern
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.destination(destination)
|
44
|
+
@my_destination = destination
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.run!
|
48
|
+
@my_destination.nil? ? (raise ArgumentError) : run_watcher(@my_destination)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def self.run_watcher(dest)
|
53
|
+
log = Logger.new(STDOUT)
|
54
|
+
log.level = Logger::INFO
|
55
|
+
log.debug "#{dest.inspect}"
|
56
|
+
redis_url = URI.parse(@my_redis)
|
57
|
+
db = redis_url.path.gsub(/\//,'')
|
58
|
+
|
59
|
+
EventMachine.run do
|
60
|
+
trap("TERM") { log.info "Killed"; EventMachine.stop }
|
61
|
+
trap("INT") { log.info "Interrupted"; EventMachine.stop }
|
62
|
+
channel = EventMachine::Channel.new
|
63
|
+
r = EventMachine::Hiredis::Client.connect(redis_url.host, redis_url.port)
|
64
|
+
# Pulling out dbnum for now. Need to rethink it
|
65
|
+
#log.info "Binding to pattern #{db}:#{@my_pattern}"
|
66
|
+
log.info "Binding to pattern #{@my_pattern}"
|
67
|
+
r.psubscribe("#{@my_pattern}*")
|
68
|
+
r.on(:pmessage) do |pattern, event, message|
|
69
|
+
log.debug "Got message"
|
70
|
+
channel.push "#{message}"
|
71
|
+
end
|
72
|
+
r.errback { log.info "Something went tango-uniform" }
|
73
|
+
|
74
|
+
sub = channel.subscribe {|msg| log.info "Calling message handler"; dest.call(msg)}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Noah::App
|
2
|
+
get '/e/?' do
|
3
|
+
halt 404
|
4
|
+
end
|
5
|
+
|
6
|
+
get '/e/*' do
|
7
|
+
params["splat"].size == 0 ? (halt 404) : (e=Noah::Ephemeral.find(:path => "/#{params["splat"][0]}").first)
|
8
|
+
(halt 404) if e.nil?
|
9
|
+
content_type "application/octet-stream"
|
10
|
+
e.data.nil? ? "" : "#{e.data}"
|
11
|
+
end
|
12
|
+
|
13
|
+
put '/e/*/watch' do
|
14
|
+
required_params = ["endpoint"]
|
15
|
+
data = JSON.parse(request.body.read)
|
16
|
+
(data.keys.sort == required_params.sort) ? (e = Noah::Watcher.find(:path => params[:splat][0]).first) : (raise "Missing Parameters")
|
17
|
+
e.nil? ? (halt 404) : (w = e.watch!(:endpoint => data["endpoint"]))
|
18
|
+
w.to_json
|
19
|
+
end
|
20
|
+
|
21
|
+
put '/e/*' do
|
22
|
+
raise("Data too large") if request.body.size > 512
|
23
|
+
d = request.body.read || nil
|
24
|
+
opts = {:path => "/#{params[:splat][0]}", :data => d}
|
25
|
+
e = Noah::Ephemeral.find_or_create(opts)
|
26
|
+
if e.valid?
|
27
|
+
action = e.is_new? ? "create" : "update"
|
28
|
+
r = {"action" => action, "result" => "success", "id" => e.id, "path" => e.path, "data" => e.data}
|
29
|
+
r.to_json
|
30
|
+
else
|
31
|
+
raise "#{format_errors(e)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
delete '/e/*' do
|
36
|
+
p = params[:splat][0]
|
37
|
+
e = Noah::Ephemeral.find(:path => "/"+p).first
|
38
|
+
if e
|
39
|
+
e.delete
|
40
|
+
r = {"result" => "success", "id" => "#{e.id}", "action" => "delete", "path" => e.name}
|
41
|
+
r.to_json
|
42
|
+
else
|
43
|
+
halt 404
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/noah/helpers.rb
CHANGED
@@ -1,56 +1,79 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'models')
|
2
1
|
module Noah
|
3
2
|
module SinatraHelpers
|
4
|
-
|
3
|
+
|
4
|
+
def format_errors(model)
|
5
|
+
error_messages = model.errors.present do |e|
|
6
|
+
# Missing attributes
|
7
|
+
e.on [:name, :not_present], "Name attribute missing"
|
8
|
+
e.on [:status, :not_present], "Status attribute missing"
|
9
|
+
e.on [:format, :not_present], "Format attribute missing"
|
10
|
+
e.on [:body, :not_present], "Body attribute missing"
|
11
|
+
e.on [:application_id, :not_present], "Application attribute missing"
|
12
|
+
e.on [:path, :not_present], "Path attribute missing"
|
13
|
+
e.on [:pattern, :not_present], "Pattern attribute missing"
|
14
|
+
e.on [:endpoint, :not_present], "Endpoint attribute missing"
|
15
|
+
# Invalid option
|
16
|
+
e.on [:status, :not_member], "Status must be up, down or pending"
|
17
|
+
# Duplicate keys
|
18
|
+
e.on [[:name, :application_id], :not_unique], "Record already exists"
|
19
|
+
e.on [[:name, :host_id], :not_unique], "Record already exists"
|
20
|
+
e.on [[:endpoint, :pattern], :not_unique], "Record already exists"
|
21
|
+
e.on [:path, :not_unique], "Record already exists"
|
22
|
+
# Custom exceptions
|
23
|
+
e.on [:pattern, :already_provided], "Pattern is already provided"
|
24
|
+
e.on [:pattern, :replaces_existing], "Pattern would overwrite existing"
|
25
|
+
end
|
26
|
+
error_messages.first
|
27
|
+
end
|
5
28
|
|
6
29
|
def host(opts = {})
|
7
|
-
|
30
|
+
Noah::Host.find(opts).first
|
8
31
|
end
|
9
32
|
|
10
33
|
def hosts(opts = {})
|
11
|
-
Hosts.all(opts)
|
34
|
+
Noah::Hosts.all(opts)
|
12
35
|
end
|
13
36
|
|
14
37
|
def service(opts = {})
|
15
|
-
Service.find(options)
|
38
|
+
Noah::Service.find(options)
|
16
39
|
end
|
17
40
|
|
18
41
|
def services(opts = {})
|
19
|
-
Services.all(opts)
|
42
|
+
Noah::Services.all(opts)
|
20
43
|
end
|
21
44
|
|
22
45
|
def host_service(hostname, servicename)
|
23
|
-
h = Host.find(:name => hostname).first
|
46
|
+
h = Noah::Host.find(:name => hostname).first
|
24
47
|
if h.nil?
|
25
48
|
nil
|
26
49
|
else
|
27
|
-
Service.find(:host_id => h.id, :name => servicename).first
|
50
|
+
Noah::Service.find(:host_id => h.id, :name => servicename).first
|
28
51
|
end
|
29
52
|
end
|
30
53
|
|
31
54
|
def host_services(hostname)
|
32
|
-
h = Host.find(:name => hostname).first
|
55
|
+
h = Noah::Host.find(:name => hostname).first
|
33
56
|
if h.nil?
|
34
57
|
nil
|
35
58
|
else
|
36
|
-
Services.all(:host_id => id)
|
59
|
+
Noah::Services.all(:host_id => id)
|
37
60
|
end
|
38
61
|
end
|
39
62
|
|
40
63
|
def application(opts = {})
|
41
|
-
Application.find(opts).first
|
64
|
+
Noah::Application.find(opts).first
|
42
65
|
end
|
43
66
|
|
44
67
|
def applications(opts = {})
|
45
|
-
Applications.all(opts)
|
68
|
+
Noah::Applications.all(opts)
|
46
69
|
end
|
47
70
|
|
48
71
|
def configuration(opts = {})
|
49
|
-
Configuration.find(opts).first
|
72
|
+
Noah::Configuration.find(opts).first
|
50
73
|
end
|
51
74
|
|
52
75
|
def configurations(opts = {})
|
53
|
-
Configurations.all(opts)
|
76
|
+
Noah::Configurations.all(opts)
|
54
77
|
end
|
55
78
|
end
|
56
79
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Noah::App
|
2
|
+
# Host URIs
|
3
|
+
|
4
|
+
# GET named {Service} for named {Host}
|
5
|
+
get '/h/:hostname/:servicename/?' do |hostname, servicename|
|
6
|
+
h = host_service(hostname, servicename)
|
7
|
+
if h.nil?
|
8
|
+
halt 404
|
9
|
+
else
|
10
|
+
h.to_json
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# GET named {Host}
|
15
|
+
# @param :hostname name of {Host}
|
16
|
+
# @return [JSON] representation of {Host}
|
17
|
+
get '/h/:hostname/?' do |hostname|
|
18
|
+
h = host(:name => hostname)
|
19
|
+
if h.nil?
|
20
|
+
halt 404
|
21
|
+
else
|
22
|
+
h.to_json
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# GET all {Hosts}
|
27
|
+
get '/h/?' do
|
28
|
+
hosts.map {|h| h.to_hash}
|
29
|
+
if hosts.size == 0
|
30
|
+
halt 404
|
31
|
+
else
|
32
|
+
hosts.to_json
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
put '/h/:hostname/watch' do |hostname|
|
37
|
+
required_params = ["endpoint"]
|
38
|
+
data = JSON.parse(request.body.read)
|
39
|
+
(data.keys.sort == required_params.sort) ? (h = Noah::Host.find(:name => hostname).first) : (raise "Missing Parameters")
|
40
|
+
h.nil? ? (halt 404) : (w = h.watch!(:endpoint => data['endpoint']))
|
41
|
+
w.to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
put '/h/:hostname/?' do |hostname|
|
45
|
+
required_params = ["name", "status"]
|
46
|
+
data = JSON.parse(request.body.read)
|
47
|
+
(data.keys.sort == required_params.sort && data['name'] == hostname) ? (host = Noah::Host.find_or_create(:name => data['name'], :status => data['status'])) : (raise "Missing Parameters")
|
48
|
+
if host.valid?
|
49
|
+
r = {"result" => "success","id" => "#{host.id}","status" => "#{host.status}", "name" => "#{host.name}", "new_record" => host.is_new?}
|
50
|
+
r.to_json
|
51
|
+
else
|
52
|
+
raise "#{format_errors(host)}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
delete '/h/:hostname/?' do |hostname|
|
57
|
+
host = Noah::Host.find(:name => hostname).first
|
58
|
+
if host
|
59
|
+
services = []
|
60
|
+
Noah::Service.find(:host_id => host.id).sort.each {|x| services << x; x.delete} if host.services.size > 0
|
61
|
+
host.delete
|
62
|
+
r = {"result" => "success", "id" => "#{host.id}", "name" => "#{hostname}", "service_count" => "#{services.size}"}
|
63
|
+
r.to_json
|
64
|
+
else
|
65
|
+
halt 404
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/noah/models.rb
CHANGED
@@ -1,5 +1,86 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require 'ohm'
|
2
|
+
require 'ohm/contrib'
|
3
|
+
module Noah
|
4
|
+
class Model < Ohm::Model
|
5
|
+
def self.inherited(model)
|
6
|
+
|
7
|
+
model.send :include, Ohm::Timestamping
|
8
|
+
model.send :include, Ohm::Typecast
|
9
|
+
model.send :include, Ohm::Callbacks
|
10
|
+
model.send :include, Ohm::ExtraValidations
|
11
|
+
|
12
|
+
# removing this as it's simply redundant
|
13
|
+
# model.after :save, :notify_via_redis_save
|
14
|
+
model.after :create, :notify_via_redis_create
|
15
|
+
model.after :update, :notify_via_redis_update
|
16
|
+
model.before :delete, :stash_name
|
17
|
+
model.after :delete, :notify_via_redis_delete
|
18
|
+
model.send :include, ModelClassMethods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ModelClassMethods
|
23
|
+
|
24
|
+
def is_new?
|
25
|
+
self.created_at == self.updated_at
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def watch!(opts={:endpoint => nil, :pattern => nil})
|
30
|
+
base_pattern = "#{self.patternize_me}"
|
31
|
+
opts[:endpoint].nil? ? (raise ArgumentError, "Need an endpoint") : endpoint=opts[:endpoint]
|
32
|
+
opts[:pattern].nil? ? pattern=base_pattern : pattern=opts[:pattern]
|
33
|
+
|
34
|
+
begin
|
35
|
+
w = Watcher.new :pattern => pattern, :endpoint => endpoint
|
36
|
+
w.valid? ? w.save : (raise "#{w.errors}")
|
37
|
+
w.name
|
38
|
+
rescue Exception => e
|
39
|
+
e.message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def patternize_me
|
45
|
+
"//noah/#{self.class_to_lower}/#{name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def stash_name
|
49
|
+
@deleted_name = self.name
|
50
|
+
end
|
51
|
+
|
52
|
+
def class_to_lower
|
53
|
+
self.class.to_s.gsub(/(.*)::(\w)/,'\2').downcase
|
54
|
+
end
|
55
|
+
def dbnum
|
56
|
+
o = Ohm.options.first
|
57
|
+
return "0" if o.nil?
|
58
|
+
return "0" if (o[:db].nil? && o[:url].nil?)
|
59
|
+
o[:db].nil? ? "#{o[:url].split('/').last}" : "#{o[:db]}"
|
60
|
+
end
|
61
|
+
|
62
|
+
["create", "update", "delete"].each do |meth|
|
63
|
+
class_eval do
|
64
|
+
define_method("notify_via_redis_#{meth}".to_sym) do
|
65
|
+
db = self.dbnum
|
66
|
+
self.name.nil? ? name=@deleted_name : name=self.name
|
67
|
+
# Pulling out dbnum for now. Need to rethink it
|
68
|
+
#pub_category = "#{db}:noah.#{self.class.to_s}[#{name}].#{meth}"
|
69
|
+
pub_category = "#{self.patternize_me}"
|
70
|
+
Ohm.redis.publish(pub_category, self.to_hash.merge({"action" => meth, "pubcategory" => pub_category}).to_json)
|
71
|
+
|
72
|
+
# The following provides a post post-action hook. It allows a class to provide it's own handling after the fact
|
73
|
+
# good example is in [Noah::Ephemeral] where it's used to check for/clean up expired ephemeral nodes entries
|
74
|
+
self.send("#{meth}_hook".to_sym) unless self.protected_methods.member?("#{meth}_hook".to_sym) == false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
require File.join(File.dirname(__FILE__), 'models','hosts')
|
82
|
+
require File.join(File.dirname(__FILE__), 'models','services')
|
83
|
+
require File.join(File.dirname(__FILE__), 'models','applications')
|
84
|
+
require File.join(File.dirname(__FILE__), 'models','configurations')
|
85
|
+
require File.join(File.dirname(__FILE__), 'models','watchers')
|
86
|
+
require File.join(File.dirname(__FILE__), 'models','ephemerals')
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'configurations')
|
2
|
+
module Noah
|
3
|
+
class Application < Model
|
4
|
+
attribute :name
|
5
|
+
collection :configurations, Configuration
|
6
|
+
|
7
|
+
index :name
|
8
|
+
|
9
|
+
def validate
|
10
|
+
super
|
11
|
+
assert_present :name
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_hash
|
15
|
+
arr = []
|
16
|
+
configurations.sort.each {|c| arr << c.to_hash}
|
17
|
+
super.merge(:name => name, :created_at => created_at, :updated_at => updated_at, :configurations => arr)
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def find_or_create(opts = {})
|
22
|
+
begin
|
23
|
+
find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)
|
24
|
+
if app.valid?
|
25
|
+
app.save
|
26
|
+
end
|
27
|
+
app
|
28
|
+
rescue Exception => e
|
29
|
+
e.message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class Applications
|
37
|
+
def self.all(options = {})
|
38
|
+
options.empty? ? Application.all.sort : Application.find(options).sort
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Noah
|
2
|
+
class Configuration < Model
|
3
|
+
|
4
|
+
attribute :name
|
5
|
+
attribute :format
|
6
|
+
attribute :body
|
7
|
+
attribute :new_record
|
8
|
+
reference :application, Application
|
9
|
+
|
10
|
+
index :name
|
11
|
+
index :format
|
12
|
+
index :body
|
13
|
+
|
14
|
+
def validate
|
15
|
+
super
|
16
|
+
assert_present :name
|
17
|
+
assert_present :format
|
18
|
+
assert_present :body
|
19
|
+
assert_present :application_id
|
20
|
+
assert_unique [:name, :application_id]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
Application[application_id].nil? ? app_name=nil : app_name=Application[application_id].name
|
25
|
+
super.merge(:name => name, :format => format, :body => body, :created_at => created_at, :updated_at => updated_at, :application => app_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def find_or_create(opts={})
|
30
|
+
begin
|
31
|
+
if find(opts).first.nil?
|
32
|
+
conf = create(opts)
|
33
|
+
else
|
34
|
+
conf = find(opts).first
|
35
|
+
end
|
36
|
+
rescue Exception => e
|
37
|
+
e.message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Configurations
|
45
|
+
def self.all(options = {})
|
46
|
+
options.empty? ? Configuration.all.sort : Configuration.find(options).sort
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|