Commit 74683c9c authored by John E. Vincent's avatar John E. Vincent

merging in testing

parents ae0e610d c9a725d2
require 'autotest/growl'
Autotest.add_hook(:initialize) {|at|
at.add_exception %r{^\.git} # ignore Version Control System
at.add_exception %r{^./tmp} # ignore temp files, lest autotest will run again, and again...
at.clear_mappings # take out the default (test/test*rb)
at.add_mapping(%r{^noah\.rb$}) {|f, _|
Dir['spec/**/*.rb']
}
at.add_mapping(%r{^lib/.*\.rb$}) {|f, _|
Dir['spec/**/*.rb']
}
nil
}
.rvmrc
*.swp
coverage.data
.idea
.gem
pkg/*
logs/*
PATH
remote: .
specs:
noah (0.0.5)
haml (= 3.0.25)
ohm (= 0.1.3)
ohm-contrib (= 0.1.0)
rake (= 0.8.7)
sinatra (= 1.1.2)
sinatra-namespace (= 0.6.1)
thin (= 1.2.7)
vegas (= 0.1.8)
yajl-ruby (= 0.7.9)
GEM
remote: http://rubygems.org/
specs:
ZenTest (4.4.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
autotest-growl (0.2.9)
backports (1.18.2)
daemons (1.1.0)
diff-lcs (1.1.2)
eventmachine (0.12.10)
haml (3.0.25)
monkey-lib (0.5.4)
backports
nest (1.1.0)
redis (~> 2.1)
ohm (0.1.3)
nest (~> 1.0)
ohm-contrib (0.1.0)
ohm
rack (1.2.1)
rack-test (0.5.7)
rack (>= 1.0)
rake (0.8.7)
rcov (0.9.9)
redis (2.1.1)
rspec (2.4.0)
rspec-core (~> 2.4.0)
rspec-expectations (~> 2.4.0)
rspec-mocks (~> 2.4.0)
rspec-core (2.4.0)
rspec-expectations (2.4.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.4.0)
sinatra (1.1.2)
rack (~> 1.1)
tilt (~> 1.2)
sinatra-advanced-routes (0.5.1)
monkey-lib (~> 0.5.0)
sinatra (~> 1.0)
sinatra-sugar (~> 0.5.0)
sinatra-namespace (0.6.1)
sinatra (~> 1.1)
sinatra-reloader (0.5.0)
sinatra (~> 1.0)
sinatra-advanced-routes (~> 0.5.0)
sinatra-sugar (0.5.0)
monkey-lib (~> 0.5.0)
sinatra (~> 1.0)
thin (1.2.7)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
tilt (1.2.2)
vegas (0.1.8)
rack (>= 1.0.0)
yajl-ruby (0.7.9)
PLATFORMS
java
ruby
DEPENDENCIES
ZenTest (= 4.4.2)
autotest (= 4.4.6)
autotest-growl (= 0.2.9)
noah!
rack-test (= 0.5.7)
rcov (= 0.9.9)
rspec (= 2.4.0)
sinatra-reloader (= 0.5.0)
_(looking for the real code? See the section "Where's the code?")_
# Noah testing quickstart
(make sure redis is running)
## Setup
There is no specific configuration file in use anymore. Everything is configured via command-line options to the noah binary or stored in config.ru.
* The instance of Redis is configured via the `REDIS_URL` env setting. This is honored by Ohm.
* Redis DSN strings are in the format of "redis://hostname:port/db"
* `RACK_ENV` is honored.
* `rake sample["redis://localhost:6379/2"]` populates the locally running redis instance - db 2
* `rake spec` runs the test suite (using 'redis://localhost:6379/3' for storing test data)
_rake sample_
Creating Host entry for 'localhost'
Create Service entry for redis
Create Service entry for noah
Creating Application entry for 'noah'
Creating Configuration entry for 'noah'
Creating sample entries - Host and Service
Creating sample entries - Application and Configuration
Setup successful!
## Run it
There are two way to run Noah
### config.ru
Edit config.ru to change the redis instance or rack environment.
### bin/noah
The binary script in bin was created using [Vegas](https://github.com/quirkey/vegas). It accepts the familiar rack options as well as an option for specifying the redis url.
bin/noah -p 9292 -s thin -d -F -e production -r redis://localhost:6379/2
[2011-02-07 16:48:15 -0500] Starting 'noah'...
[2011-02-07 16:48:15 -0500] trying port 9292...
Couldn't get a file descriptor referring to the console
[2011-02-07 16:48:15 -0500] Running with Rack handler: Rack::Handler::Thin
>> Thin web server (v1.2.7 codename No Hup)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:9292, CTRL+C to stop
If you leave off `-F`, all information will be logged to `$HOME/.vegas/noah`. Run `bin/noah -h` for more options.
Please note on JRuby that the port setting does not work for some f'cking reason. Bug claims to have been fixed. Until then, when on Jruby run like so:
noah -F -d -r redis://localhost:6379/0
## Example links
[Noah Start Page](http://localhost:9292/)
If you have Noah running, you can hit the above link for some links created by the setup samples.
## All configs
_curl http://localhost:9292/c/_
[
{"id":"1",
"name":"db",
"format":"string",
"body":"redis://127.0.0.1:6379/0",
"update_at":"2011-01-17 14:12:43 UTC",
"application":"noah"
}
]
## All services
_curl http://localhost:9292/s/_
[
{
"id":"1",
"name":"redis",
"status":"up",
"updated_at":"2011-01-17 14:12:43 UTC",
"host":"localhost"
},
{
"id":"2",
"name":"noah",
"status":"up",
"updated_at":"2011-01-17 14:12:43 UTC",
"host":"localhost"
}
]
## All hosts
_curl http://localhost:9292/h/_
[
{
"id":"1",
"name":"localhost",
"status":"up",
"updated_at":"2011-01-17 14:12:43 UTC",
"services":[
{
"id":"1",
"name":"redis",
"status":"up",
"updated_at":"2011-01-17 14:12:43 UTC",
"host":"localhost"
},
{
"id":"2",
"name":"noah",
"status":"up",
"updated_at":"2011-01-17 14:12:43 UTC",
"host":"localhost"
}
]
}
]
## All applications
_curl http://localhost:9292/a/_
[
{
"id":"1",
"name":"noah",
"updated_at":"2011-01-17 14:12:43 UTC"
}
]
Most other combinations of endpoints work as well:
* `http://localhost:9292/h/<hostname>/<servicename>` - `<servicename>` on `<hostname>`
* `http://localhost:9292/a/<appname>/<configname>` - Configuration for `<appname>`
* `http://localhost:9292/c/<appname>/<element>` - Specific configuration element for `<appname>`
{
"id":"1",
"name":"db",
"format":"string",
"body":"redis://127.0.0.1:6379/0",
"update_at":"2011-01-17 14:12:43 UTC",
"application":"noah"
}
# Adding new entries
There are two ways to add new objects: via irb and via the [API](https://github.com/lusis/Noah/wiki/Stabilize-API) on a running instance. The API is still in a state of flux.
## Adding a new application and configuration item
irb -rohm -rohm/contrib -r./lib/noah/models.rb
a1 = Application.create(:name => 'myapplication')
if a1.save
a1.configurations << Configuration.create(:name => 'jsonconfigobj', :format => 'json', :body => '{"configvar1":"foo","configvar2":"bar"}', :application => a1)
end
JSON.parse(Configuration[2].body)
{"configvar1"=>"foo", "configvar2"=>"bar"}
## database.yml inside Noah? Sure!
dbyaml = <<EOY
development:
adapter: mysql
database: rails_development
username: root
password: my super secret password
EOY
a2 = Application.create(:name => 'myrailsapp')
if a2.save
a2.configurations << Configuration.create(:name => 'database.yml', :format => 'yaml', :body => dbyaml, :application => a2)
end
puts YAML.dump(Configuration[3].body)
development:
adapter: mysql
database: rails_development
username: root
password: my super secret password
# Hosts and Services/Applications and Configurations
Host/Services and Applications/Configurations are almost the same thing with a few exceptions. Here are some basic facts:
* Hosts have many Services
* Applications have many Configurations
* Hosts and Services have a status - `up`,`down` or `pending`
The intention of the `status` field for Hosts and Services is that a service might, when starting up, set the appropriate status. Same goes for said service shutting down. This also applies to hosts (i.e. a curl PUT is sent to Noah during the boot process).
While an application might have different "configurations" based on environment (production, qa, dev), the Configuration object in Noah is intended to be more holistic i.e. these are the Configuration atoms (a yaml file, property X, property Y) that form the running configuration of an Application.
Here's a holistic example using a tomcat application:
* Host running tomcat comes up. It sets its status as "pending"
* Each service on the box starts up and sets its status to "pending" and finally "up" (think steps in the init script for the service)
* Tomcat (now in the role of `Application`) given a single property in a properties file called "bootstrap.url", grabs a list of `Configuration`atoms it needs to run. Let's say, by default, Tomcat starts up with all webapps disabled. Using the `Configuration` item `webapps`, it knows which ones to start up.
* Each webapp (an application under a different context root) now has the role of `Application` and the role of `Service`. As an application, the webapp would grab things that would normally be stored in a .properties file. Maybe even the log4j.xml file. In the role of `Service`, a given webapp might be an API endpoint and so it would have a hostname (a virtual host maybe?) and services associated with it. Each of those, has a `status`.
That might be confusing and it's a fairly overly-contrived example. A more comon use case would be the above where, instead of storing the database.yml on the server, the Rails application actually reads the file from Noah. Now that might not be too exciting but try this example:
* Rails application with memcached as part of the stack.
* Instead of a local configuration file, the list of memcached servers is a `Configuration` object belonging to the rails application's `Application` object.
* As new memcached servers are brought online, your CM tool (puppet or chef) updates Noah
* Your Rails application either via restarting (and thus rebootstrapping the list of memcached servers from Noah) or using the Watcher subsystem is instantly aware of those servers. You could fairly easily implement a custom Watcher that, when the list of memcached server changes, the Passenger restart file is written.
Make sense?
# Constraints
You can view all the constraints inside `models.rb` but here they are for now:
* A new host must have at least `name` and `status` set.
* A new service must have at least `name` and `status` set.
* Each Host `name` must be unique
* Each Service `name` per Host must be unique
* Each Application `name` must exist and be unique
* Each Configuration name per Application must be unique.
* Each Configuration must have `name`,`format` and `body`
# Noah
Noah is a lightweight registry based on the concepts in the Apache Zookeeper project.
## Impetus
Apache Zookeeper is an amazing peice of software. From the project's own description:
> ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications.
The problem is that zookeeper is *BIG*. It has quite a bit of overhead associated with it and it's not easily accessible from anything other than C or Java. You could argue that you need to be big when this service is the crux of your infrastructure but that's confusing big with highly available and highly scalable.
_(yes I'm aware that you can run Zookeeper in single server mode from a single JAR file)_
## So why reinvent the wheel?
It's something I've wanted to do for a while. Everytime I've needed something like Zookeeper, Zookeeper has always been too bulky and had too many moving parts. I've also always needed to interact with it from more than just Java or C. Sometimes it's been Ruby and sometimes it's been Python.
In the end, we reinvent the wheel ANYWAY. Maybe we do something like have our CM tool write our application config files with a list of memcached hosts. Maybe we write our own logic around (shudder) RMI to do some chatty broadcasting around the network for finding local nodes of type X. We always reinvent the wheel in some way.
## Design Goals
I have a few basic design goals:
* The system must support RESTful interaction for operations where REST maps properly
* The system must support basic concepts of hosts, services and configuration parameters
* The system must support horizontal scaling
* The system must support traditional load balancing methodologies
* The system must support ephemeral clients and stateless operation where applicable
In essence, the system must be resemble your typical web application.
Here are some "would really like to have" things:
* The system should support watches similar to ZooKeeper
* The system should support pluggable callback methods (more below)
* The system should support being a client of itself
## Opinionated stack
I've found that being opinionated has its benefits. Swappable storage backends are nice but those have to evolve naturally over time. To this end I've tried to keep the stack as lightweight and easy to distribute as possible. I'm also a big fan of the Unix philosophy of doing one thing and doing it very well. To this end, I've chosen the followng initial stack:
* Ruby
* EventMachine/Sinatra/Ohm
* Redis
The main reason for choosing Redis is that much of the functionality I need/that I want to implement from ZooKeeper is already available in Redis semantics. Essentially Noah becomes a custom API for Redis - a way to provide disconnected client operations for it. By using Ohm, I get an easy and unobtrusive orm that only does what I tell it to do. Sinatra was originally going to be the only inteface but as I thought about how best to handle the Watch scenario and callbacks, I realized I needed something a bit more evented hence EventMachine.
## Pretty pictures
This is the original Mindmap I did when thinking about Noah. It's not complete and was just a dump of what was in my head at the time:
[Noah MindMap](https://github.com/lusis/Noah/raw/master/doc/noah-mindmap-original.png)
## Where's the code?
I'm still working on the codebase in a private local branch. Sorry. I'm going to be flailing around a bit trying a few things and don't want people to fork yet. I'll still commit regularly to that branch so you'll see said flailing in the history when I merge. What I've done is create a simple sinatra app that you can get a feel for where I'm headed with this. It's under the branch called [testing](https://github.com/lusis/Noah/tree/testing).
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'rake'
require 'rspec/core'
require 'rspec/core/rake_task'
Bundler::GemHelper.install_tasks
desc "Populate database with sample dataset"
task :sample, :redis_url do |t, args|
require 'ohm'
begin
require 'yajl'
rescue LoadError
require 'json'
end
require File.join(File.dirname(__FILE__), 'lib','noah')
Ohm::connect(:url => args.redis_url)
Ohm::redis.flushdb
puts "Creating Host entry for 'localhost'"
h = Host.create(:name => 'localhost', :status => "up")
if h.save
%w[redis noah].each do |service|
puts "Create Service entry for #{service}"
s = Service.create(:name => service, :status => "up", :host => h)
h.services << s
end
end
puts "Creating Application entry for 'noah'"
a = Application.create(:name => 'noah')
if a.save
puts "Creating Configuration entry for 'noah'"
cr = Configuration.create(:name => 'redis', :format => 'string', :body => 'redis://127.0.0.1:6379/0', :application => a)
ch = Configuration.create(:name => 'host', :format => 'string', :body => 'localhost', :application => a)
cp = Configuration.create(:name => 'port', :format => 'string', :body => '9292', :application => a)
%w[cr ch cp].each do |c|
a.configurations << eval(c)
end
end
puts "Creating sample entries - Host and Service"
%w[host1.domain.com host2.domain.com host3.domain.com].each do |host|
h = Host.create(:name => host, :status => "up")
if h.save
%w[http https smtp mysql].each do |service|
s = Service.create(:name => service, :status => "pending", :host => h)
h.services << s
end
end
end
puts "Creating sample entries - Application and Configuration"
my_yaml = <<EOY
development:
database: development_database
adapter: mysql
username: dev_user
password: dev_password
EOY
my_json = <<EOJ
{
"id":"hostname",
"data":"localhost"
}
EOJ
a1 = Application.create(:name => 'myrailsapp1')
if a1.save
c1 = Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
a1.configurations << c1
end
a2 = Application.create(:name => 'myrestapp1')
if a2.save
c2 = Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
a2.configurations << c2
end
puts "Sample data populated!"
end
RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb']
end
namespace :coverage do
desc "Delete aggregate coverage data."
task(:clean) { rm_f "coverage.data" }
end
desc "Run Rcov code coverage analysis"
RSpec::Core::RakeTask.new(:coverage) do |t|
t.rcov = true
t.verbose = true
t.rcov_opts = %q[--aggregate coverage.data --sort coverage --text-report --exclude "config,.bundle/*,gems/*,spec/*" -o doc/coverage -Ilib -i "noah.rb"]
end
Autotest.add_discovery { "rspec2" }
#!/usr/bin/env ruby
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require 'noah'
require 'vegas'
Vegas::Runner.new(Noah::App, 'noah') do |runner, opts, app|
opts.on("-r", "--redis URL", "redis url to connect to (default: redis://localhost:6379/0)") {|r| ENV["REDIS_URL"] = r }
end
require File.join(File.dirname(__FILE__), 'lib','noah')
ENV['REDIS_URL'] = "redis://localhost:6379/0"
run Noah::App
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>Noah C0 Coverage Information - RCov</title>
<link href="screen.css" media="all" rel="stylesheet" type="text/css" />
<link href="print.css" media="print" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="rcov.js"></script>
</head>
<body>
<h1>Noah C0 Coverage Information - RCov</h1>
<noscript><style type="text/css">.if_js { display:none; }</style></noscript>
<div class="filters if_js">
<fieldset>
<label>File Filter:</label>
<select id="file_filter" class="filter">
<option value="all_files">Show All</option>
<option value="lib">lib/</option>
</select>
</fieldset>
<fieldset>
<label>Code Coverage Threshold:</label>
<select id="coverage_filter" class="filter">
<option value="all_coverage">Show All</option>
<option value="10">&lt; 10% Coverage</option><option value="20">&lt; 20% Coverage</option><option value="30">&lt; 30% Coverage</option><option value="40">&lt; 40% Coverage</option><option value="50">&lt; 50% Coverage</option><option value="60">&lt; 60% Coverage</option><option value="70">&lt; 70% Coverage</option><option value="80">&lt; 80% Coverage</option><option value="90">&lt; 90% Coverage</option><option value="100">&lt; 100% Coverage</option>
<option value="110">= 100% Coverage</option>
</select>
</fieldset>
</div>
<div class="report_table_wrapper">
<table class='report' id='report_table'>
<thead>
<tr>
<th class="left_align">Name</th>
<th class="right_align">Total Lines</th>
<th class="right_align">Lines of Code</th>
<th class="left_align">Total Coverage</th>
<th class="left_align">Code Coverage</th>
</tr>
</thead>
<tfoot>
<tr>
<td class="left_align">TOTAL</td>
<td class='right_align'><tt>612</tt></td>
<td class='right_align'><tt>521</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>83.33%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:83px"></div>
<div class="uncovered" style="width:17px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class='coverage_total'>81.96%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:82px"></div>
<div class="uncovered" style="width:18px"></div>
</div></td>
</tr>
</tfoot>
<tbody>
<tr class="all_files all_coverage 60 70 80 90 100 lib even">
<td class="left_align"><a href="lib-helpers_rb.html">lib/helpers.rb</a></td>
<td class='right_align'><tt>55</tt></td>
<td class='right_align'><tt>45</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>56.36%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:56px"></div>
<div class="uncovered" style="width:44px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>57.78%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:58px"></div>
<div class="uncovered" style="width:42px"></div>
</div></td>
</tr>
<tr class="all_files all_coverage 90 100 odd">
<td class="left_align"><a href="noah_rb.html">noah.rb</a></td>
<td class='right_align'><tt>326</tt></td>
<td class='right_align'><tt>285</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>82.52%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:83px"></div>
<div class="uncovered" style="width:17px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>81.05%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:81px"></div>
<div class="uncovered" style="width:19px"></div>
</div></td>
</tr>
<tr class="all_files all_coverage 90 100 lib even">
<td class="left_align"><a href="lib-models_rb.html">lib/models.rb</a></td>
<td class='right_align'><tt>231</tt></td>
<td class='right_align'><tt>191</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>90.91%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:91px"></div>
<div class="uncovered" style="width:9px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>89.01%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:89px"></div>
<div class="uncovered" style="width:11px"></div>
</div></td>
</tr>
</tbody>
</table>
</div>
<p>Generated on 2011-01-31 05:03:32 -0500 with <a href="http://github.com/relevance/rcov">rcov 0.9.8</a></p>
<script type="text/javascript">
$(document).ready(function(){$("#report_table").tablesorter({widgets: ['zebra'], textExtraction: 'complex'});});
$('.filter').change(function(){
ff = $('#file_filter').val();
cf = $('#coverage_filter').val();
$('table#report_table tbody tr').each(function(i){
if ((this.className.split(" ").indexOf(ff) > -1) && (this.className.split(" ").indexOf(cf) > -1)) {
this.style.display = "";
} else {
this.style.display = "none";
};
restripe();
})
})
</script>
</body>
</html>
/*
* jQuery JavaScript Library v1.3.2
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
* Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
* Revision: 6246
*/
(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
/*
* Sizzle CSS Selector Engine - v0.9.3
* Copyright 2009, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
/*
*
* TableSorter 2.0 - Client-side table sorting with ease!
* Version 2.0.3
* @requires jQuery v1.2.3
*
* Copyright (c) 2007 Christian Bach
* Examples and docs at: http://tablesorter.com
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
\ No newline at end of file
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>lib/helpers.rb</title>
<link href="screen.css" media="all" rel="stylesheet" type="text/css" />
<link href="print.css" media="print" rel="stylesheet" type="text/css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<script type="text/javascript" src="rcov.js"></script>
</head>
<body>
<h1>Noah C0 Coverage Information - RCov</h1>
<h2>lib/helpers.rb</h2>
<div class="report_table_wrapper">
<table class='report' id='report_table'>
<thead>
<tr>
<th class="left_align">Name</th>
<th class="right_align">Total Lines</th>
<th class="right_align">Lines of Code</th>
<th class="left_align">Total Coverage</th>
<th class="left_align">Code Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td class="left_align"><a href="lib-helpers_rb.html">lib/helpers.rb</a></td>
<td class='right_align'><tt>55</tt></td>
<td class='right_align'><tt>45</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>56.36%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:56px"></div>
<div class="uncovered" style="width:44px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>57.78%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:58px"></div>
<div class="uncovered" style="width:42px"></div>
</div></td>
</tr>
</tbody>
</table>
</div>
<h3>Key</h3>
<div class="key"><pre><span class='marked'>Code reported as executed by Ruby looks like this...</span><span class='marked1'>and this: this line is also marked as covered.</span><span class='inferred'>Lines considered as run by rcov, but not reported by Ruby, look like this,</span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics).</span><span class='uncovered'>Finally, here's a line marked as not executed.</span></pre></div>
<h3>Coverage Details</h3>
<table class="details">
<tbody>
<tr class="uncovered">
<td><pre><a name="line1">1</a> module Sinatra</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line2">2</a> module NoahHelpers</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line3">3</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line4">4</a> def host(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line5">5</a> Host.find(opts).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line6">6</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line7">7</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line8">8</a> def hosts(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line9">9</a> Hosts.all(opts)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line10">10</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line11">11</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line12">12</a> def service(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line13">13</a> Service.find(options)</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line14">14</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line15">15</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line16">16</a> def services(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line17">17</a> Services.all(opts)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line18">18</a> end </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line19">19</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line20">20</a> def host_service(hostname, servicename)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line21">21</a> h = Host.find(:name =&gt; hostname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line22">22</a> if h.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line23">23</a> nil</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line24">24</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line25">25</a> Service.find(:host_id =&gt; h.id, :name =&gt; servicename).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line26">26</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line27">27</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line28">28</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line29">29</a> def host_services(hostname)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line30">30</a> h = Host.find(:name =&gt; hostname).first</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line31">31</a> if h.nil?</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line32">32</a> nil</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line33">33</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line34">34</a> Services.all(:host_id =&gt; id)</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line35">35</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line36">36</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line37">37</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line38">38</a> def application(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line39">39</a> Application.find(opts).first</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line40">40</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line41">41</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line42">42</a> def applications(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line43">43</a> Applications.all(opts)</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line44">44</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line45">45</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line46">46</a> def configuration(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line47">47</a> Configuration.find(opts).first</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line48">48</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line49">49</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line50">50</a> def configurations(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line51">51</a> Configurations.all(opts)</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line52">52</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line53">53</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line54">54</a> helpers NoahHelpers</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line55">55</a> end </pre></td>
</tr>
</tbody>
</table>
<p>Generated on 2011-01-31 05:03:32 -0500 with <a href="http://github.com/relevance/rcov">rcov 0.9.8</a></p>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>lib/models.rb</title>
<link href="screen.css" media="all" rel="stylesheet" type="text/css" />
<link href="print.css" media="print" rel="stylesheet" type="text/css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<script type="text/javascript" src="rcov.js"></script>
</head>
<body>
<h1>Noah C0 Coverage Information - RCov</h1>
<h2>lib/models.rb</h2>
<div class="report_table_wrapper">
<table class='report' id='report_table'>
<thead>
<tr>
<th class="left_align">Name</th>
<th class="right_align">Total Lines</th>
<th class="right_align">Lines of Code</th>
<th class="left_align">Total Coverage</th>
<th class="left_align">Code Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td class="left_align"><a href="lib-models_rb.html">lib/models.rb</a></td>
<td class='right_align'><tt>231</tt></td>
<td class='right_align'><tt>191</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>90.91%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:91px"></div>
<div class="uncovered" style="width:9px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>89.01%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:89px"></div>
<div class="uncovered" style="width:11px"></div>
</div></td>
</tr>
</tbody>
</table>
</div>
<h3>Key</h3>
<div class="key"><pre><span class='marked'>Code reported as executed by Ruby looks like this...</span><span class='marked1'>and this: this line is also marked as covered.</span><span class='inferred'>Lines considered as run by rcov, but not reported by Ruby, look like this,</span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics).</span><span class='uncovered'>Finally, here's a line marked as not executed.</span></pre></div>
<h3>Coverage Details</h3>
<table class="details">
<tbody>
<tr class="uncovered">
<td><pre><a name="line1">1</a> require 'ohm/contrib'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line2">2</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line3">3</a> class Host &lt; Ohm::Model</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line4">4</a> include Ohm::Typecast</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line5">5</a> include Ohm::Timestamping</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line6">6</a> include Ohm::Callbacks</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line7">7</a> include Ohm::ExtraValidations</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line8">8</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line9">9</a> attribute :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line10">10</a> attribute :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line11">11</a> collection :services, Service</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line12">12</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line13">13</a> index :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line14">14</a> index :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line15">15</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line16">16</a> def validate</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line17">17</a> assert_present :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line18">18</a> assert_present :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line19">19</a> assert_unique :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line20">20</a> assert_member :status, [&quot;up&quot;,&quot;down&quot;,&quot;pending&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line21">21</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line22">22</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line23">23</a> def to_hash</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line24">24</a> arr = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line25">25</a> services.sort.each {|s| arr &lt;&lt; s.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line26">26</a> h = {:name =&gt; name, :status =&gt; status, :created_at =&gt; created_at, :updated_at =&gt; updated_at, :services =&gt; arr}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line27">27</a> super.merge(h)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line28">28</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line29">29</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line30">30</a> def is_new?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line31">31</a> self.created_at == self.updated_at</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line32">32</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line33">33</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line34">34</a> class &lt;&lt; self</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line35">35</a> def find_or_create(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line36">36</a> begin</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line37">37</a> # exclude requested status from lookup</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line38">38</a> h = find(opts.reject{|key,value| key == :status}).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line39">39</a> host = h.nil? ? create(opts) : h</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line40">40</a> host.status = opts[:status]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line41">41</a> if host.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line42">42</a> host.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line43">43</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line44">44</a> host</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line45">45</a> rescue Exception =&gt; e</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line46">46</a> e.message</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line47">47</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line48">48</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line49">49</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line50">50</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line51">51</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line52">52</a> class Service &lt; Ohm::Model</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line53">53</a> include Ohm::Typecast</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line54">54</a> include Ohm::Timestamping</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line55">55</a> include Ohm::Callbacks</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line56">56</a> include Ohm::ExtraValidations</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line57">57</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line58">58</a> attribute :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line59">59</a> attribute :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line60">60</a> reference :host, Host</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line61">61</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line62">62</a> index :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line63">63</a> index :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line64">64</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line65">65</a> def validate</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line66">66</a> assert_present :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line67">67</a> assert_present :status</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line68">68</a> assert_present :host_id</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line69">69</a> assert_unique [:name, :host_id]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line70">70</a> assert_member :status, [&quot;up&quot;, &quot;down&quot;, &quot;pending&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line71">71</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line72">72</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line73">73</a> def to_hash</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line74">74</a> super.merge(:name =&gt; name, :status =&gt; status, :updated_at =&gt; updated_at, :host =&gt; Host[host_id].name)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line75">75</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line76">76</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line77">77</a> def is_new?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line78">78</a> self.created_at == self.updated_at</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line79">79</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line80">80</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line81">81</a> class &lt;&lt; self</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line82">82</a> def find_or_create(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line83">83</a> begin</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line84">84</a> # convert passed host object to host_id if passed</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line85">85</a> if opts.has_key?(:host)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line86">86</a> opts.merge!({:host_id =&gt; opts[:host].id})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line87">87</a> opts.reject!{|key, value| key == :host}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line88">88</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line89">89</a> # exclude requested status from lookup</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line90">90</a> s = find(opts.reject{|key,value| key == :status}).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line91">91</a> service = s.nil? ? create(opts) : s</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line92">92</a> service.status = opts[:status]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line93">93</a> if service.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line94">94</a> service.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line95">95</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line96">96</a> service</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line97">97</a> rescue Exception =&gt; e</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line98">98</a> e.message</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line99">99</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line100">100</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line101">101</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line102">102</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line103">103</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line104">104</a> class Configuration &lt; Ohm::Model</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line105">105</a> include Ohm::Typecast</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line106">106</a> include Ohm::Timestamping</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line107">107</a> include Ohm::Callbacks</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line108">108</a> include Ohm::ExtraValidations</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line109">109</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line110">110</a> attribute :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line111">111</a> attribute :format</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line112">112</a> attribute :body</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line113">113</a> attribute :new_record</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line114">114</a> reference :application, Application</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line115">115</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line116">116</a> index :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line117">117</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line118">118</a> def validate</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line119">119</a> assert_present :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line120">120</a> assert_present :format</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line121">121</a> assert_present :body</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line122">122</a> assert_unique [:name, :application_id]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line123">123</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line124">124</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line125">125</a> def to_hash</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line126">126</a> super.merge(:name =&gt; name, :format =&gt; format, :body =&gt; body, :update_at =&gt; updated_at, :application =&gt; Application[application_id].name)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line127">127</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line128">128</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line129">129</a> def is_new?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line130">130</a> self.created_at == self.updated_at</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line131">131</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line132">132</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line133">133</a> class &lt;&lt; self</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line134">134</a> def find_or_create(opts={})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line135">135</a> begin</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line136">136</a> if find(opts).first.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line137">137</a> conf = create(opts)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line138">138</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line139">139</a> conf = find(opts).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line140">140</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line141">141</a> rescue Exception =&gt; e</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line142">142</a> e.message</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line143">143</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line144">144</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line145">145</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line146">146</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line147">147</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line148">148</a> class Application &lt; Ohm::Model</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line149">149</a> include Ohm::Typecast</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line150">150</a> include Ohm::Timestamping</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line151">151</a> include Ohm::Callbacks</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line152">152</a> include Ohm::ExtraValidations</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line153">153</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line154">154</a> attribute :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line155">155</a> collection :configurations, Configuration</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line156">156</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line157">157</a> index :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line158">158</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line159">159</a> def validate</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line160">160</a> assert_present :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line161">161</a> assert_unique :name</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line162">162</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line163">163</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line164">164</a> def to_hash</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line165">165</a> arr = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line166">166</a> configurations.sort.each {|c| arr &lt;&lt; c.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line167">167</a> super.merge(:name =&gt; name, :updated_at =&gt; updated_at, :configurations =&gt; arr)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line168">168</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line169">169</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line170">170</a> def is_new?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line171">171</a> self.created_at == self.updated_at</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line172">172</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line173">173</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line174">174</a> class &lt;&lt; self</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line175">175</a> def find_or_create(opts = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line176">176</a> begin</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line177">177</a> find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line178">178</a> if app.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line179">179</a> app.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line180">180</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line181">181</a> app</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line182">182</a> rescue Exception =&gt; e</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line183">183</a> e.message</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line184">184</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line185">185</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line186">186</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line187">187</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line188">188</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line189">189</a> class Watcher &lt; Ohm::Model #NYI</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line190">190</a> include Ohm::Typecast</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line191">191</a> include Ohm::Timestamping</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line192">192</a> include Ohm::Callbacks</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line193">193</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line194">194</a> attribute :client</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line195">195</a> attribute :endpoint</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line196">196</a> attribute :event</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line197">197</a> attribute :action </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line198">198</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line199">199</a> index :client</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line200">200</a> index :event</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line201">201</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line202">202</a> def validate</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line203">203</a> assert_present :client, :endpoint, :event, :action</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line204">204</a> assert_unique [:client, :endpoint, :event, :action]</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line205">205</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line206">206</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line207">207</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line208">208</a> # Some pluralized helper models</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line209">209</a> class Hosts</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line210">210</a> def self.all(options = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line211">211</a> options.empty? ? Host.all.sort : Host.find(options).sort</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line212">212</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line213">213</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line214">214</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line215">215</a> class Services</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line216">216</a> def self.all(options = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line217">217</a> options.empty? ? Service.all.sort : Service.find(options).sort</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line218">218</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line219">219</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line220">220</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line221">221</a> class Applications</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line222">222</a> def self.all(options = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line223">223</a> options.empty? ? Application.all.sort : Application.find(options).sort</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line224">224</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line225">225</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line226">226</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line227">227</a> class Configurations</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line228">228</a> def self.all(options = {})</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line229">229</a> options.empty? ? Configuration.all.sort : Configuration.find(options).sort</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line230">230</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line231">231</a> end </pre></td>
</tr>
</tbody>
</table>
<p>Generated on 2011-01-31 05:03:32 -0500 with <a href="http://github.com/relevance/rcov">rcov 0.9.8</a></p>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>noah.rb</title>
<link href="screen.css" media="all" rel="stylesheet" type="text/css" />
<link href="print.css" media="print" rel="stylesheet" type="text/css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<script type="text/javascript" src="rcov.js"></script>
</head>
<body>
<h1>Noah C0 Coverage Information - RCov</h1>
<h2>noah.rb</h2>
<div class="report_table_wrapper">
<table class='report' id='report_table'>
<thead>
<tr>
<th class="left_align">Name</th>
<th class="right_align">Total Lines</th>
<th class="right_align">Lines of Code</th>
<th class="left_align">Total Coverage</th>
<th class="left_align">Code Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td class="left_align"><a href="noah_rb.html">noah.rb</a></td>
<td class='right_align'><tt>326</tt></td>
<td class='right_align'><tt>285</tt></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>82.52%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:83px"></div>
<div class="uncovered" style="width:17px"></div>
</div></td>
<td class="left_align"><div class="percent_graph_legend"><tt class=''>81.05%</tt></div>
<div class="percent_graph">
<div class="covered" style="width:81px"></div>
<div class="uncovered" style="width:19px"></div>
</div></td>
</tr>
</tbody>
</table>
</div>
<h3>Key</h3>
<div class="key"><pre><span class='marked'>Code reported as executed by Ruby looks like this...</span><span class='marked1'>and this: this line is also marked as covered.</span><span class='inferred'>Lines considered as run by rcov, but not reported by Ruby, look like this,</span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics).</span><span class='uncovered'>Finally, here's a line marked as not executed.</span></pre></div>
<h3>Coverage Details</h3>
<table class="details">
<tbody>
<tr class="uncovered">
<td><pre><a name="line1">1</a> #!/usr/bin/env ruby</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line2">2</a> require 'sinatra/base'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line3">3</a> require 'sinatra/namespace'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line4">4</a> require 'ohm'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line5">5</a> begin</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line6">6</a> require 'yajl'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line7">7</a> rescue LoadError </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line8">8</a> require 'json'</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line9">9</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line10">10</a> require 'haml'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line11">11</a> require 'yaml'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line12">12</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line13">13</a> require File.join(File.dirname(__FILE__), 'lib/models')</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line14">14</a> require File.join(File.dirname(__FILE__), 'lib/helpers')</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line15">15</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line16">16</a> @db_settings = YAML::load File.new(File.join(File.dirname(__FILE__),'config','db.yml')).read</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line17">17</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line18">18</a> class NoahApp &lt; Sinatra::Base</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line19">19</a> register Sinatra::Namespace</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line20">20</a> helpers Sinatra::NoahHelpers</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line21">21</a> config_file = YAML::load File.new(File.join(File.dirname(__FILE__),'config','db.yml')).read</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line22">22</a> db = config_file[&quot;#{environment}&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line23">23</a> Ohm.connect(:url =&gt; &quot;redis://#{db[&quot;host&quot;]}:#{db[&quot;port&quot;]}/#{db[&quot;db&quot;]}&quot;)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line24">24</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line25">25</a> configure do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line26">26</a> set :app_file, __FILE__</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line27">27</a> set :root, File.dirname(__FILE__)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line28">28</a> set :server, %w[thin mongrel webrick kirk]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line29">29</a> set :port, 9291</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line30">30</a> set :logging, true</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line31">31</a> set :raise_errors, false</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line32">32</a> set :show_exceptions, false</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line33">33</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line34">34</a> configure(:development) do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line35">35</a> require 'sinatra/reloader'</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line36">36</a> register Sinatra::Reloader</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line37">37</a> also_reload &quot;models.rb&quot;</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line38">38</a> also_reload &quot;helpers.rb&quot;</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line39">39</a> set :port, 9292</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line40">40</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line41">41</a> configure(:test) do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line42">42</a> set :port, 9294</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line43">43</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line44">44</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line45">45</a> get '/' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line46">46</a> content_type &quot;text/html&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line47">47</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line48">48</a> haml :index, :format =&gt; :html5</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line49">49</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line50">50</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line51">51</a> before do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line52">52</a> content_type &quot;application/json&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line53">53</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line54">54</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line55">55</a> not_found do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line56">56</a> content_type &quot;application/json&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line57">57</a> erb :'404'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line58">58</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line59">59</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line60">60</a> error do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line61">61</a> content_type &quot;application/json&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line62">62</a> erb :'500'</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line63">63</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line64">64</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line65">65</a> namespace &quot;/h&quot; do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line66">66</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line67">67</a> get '/:hostname/:servicename/?' do |hostname, servicename|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line68">68</a> h = host_service(hostname, servicename)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line69">69</a> if h.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line70">70</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line71">71</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line72">72</a> h.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line73">73</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line74">74</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line75">75</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line76">76</a> get '/:hostname/?' do |hostname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line77">77</a> h = host(:name =&gt; hostname)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line78">78</a> if h.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line79">79</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line80">80</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line81">81</a> h.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line82">82</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line83">83</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line84">84</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line85">85</a> get '/?' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line86">86</a> hosts.map {|h| h.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line87">87</a> if hosts.size == 0</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line88">88</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line89">89</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line90">90</a> hosts.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line91">91</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line92">92</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line93">93</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line94">94</a> put '/:hostname/?' do |hostname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line95">95</a> required_params = [&quot;name&quot;, &quot;status&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line96">96</a> data = JSON.parse(request.body.read)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line97">97</a> (data.keys.sort == required_params.sort &amp;&amp; data['name'] == hostname) ? (host = Host.find_or_create(:name =&gt; data['name'], :status =&gt; data['status'])) : (raise &quot;Missing Parameters&quot;)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line98">98</a> if host.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line99">99</a> r = {&quot;result&quot; =&gt; &quot;success&quot;,&quot;id&quot; =&gt; &quot;#{host.id}&quot;,&quot;status&quot; =&gt; &quot;#{host.status}&quot;, &quot;name&quot; =&gt; &quot;#{host.name}&quot;, &quot;new_record&quot; =&gt; host.is_new?}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line100">100</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line101">101</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line102">102</a> raise &quot;#{host.errors}&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line103">103</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line104">104</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line105">105</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line106">106</a> delete '/:hostname/?' do |hostname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line107">107</a> host = Host.find(:name =&gt; hostname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line108">108</a> if host</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line109">109</a> services = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line110">110</a> Service.find(:host_id =&gt; host.id).sort.each {|x| services &lt;&lt; x; x.delete} if host.services.size &gt; 0</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line111">111</a> host.delete</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line112">112</a> r = {&quot;result&quot; =&gt; &quot;success&quot;, &quot;id&quot; =&gt; &quot;#{host.id}&quot;, &quot;name&quot; =&gt; &quot;#{hostname}&quot;, &quot;service_count&quot; =&gt; &quot;#{services.size}&quot;}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line113">113</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line114">114</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line115">115</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line116">116</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line117">117</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line118">118</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line119">119</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line120">120</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line121">121</a> namespace &quot;/s&quot; do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line122">122</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line123">123</a> get '/:servicename/:hostname/?' do |servicename, hostname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line124">124</a> hs = host_service(hostname, servicename)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line125">125</a> if hs.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line126">126</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line127">127</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line128">128</a> hs.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line129">129</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line130">130</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line131">131</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line132">132</a> get '/:servicename/?' do |servicename|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line133">133</a> s = services(:name =&gt; servicename)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line134">134</a> s.map {|x| x.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line135">135</a> if s.empty?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line136">136</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line137">137</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line138">138</a> s.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line139">139</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line140">140</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line141">141</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line142">142</a> get '/?' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line143">143</a> if services.empty?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line144">144</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line145">145</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line146">146</a> services.map {|s| s.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line147">147</a> services.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line148">148</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line149">149</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line150">150</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line151">151</a> put '/:servicename/?' do |servicename|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line152">152</a> required_params = [&quot;status&quot;, &quot;host&quot;, &quot;name&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line153">153</a> data = JSON.parse(request.body.read)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line154">154</a> if data.keys.sort == required_params.sort</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line155">155</a> h = Host.find(:name =&gt; data['host']).first || (raise &quot;Invalid Host&quot;)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line156">156</a> service = Service.find_or_create(:name =&gt; servicename, :status =&gt; data['status'], :host =&gt; h)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line157">157</a> if service.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line158">158</a> action = service.is_new? ? &quot;create&quot; : &quot;update&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line159">159</a> service.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line160">160</a> r = {&quot;action&quot; =&gt; action, &quot;result&quot; =&gt; &quot;success&quot;, &quot;id&quot; =&gt; service.id, &quot;host&quot; =&gt; h.name, &quot;name&quot; =&gt; service.name}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line161">161</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line162">162</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line163">163</a> raise &quot;#{service.errors}&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line164">164</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line165">165</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line166">166</a> raise &quot;Missing Parameters&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line167">167</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line168">168</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line169">169</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line170">170</a> delete '/:servicename/:hostname/?' do |servicename, hostname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line171">171</a> host = Host.find(:name =&gt; hostname).first || (halt 404)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line172">172</a> service = Service.find(:name =&gt; servicename, :host_id =&gt; host.id).first || (halt 404)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line173">173</a> if host &amp;&amp; service</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line174">174</a> service.delete</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line175">175</a> r = {&quot;action&quot; =&gt; &quot;delete&quot;, &quot;result&quot; =&gt; &quot;success&quot;, &quot;id&quot; =&gt; service.id, &quot;host&quot; =&gt; host.name, &quot;service&quot; =&gt; servicename}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line176">176</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line177">177</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line178">178</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line179">179</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line180">180</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line181">181</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line182">182</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line183">183</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line184">184</a> namespace &quot;/a&quot; do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line185">185</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line186">186</a> get '/:appname/:config/?' do |appname, config|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line187">187</a> app = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line188">188</a> if app.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line189">189</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line190">190</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line191">191</a> c = Configuration.find(:name =&gt; config, :application_id =&gt; app.id).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line192">192</a> c.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line193">193</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line194">194</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line195">195</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line196">196</a> get '/:appname/?' do |appname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line197">197</a> app = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line198">198</a> if app.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line199">199</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line200">200</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line201">201</a> app.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line202">202</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line203">203</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line204">204</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line205">205</a> put '/:appname/?' do |appname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line206">206</a> required_params = [&quot;name&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line207">207</a> data = JSON.parse(request.body.read)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line208">208</a> if data.keys.sort == required_params.sort &amp;&amp; data['name'] == appname</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line209">209</a> app = Application.find_or_create(:name =&gt; appname)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line210">210</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line211">211</a> raise &quot;Missing Parameters&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line212">212</a> end </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line213">213</a> if app.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line214">214</a> action = app.is_new? ? &quot;create&quot; : &quot;update&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line215">215</a> app.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line216">216</a> r = {&quot;result&quot; =&gt; &quot;success&quot;,&quot;id&quot; =&gt; app.id, &quot;action&quot; =&gt; action, &quot;name&quot; =&gt; app.name }</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line217">217</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line218">218</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line219">219</a> raise &quot;#{app.errors}&quot;</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line220">220</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line221">221</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line222">222</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line223">223</a> delete '/:appname/?' do |appname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line224">224</a> app = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line225">225</a> if app.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line226">226</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line227">227</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line228">228</a> configurations = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line229">229</a> Configuration.find(:application_id =&gt; app.id).sort.each {|x| configurations &lt;&lt; x; x.delete} if app.configurations.size &gt; 0</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line230">230</a> app.delete</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line231">231</a> r = {&quot;result&quot; =&gt; &quot;success&quot;, &quot;action&quot; =&gt; &quot;delete&quot;, &quot;id&quot; =&gt; &quot;#{app.id}&quot;, &quot;name&quot; =&gt; &quot;#{appname}&quot;, &quot;configurations&quot; =&gt; &quot;#{configurations.size}&quot;}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line232">232</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line233">233</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line234">234</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line235">235</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line236">236</a> get '/?' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line237">237</a> apps = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line238">238</a> Application.all.sort.each {|a| apps &lt;&lt; a.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line239">239</a> if apps.empty?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line240">240</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line241">241</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line242">242</a> apps.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line243">243</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line244">244</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line245">245</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line246">246</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line247">247</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line248">248</a> namespace '/c' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line249">249</a> </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line250">250</a> # Need to move this out to configuration.</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line251">251</a> # Maybe bootstrap them from itself?</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line252">252</a> content_type_mapping = {</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line253">253</a> :yaml =&gt; &quot;text/x-yaml&quot;,</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line254">254</a> :json =&gt; &quot;application/json&quot;,</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line255">255</a> :xml =&gt; &quot;text/xml&quot;,</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line256">256</a> :string =&gt; &quot;text/plain&quot;</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line257">257</a> }</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line258">258</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line259">259</a> get '/:appname/:element/?' do |appname, element|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line260">260</a> a = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line261">261</a> if a.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line262">262</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line263">263</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line264">264</a> c = Configuration.find(:name =&gt; element, :application_id =&gt; a.id).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line265">265</a> content_type content_type_mapping[c.format.to_sym] if content_type_mapping[c.format.to_sym]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line266">266</a> c.body</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line267">267</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line268">268</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line269">269</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line270">270</a> get '/:appname/?' do |appname|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line271">271</a> config = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line272">272</a> a = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line273">273</a> if a.nil?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line274">274</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line275">275</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line276">276</a> Configuration.find(:application_id =&gt; a.id).sort.each {|c| config &lt;&lt; c.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line277">277</a> config.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line278">278</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line279">279</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line280">280</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line281">281</a> get '/?' do</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line282">282</a> configs = []</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line283">283</a> Configuration.all.sort.each {|c| configs &lt;&lt; c.to_hash}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line284">284</a> if configs.empty?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line285">285</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line286">286</a> else </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line287">287</a> configs.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line288">288</a> end </pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line289">289</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line290">290</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line291">291</a> put '/:appname/:element?' do |appname, element|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line292">292</a> app = Application.find_or_create(:name =&gt; appname)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line293">293</a> config = Configuration.find_or_create(:name =&gt; element, :application_id =&gt; app.id)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line294">294</a> required_params = [&quot;format&quot;, &quot;body&quot;]</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line295">295</a> data = JSON.parse(request.body.read)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line296">296</a> data.keys.sort == required_params.sort ? (config.format = data[&quot;format&quot;]; config.body = data[&quot;body&quot;]) : (raise &quot;Missing Parameters&quot;)</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line297">297</a> if config.valid?</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line298">298</a> config.save</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line299">299</a> action = config.is_new? ? &quot;create&quot; : &quot;update&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line300">300</a> dependency_action = app.is_new? ? &quot;created&quot; : &quot;updated&quot;</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line301">301</a> r = {&quot;result&quot; =&gt; &quot;success&quot;,&quot;id&quot; =&gt; &quot;#{config.id}&quot;, &quot;action&quot; =&gt; action, &quot;dependencies&quot; =&gt; dependency_action, &quot;application&quot; =&gt; app.name, &quot;item&quot; =&gt; config.name}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line302">302</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line303">303</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line304">304</a> raise &quot;#{config.errors}&quot;</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line305">305</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line306">306</a> end</pre></td>
</tr>
<tr class="inferred">
<td><pre><a name="line307">307</a> </pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line308">308</a> delete '/:appname/:element?' do |appname, element|</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line309">309</a> app = Application.find(:name =&gt; appname).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line310">310</a> if app</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line311">311</a> config = Configuration.find(:name=&gt; element, :application_id =&gt; app.id).first</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line312">312</a> if config</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line313">313</a> config.delete</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line314">314</a> r = {&quot;result&quot; =&gt; &quot;success&quot;, &quot;id&quot; =&gt; &quot;#{config.id}&quot;, &quot;action&quot; =&gt; &quot;delete&quot;, &quot;application&quot; =&gt; &quot;#{app.name}&quot;, &quot;item&quot; =&gt; &quot;#{element}&quot;}</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line315">315</a> r.to_json</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line316">316</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line317">317</a> halt 404</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line318">318</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line319">319</a> else</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line320">320</a> halt 404</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line321">321</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line322">322</a> end</pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line323">323</a> </pre></td>
</tr>
<tr class="uncovered">
<td><pre><a name="line324">324</a> end</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line325">325</a> run! if app_file == $0</pre></td>
</tr>
<tr class="marked">
<td><pre><a name="line326">326</a> end</pre></td>
</tr>
</tbody>
</table>
<p>Generated on 2011-01-31 05:03:32 -0500 with <a href="http://github.com/relevance/rcov">rcov 0.9.8</a></p>
</body>
</html>
/* Hide controls */
body {
color: #000000;
background-color: #ffffff;
}
/* Hide controls */
div.filters {
display: none;
}
\ No newline at end of file
function toggleCode( id ) {
if ( document.getElementById ) {
elem = document.getElementById( id );
} else if ( document.all ) {
elem = eval( "document.all." + id );
} else {
return false;
}
elemStyle = elem.style;
if ( elemStyle.display != "block" ) {
elemStyle.display = "block";
} else {
elemStyle.display = "none";
}
return true;
}
function restripe() {
i = 0;
$('table#report_table tbody tr').each(function(){
if (this.style.display != "none") {
i += 1;
classes = this.className.split(" ");
if ($.inArray("even",classes) != -1) {
classes.splice($.inArray("even",classes),1);
} else if ($.inArray("odd",classes) != -1) {
classes.splice($.inArray("odd",classes),1);
}
if (i % 2 === 0) {
this.className = classes.join(" ") + " odd";
} else {
this.className = classes.join(" ") + " even";
}
}
});
}
// Fix IE's lack of support for indexOf (!)
if (!Array.indexOf) { Array.prototype.indexOf = function(obj){ for(var i=0; i<this.length; i++){ if(this[i]==obj){return i;} } return -1; }}
\ No newline at end of file
/* @group General */
body {
font-family: Verdana, Helvetica, Arial, Sans-Serif;
font-size: 12px;
color: #4C4C4C;
background-color: #F4F2ED;
padding: 1em;
}
a:link {
color: #191919;
}
a:visited {
color: #191919;
}
pre, code {
color: #000000;
font-family: "Bitstream Vera Sans Mono","Monaco","Courier New",monospace;
font-size: 95%;
line-height: 1.3em;
margin-top: 0;
margin-bottom: 0;
padding: 0;
word-wrap: break-word;
}
h1, h2, h3, h4, h5, h6 {
margin: 0em 0em 1em 0em;
color: #666666;
}
h1 {
display: block;
font-size: 2em;
letter-spacing: -1px;
}
h2 {
margin-top: -1em;
}
fieldset {
display: inline;
border: 0px;
padding: 0px;
margin-right: 1em;
}
div.filters {
margin-bottom: 1em;
}
.hidden {
display: none;
}
/* @end */
/* @group Cross-References */
span.cross-ref-title {
font-size: 140%;
}
span.cross-ref a {
text-decoration: none;
}
span.cross-ref {
background-color:#f3f7fa;
border: 1px dashed #333;
margin: 1em;
padding: 0.5em;
overflow: hidden;
}
a.crossref-toggle {
text-decoration: none;
}
/* @end */
/* @group Report Table */
div.report_table_wrapper {
min-width: 900px;
}
table.report {
border-collapse: collapse;
border: 1px solid #666666;
width: 100%;
margin-bottom: 1em;
}
table.report tr {
line-height: 1.75em;
}
table.report th {
background: #666666;
color: #ffffff;
text-align: right;
text-transform: uppercase;
font-size: .8em;
font-weight: bold;
padding: 0em .5em;
border: 1px solid #666666;
}
table.report tfoot tr {
background: #dddddd;
font-weight: bold;
padding: .5em;
border: 1px solid #666666;
}
th.left_align, td.left_align {
text-align: left !important;
}
th.right_align, td.right_align {
text-align: right;
padding-right: 2em !important;
}
table.report th.header:hover {
cursor: pointer;
text-decoration: underline;
}
table.report th.headerSortUp:after{
content: "\25BC";
margin-left: 1em;
}
table.report th.headerSortDown:after {
content: "\25B2";
margin-left: 1em;
}
table.report tr.summary_row {
background: #cccccc;
border: 1px solid #cccccc;
}
table.report tr.summary_row td {
padding-left: .2em !important;
color: #333333;
font-weight: bold;
}
table.report td {
padding: .2em .5em .2em .5em;
}
table.report td a {
text-decoration: none;
}
table.report tbody tr:hover {
background: #cccccc !important;
}
table.report tr.summary_row td {
border-bottom: 1px solid #aaaaaa;
}
table.report tr {
background-color: #eeeeee;
}
table.report tr.odd {
background-color: #dddddd;
}
/* @end */
/* @group Percentage Graphs */
div.percent_graph_legend {
width: 5.5em;
float: left;
margin: .5em 1em .5em 0em;
height: 1em;
line-height: 1em;
}
div.percent_graph {
height: 1em;
border: #333333 1px solid;
empty-cells: show;
padding: 0px;
border-collapse: collapse;
width: 100px !important;
float: left;
margin: .5em 1em .5em 0em;
}
div.percent_graph div {
float: left;
height: 1em;
padding: 0px !important;
}
div.percent_graph div.covered {
background: #649632;
}
div.percent_graph div.uncovered {
background: #a92730;
}
div.percent_graph div.NA {
background: #eaeaea;
}
/* @end */
/* @group Details page */
table.details {
margin-top: 1em;
border-collapse: collapse;
width: 100%;
border: 1px solid #666666;
}
table.details tr {
line-height: 1.75em;
}
table.details td {
padding: .25em;
}
span.inferred, span.inferred1, span.marked, span.marked1, span.uncovered, span.uncovered1 {
display: block;
padding: .25em;
}
tr.inferred td, span.inferred {
background-color: #e0dedb;
}
tr.inferred1 td, span.inferred1 {
background-color: #e0dedb;
}
tr.marked td, span.marked, span.marked1 {
background-color: #bed2be;
}
tr.uncovered td, span.uncovered {
background-color: #ce8b8c;
}
tr.uncovered1 td, span.uncovered1 {
background-color: #ce8b8c;
}
div.key {
border: 1px solid #666666;
margin: 1em 0em;
}
/* @end */
require 'ohm'
require 'ohm/contrib'
begin
require 'yajl'
rescue LoadError
require 'json'
end
require 'haml'
require 'yaml'
require 'sinatra/base'
require 'sinatra/namespace'
require File.join(File.dirname(__FILE__), 'noah','hosts')
require File.join(File.dirname(__FILE__), 'noah','services')
require File.join(File.dirname(__FILE__), 'noah','applications')
require File.join(File.dirname(__FILE__), 'noah','configurations')
require File.join(File.dirname(__FILE__), 'noah','watchers')
require File.join(File.dirname(__FILE__), 'noah','app')
require 'sinatra/base'
require 'sinatra/namespace'
require 'ohm'
require 'ohm/contrib'
require File.join(File.dirname(__FILE__), 'helpers')
require File.join(File.dirname(__FILE__), 'models')
module Noah
class App < Sinatra::Base
register ::Sinatra::Namespace
helpers Noah::SinatraHelpers
configure do
set :app_file, __FILE__
set :root, File.expand_path(File.join(File.dirname(__FILE__), "..",".."))
set :server, %w[thin mongrel webrick]
set :logging, true
set :raise_errors, false
set :show_exceptions, false
end
configure(:development) do
begin
require 'sinatra/reloader'
register Sinatra::Reloader
also_reload File.join(File.dirname(__FILE__), 'models.rb')
also_reload File.join(File.dirname(__FILE__), 'helpers.rb')
rescue LoadError
puts "sinatra-reloader gem missing. reloading disabled"
end
end
before do
content_type "application/json"
end
get '/' do
content_type "text/html"
haml :index, :format => :html5
end
not_found do
content_type "application/json"
erb :'404'
end
error do
content_type "application/json"
erb :'500'
end
namespace "/h" do
get '/:hostname/:servicename/?' do |hostname, servicename|
h = host_service(hostname, servicename)
if h.nil?
halt 404
else
h.to_json
end
end
get '/:hostname/?' do |hostname|
h = host(:name => hostname)
if h.nil?
halt 404
else
h.to_json
end
end
get '/?' do
hosts.map {|h| h.to_hash}
if hosts.size == 0
halt 404
else
hosts.to_json
end
end
put '/:hostname/?' do |hostname|
required_params = ["name", "status"]
data = JSON.parse(request.body.read)
(data.keys.sort == required_params.sort && data['name'] == hostname) ? (host = ::Host.find_or_create(:name => data['name'], :status => data['status'])) : (raise "Missing Parameters")
if host.valid?
r = {"result" => "success","id" => "#{host.id}","status" => "#{host.status}", "name" => "#{host.name}", "new_record" => host.is_new?}
r.to_json
else
raise "#{host.errors}"
end
end
delete '/:hostname/?' do |hostname|
host = ::Host.find(:name => hostname).first
if host
services = []
Service.find(:host_id => host.id).sort.each {|x| services << x; x.delete} if host.services.size > 0
host.delete
r = {"result" => "success", "id" => "#{host.id}", "name" => "#{hostname}", "service_count" => "#{services.size}"}
r.to_json
else
halt 404
end
end
end
namespace "/s" do
get '/:servicename/:hostname/?' do |servicename, hostname|
hs = host_service(hostname, servicename)
if hs.nil?
halt 404
else
hs.to_json
end
end
get '/:servicename/?' do |servicename|
s = services(:name => servicename)
s.map {|x| x.to_hash}
if s.empty?
halt 404
else
s.to_json
end
end
get '/?' do
if services.empty?
halt 404
else
services.map {|s| s.to_hash}
services.to_json
end
end
put '/:servicename/?' do |servicename|
required_params = ["status", "host", "name"]
data = JSON.parse(request.body.read)
if data.keys.sort == required_params.sort
h = ::Host.find(:name => data['host']).first || (raise "Invalid Host")
service = ::Service.find_or_create(:name => servicename, :status => data['status'], :host => h)
if service.valid?
action = service.is_new? ? "create" : "update"
service.save
r = {"action" => action, "result" => "success", "id" => service.id, "host" => h.name, "name" => service.name}
r.to_json
else
raise "#{service.errors}"
end
else
raise "Missing Parameters"
end
end
delete '/:servicename/:hostname/?' do |servicename, hostname|
host = ::Host.find(:name => hostname).first || (halt 404)
service = ::Service.find(:name => servicename, :host_id => host.id).first || (halt 404)
if host && service
service.delete
r = {"action" => "delete", "result" => "success", "id" => service.id, "host" => host.name, "service" => servicename}
r.to_json
else
halt 404
end
end
end
namespace "/a" do
get '/:appname/:config/?' do |appname, config|
app = ::Application.find(:name => appname).first
if app.nil?
halt 404
else
c = ::Configuration.find(:name => config, :application_id => app.id).first
c.to_json
end
end
get '/:appname/?' do |appname|
app = ::Application.find(:name => appname).first
if app.nil?
halt 404
else
app.to_json
end
end
put '/:appname/?' do |appname|
required_params = ["name"]
data = JSON.parse(request.body.read)
if data.keys.sort == required_params.sort && data['name'] == appname
app = ::Application.find_or_create(:name => appname)
else
raise "Missing Parameters"
end
if app.valid?
action = app.is_new? ? "create" : "update"
app.save
r = {"result" => "success","id" => app.id, "action" => action, "name" => app.name }
r.to_json
else
raise "#{app.errors}"
end
end
delete '/:appname/?' do |appname|
app = ::Application.find(:name => appname).first
if app.nil?
halt 404
else
configurations = []
::Configuration.find(:application_id => app.id).sort.each {|x| configurations << x; x.delete} if app.configurations.size > 0
app.delete
r = {"result" => "success", "action" => "delete", "id" => "#{app.id}", "name" => "#{appname}", "configurations" => "#{configurations.size}"}
r.to_json
end
end
get '/?' do
apps = []
::Application.all.sort.each {|a| apps << a.to_hash}
if apps.empty?
halt 404
else
apps.to_json
end
end
end
namespace '/c' do
# Need to move this out to configuration.
# Maybe bootstrap them from itself?
content_type_mapping = {
:yaml => "text/x-yaml",
:json => "application/json",
:xml => "text/xml",
:string => "text/plain"
}
get '/:appname/:element/?' do |appname, element|
a = ::Application.find(:name => appname).first
if a.nil?
halt 404
else
c = ::Configuration.find(:name => element, :application_id => a.id).first
content_type content_type_mapping[c.format.to_sym] if content_type_mapping[c.format.to_sym]
c.body
end
end
get '/:appname/?' do |appname|
config = []
a = ::Application.find(:name => appname).first
if a.nil?
halt 404
else
::Configuration.find(:application_id => a.id).sort.each {|c| config << c.to_hash}
config.to_json
end
end
get '/?' do
configs = []
::Configuration.all.sort.each {|c| configs << c.to_hash}
if configs.empty?
halt 404
else
configs.to_json
end
end
put '/:appname/:element?' do |appname, element|
app = ::Application.find_or_create(:name => appname)
config = ::Configuration.find_or_create(:name => element, :application_id => app.id)
required_params = ["format", "body"]
data = JSON.parse(request.body.read)
data.keys.sort == required_params.sort ? (config.format = data["format"]; config.body = data["body"]) : (raise "Missing Parameters")
if config.valid?
config.save
action = config.is_new? ? "create" : "update"
dependency_action = app.is_new? ? "created" : "updated"
r = {"result" => "success","id" => "#{config.id}", "action" => action, "dependencies" => dependency_action, "application" => app.name, "item" => config.name}
r.to_json
else
raise "#{config.errors}"
end
end
delete '/:appname/:element?' do |appname, element|
app = ::Application.find(:name => appname).first
if app
config = ::Configuration.find(:name=> element, :application_id => app.id).first
if config
config.delete
r = {"result" => "success", "id" => "#{config.id}", "action" => "delete", "application" => "#{app.name}", "item" => "#{element}"}
r.to_json
else
halt 404
end
else
halt 404
end
end
end
# run! if app_file == $0
end
end
class Application < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
collection :configurations, Configuration
index :name
def validate
assert_present :name
assert_unique :name
end
def to_hash
arr = []
configurations.sort.each {|c| arr << c.to_hash}
super.merge(:name => name, :updated_at => updated_at, :configurations => arr)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)
if app.valid?
app.save
end
app
rescue Exception => e
e.message
end
end
end
end
class Applications
def self.all(options = {})
options.empty? ? Application.all.sort : Application.find(options).sort
end
end
class Configuration < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :format
attribute :body
attribute :new_record
reference :application, Application
index :name
def validate
assert_present :name
assert_present :format
assert_present :body
assert_unique [:name, :application_id]
end
def to_hash
super.merge(:name => name, :format => format, :body => body, :update_at => updated_at, :application => Application[application_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts={})
begin
if find(opts).first.nil?
conf = create(opts)
else
conf = find(opts).first
end
rescue Exception => e
e.message
end
end
end
end
class Configurations
def self.all(options = {})
options.empty? ? Configuration.all.sort : Configuration.find(options).sort
end
end
require File.join(File.dirname(__FILE__), 'models')
module Noah
module SinatraHelpers
extend(Ohm)
def host(opts = {})
Host.find(opts).first
end
def hosts(opts = {})
Hosts.all(opts)
end
def service(opts = {})
Service.find(options)
end
def services(opts = {})
Services.all(opts)
end
def host_service(hostname, servicename)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Service.find(:host_id => h.id, :name => servicename).first
end
end
def host_services(hostname)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Services.all(:host_id => id)
end
end
def application(opts = {})
Application.find(opts).first
end
def applications(opts = {})
Applications.all(opts)
end
def configuration(opts = {})
Configuration.find(opts).first
end
def configurations(opts = {})
Configurations.all(opts)
end
end
end
class Host < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
collection :services, Service
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_unique :name
assert_member :status, ["up","down","pending"]
end
def to_hash
arr = []
services.sort.each {|s| arr << s.to_hash}
h = {:name => name, :status => status, :created_at => created_at, :updated_at => updated_at, :services => arr}
super.merge(h)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# exclude requested status from lookup
h = find(opts.reject{|key,value| key == :status}).first
host = h.nil? ? create(opts) : h
host.status = opts[:status]
if host.valid?
host.save
end
host
rescue Exception => e
e.message
end
end
end
end
class Hosts
def self.all(options = {})
options.empty? ? Host.all.sort : Host.find(options).sort
end
end
require File.join(File.dirname(__FILE__),'hosts')
require File.join(File.dirname(__FILE__),'services')
require File.join(File.dirname(__FILE__),'applications')
require File.join(File.dirname(__FILE__),'configurations')
require File.join(File.dirname(__FILE__),'watchers')
class Service < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
reference :host, Host
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_present :host_id
assert_unique [:name, :host_id]
assert_member :status, ["up", "down", "pending"]
end
def to_hash
super.merge(:name => name, :status => status, :updated_at => updated_at, :host => Host[host_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# convert passed host object to host_id if passed
if opts.has_key?(:host)
opts.merge!({:host_id => opts[:host].id})
opts.reject!{|key, value| key == :host}
end
# exclude requested status from lookup
s = find(opts.reject{|key,value| key == :status}).first
service = s.nil? ? create(opts) : s
service.status = opts[:status]
if service.valid?
service.save
end
service
rescue Exception => e
e.message
end
end
end
end
class Services
def self.all(options = {})
options.empty? ? Service.all.sort : Service.find(options).sort
end
end
module Noah
VERSION = "0.0.5"
end
class Watcher < Ohm::Model #NYI
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
attribute :client
attribute :endpoint
attribute :event
attribute :action
index :client
index :event
def validate
assert_present :client, :endpoint, :event, :action
assert_unique [:client, :endpoint, :event, :action]
end
end
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "noah/version"
Gem::Specification.new do |s|
s.name = "noah"
s.version = Noah::VERSION
s.platform = RUBY_ENGINE
s.authors = ["lusis"]
s.email = ["lusis.org+rubygems.org@gmail.com"]
s.homepage = "https://github.com/lusis/noah"
s.summary = %q{Application registry based on Apache Zookeeper}
s.description = %q{Application registry based on Apache Zookeeper}
s.rubyforge_project = "noah"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_dependency("rake", ["= 0.8.7"])
s.add_dependency("sinatra", ["= 1.1.2"])
s.add_dependency("sinatra-namespace", ["0.6.1"])
s.add_dependency("ohm", ["= 0.1.3"])
s.add_dependency("ohm-contrib", ["= 0.1.0"])
s.add_dependency("haml", ["= 3.0.25"])
s.add_dependency("vegas", ["= 0.1.8"])
s.add_dependency("yajl-ruby", ["= 0.7.9"]) if s.platform.to_s == 'ruby'
s.add_dependency("jruby-json", ["= 1.5.0"]) if s.platform.to_s == 'jruby'
s.add_dependency("thin", ["= 1.2.7"]) if s.platform.to_s == 'ruby'
s.add_dependency("json-jruby", ["= 1.4.6"]) if s.platform.to_s == 'jruby'
s.add_dependency("jruby-openssl", ["= 0.7.3"]) if s.platform.to_s == 'jruby'
s.add_development_dependency("sinatra-reloader", ["= 0.5.0"])
s.add_development_dependency("rspec", ["= 2.4.0"])
s.add_development_dependency("rcov", ["= 0.9.9"]) if s.platform.to_s == 'ruby'
s.add_development_dependency("rack-test", ["= 0.5.7"])
s.add_development_dependency("ZenTest", ["= 4.4.2"])
s.add_development_dependency("autotest", ["= 4.4.6"])
s.add_development_dependency("autotest-growl", ["= 0.2.9"])
s.add_development_dependency("warbler", ["= 1.2.1"]) if s.platform.to_s == 'java'
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Application Model", :reset_redis => true do
before(:all) do
@appdata1 = {:name => "my_application"}
@appdata2 = {:name => "my_other_app"}
@appconf_string = {:name => "mystringconf", :format => "string", :body => "some_var"}
@appconf_json = {:name => "myjsonconf", :format => "json", :body => @appconf_string.to_json}
end
before(:each) do
Ohm.redis.flushdb
end
after(:each) do
Ohm.redis.flushdb
end
describe "should" do
it "create a new Application" do
a = Application.create(@appdata1)
a.valid?.should == true
a.is_new?.should == true
b = Application.find(@appdata1).first
b.should == a
end
it "create a new Application with Configurations" do
a = Application.create(@appdata1)
a.configurations << Configuration.create(@appconf_string.merge({:application => a}))
a.valid?.should == true
a.is_new?.should == true
a.save
b = Application.find(@appdata1).first
b.should == a
b.configurations.size.should == 1
b.configurations.first.name.should == @appconf_string[:name]
b.configurations.first.format.should == @appconf_string[:format]
b.configurations.first.body.should == @appconf_string[:body]
end
it "create a new Application via find_or_create" do
a = Application.find_or_create(@appdata2)
a.valid?.should == true
a.is_new?.should == true
b = Application.find(@appdata2).first
b.should == a
end
it "update an existing Application via find_or_create" do
a = Application.create(@appdata1)
a.is_new?.should == true
sleep 2
b = Application.find_or_create(@appdata1)
b.is_new?.should == false
end
it "delete an existing Application" do
a = Application.create(@appdata1)
b = Application.find(@appdata1).first
b.should == a
b.delete
c = Application.find(@appdata1).first
c.nil?.should == true
end
it "return all Applications" do
a = Application.create(@appdata1)
b = Application.create(@appdata2)
c = Applications.all
c.size.should == 2
c.member?(a).should == true
c.member?(b).should == true
end
end
describe "should not" do
it "should not create a new Application without a name" do
a = Application.create
a.valid?.should == false
a.errors.should == [[:name, :not_present]]
end
it "should not create a duplicate Application" do
a = Application.create(@appdata1)
a.valid?.should == true
b = Application.create(@appdata1)
b.valid?.should == false
b.errors.should == [[:name, :not_unique]]
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Configuration Model", :reset_redis => true do
describe "should" do
it "create a new Configuration"
it "create a new Configuration via find_or_create"
it "update an existing Configuration via find_or_create"
it "delete an existing Configuration"
it "return all Configurations"
end
describe "should not" do
it "create a new Configuration without a name"
it "create a new Configuration without a format"
it "create a new Configuration without a body"
it "create a new Configuration without an Application"
it "create a duplicate Configuration"
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Host Model", :reset_redis => true do
describe "should" do
it "create a new Host with no services" do
hostname = "host1.domain.com"
hoststatus = "up"
host = Host.create(:name => hostname, :status => hoststatus)
host.valid?.should == true
host.save
host.name.should == hostname
host.status.should == hoststatus
host.services.size.should == 0
end
it "create a new Host with services" do
hostname = "host2.domain.com"
hoststatus = "down"
servicename = 'myservice'
servicestatus = 'pending'
host = Host.create(:name => hostname, :status => hoststatus)
host.valid?.should == true
host.save
host.services << Service.create(:name => servicename, :status => servicestatus, :host => host)
host.name.should == hostname
host.status.should == hoststatus
host.services.size.should == 1
host.services[1].valid?.should == true
host.services[1].name.should == servicename
host.services[1].status.should == servicestatus
host.services[1].host_id.should == host.id
end
it "create a Host via find_or_create" do
hostname = "host3.domain.com"
hoststatus = "up"
host = Host.find_or_create(:name => hostname, :status => hoststatus)
host.valid?.should == true
host.save
host.is_new?.should == true
end
it "update a Host via find_or_create" do
hostname = "host3.domain.com"
hoststatus = "pending"
newstatus = "down"
host = Host.find_or_create(:name => hostname, :status => hoststatus)
host.valid?.should == true
host.save
host.is_new?.should == true
sleep 1
host1 = Host.find_or_create(:name => hostname, :status => newstatus)
host1.save
host1.is_new?.should == false
end
it "delete a Host" do
hostname = "host3.domain.com"
hoststatus = "pending"
host = Host.create(:name => hostname, :status => hoststatus)
host.save
host.delete
Host.find(:name => hostname).empty?.should == true
end
it "should return all Hosts" do
hostname1 = "host1.domain.com"
status1 = "up"
hostname2 = "host2.domain.com"
status2= "down"
host1 = Host.create(:name => hostname1, :status => status1)
host2 = Host.create(:name => hostname2, :status => status2)
host1.save
host2.save
Hosts.all.class.should == Array
Hosts.all.size.should == 2
Hosts.all.first.name.should == hostname1
Hosts.all.first.status.should == status1
Hosts.all.last.name.should == hostname2
Hosts.all.last.status.should == status2
end
end
describe "should not" do
it "create a new Host with missing status" do
hostname ="host3.domain.com"
host = Host.create(:name => hostname)
host.valid?.should == false
host.errors.should == [[:status, :not_present], [:status, :not_member]]
end
it "create a new Host with missing hostname" do
status = "up"
host = Host.create(:status => status)
host.valid?.should == false
host.errors.should == [[:name, :not_present]]
end
it "create a new Host with an invalid status" do
hostname = "host3.domain.com"
status = "online"
host = Host.create(:name => hostname, :status => status)
host.valid?.should == false
host.errors.should == [[:status, :not_member]]
end
it "create a duplicate Host" do
hostname = "host1.domain.com"
status = "up"
host1 = Host.create(:name => hostname, :status => status)
host1.save
host2 = Host.create(:name => hostname, :status => status)
host2.valid?.should == false
host2.errors.should == [[:name, :not_unique]]
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Application API", :reset_redis => false do
before(:all) do
@a = Application.create(:name => 'rspec_sample_app')
@a.configurations << Configuration.create(:name => 'rspec_config', :format => 'string', :body => 'rspec is great', :application => @a)
@a.save
@c = @a.configurations.first
end
describe "calling" do
describe "GET" do
it "all applications should work" do
get '/a'
last_response.should be_ok
response = last_response.should return_json
response.is_a?(Array).should == true
end
it "named application should work" do
get '/a/rspec_sample_app'
last_response.should be_ok
response = last_response.should return_json
response["id"].should == @a.id
response["name"].should == @a.name
c = response["configurations"].first
c["id"].should == @c.id
c["name"].should == @c.name
c["body"].should == @c.body
c["format"].should == @c.format
end
it "named configuration for application should work" do
get "/a/#{@a.name}/#{@c.name}"
last_response.should be_ok
response = last_response.should return_json
response["id"].should == @c.id
response["name"].should == @c.name
response["format"].should == @c.format
response["body"].should == @c.body
response["application"].should == @a.name
end
it "invalid application should not work" do
get "/a/should_not_exist"
last_response.should be_missing
end
it "invalid configuration for application should not work" do
get "/a/should_not_exist/should_not_exist"
last_response.should be_missing
end
end
describe "PUT" do
before(:all) do
@appdata = {:name => "should_now_exist"}
end
it "new application should work" do
put "/a/#{@appdata[:name]}", @appdata.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["id"].nil?.should == false
response["name"].should == @appdata[:name]
response["action"].should == "create"
Application.find(:name => @appdata[:name]).size.should == 1
Application.find(:name => @appdata[:name]).first.is_new?.should == true
end
it "new application with missing name should not work" do
put "/a/should_not_work", '{"foo":"bar"}', "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "existing application should work" do
sleep 3
put "/a/#{@appdata[:name]}", @appdata.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["id"].nil?.should == false
response["name"].should == @appdata[:name]
response["action"].should == "update"
Application.find(:name => @appdata[:name]).size.should == 1
Application.find(:name => @appdata[:name]).first.is_new?.should == false
end
end
describe "DELETE" do
before(:each) do
@appdata = {:name => "should_now_exist"}
end
it "existing application should work" do
delete "/a/#{@appdata[:name]}"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "delete"
response["id"].nil?.should == false
response["name"].should == @appdata[:name]
response["configurations"].should == "0"
end
it "invalid application should not work" do
delete "/a/should_not_work"
last_response.should be_missing
end
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Configuration API", :reset_redis => false, :populate_sample_data => true do
describe "calling" do
describe "GET" do
it "all configurations should work" do
get '/c'
last_response.should be_ok
last_response.should return_json
end
it "named application should work" do
get '/c/noah'
last_response.should be_ok
response = last_response.should return_json
response.is_a?(Array).should == true
response.first["name"].should == "redis"
response.first["format"].should == "string"
response.first["body"].should == "redis://127.0.0.1:6379/0"
response.first["application"].should == "noah"
end
it "named configuration for application should work" do
get '/c/noah/redis'
last_response.should be_ok
response = last_response.body
response.should == "redis://127.0.0.1:6379/0"
end
it "named configuration should work with mime-type" do
require 'yaml'
get '/c/myrailsapp1/database.yml'
last_response.should be_ok
last_response.headers["Content-Type"].should == "text/x-yaml;charset=utf-8"
response = YAML.load(last_response.body)
response.is_a?(Hash).should == true
response.keys.should == ["development"]
response["development"].keys.should == ["database", "adapter", "username", "password"]
response["development"].values.should == ["development_database", "mysql", "dev_user", "dev_password"]
end
it "invalid application should not work" do
get '/c/badapp'
last_response.should be_missing
end
it "invalid configuration for application should not work" do
get '/c/badapp/badconfig'
last_response.should be_missing
end
end
describe "PUT" do
it "new configuration should work" do
config_data = {:format => "string", :body => "sample_config_entry"}.to_json
put '/c/newapp/newconfig', config_data, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "create"
response["dependencies"].should == "created"
response["application"].should == "newapp"
response["item"].should == "newconfig"
end
it "existing configuration should work" do
config_data = {:format => "string", :body => "sample_config_entry"}.to_json
sleep 3
put '/c/newapp/newconfig', config_data, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "update"
response["dependencies"].should == "updated"
response["application"].should == "newapp"
response["item"].should == "newconfig"
end
it "new configuration with missing format should not work" do
config_data = {:body => "a string"}.to_json
put '/c/newnewapp/someconfig', config_data, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "new configuration with missing body should not work" do
config_data = {:body => "a string"}.to_json
put '/c/newnewapp/someconfig', config_data, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
end
describe "DELETE" do
before(:all) do
cparms = {:name => 'a', :format => 'string', :body => 'asdf'}
@a = Application.create(:name => 'delete_test_app')
@a.configurations << Configuration.create(cparms)
@a.save
@c = @a.configurations.first
end
it "existing configuration should work" do
delete "/c/#{@a.name}/#{@c.name}"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["id"].should == @c.id
response["action"].should == "delete"
response["application"].should == @a.name
response["item"].should == @c.name
end
it "invalid configuration should not work" do
delete "/c/#{@a.name}/#{@c.name}"
last_response.should be_missing
end
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Host API", :reset_redis => false, :populate_sample_data => true do
describe "calling" do
describe "GET" do
it "all hosts should work" do
get '/h'
last_response.should be_ok
last_response.should return_json
end
it "existing host should work" do
get '/h/localhost'
last_response.should be_ok
response = last_response.should return_json
services = response["services"]
response["name"].should == "localhost"
response["status"].should == "up"
services.size.should == 2
services.first["name"].should == "redis"
services.first["status"].should == "up"
services.first["host"].should == "localhost"
services.last["name"].should == "noah"
services.last["status"].should == "up"
services.last["host"].should == "localhost"
end
it "named service for host should work" do
get '/h/localhost/noah'
last_response.should be_ok
response = last_response.should return_json
response["name"].should == "noah"
response["status"].should == "up"
response["host"].should == "localhost"
end
end
describe "PUT" do
it "new host should work" do
host_data = {:name => "host99.domain.com", :status => "down"}.to_json
put '/h/host99.domain.com', host_data, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["id"].nil?.should == false
response["name"].should == "host99.domain.com"
response["status"].should == "down"
response["new_record"].should == true
end
it "existing host should work" do
sleep 3
host_data = {:name => "host99.domain.com", :status => "pending"}.to_json
put '/h/host99.domain.com', host_data, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["new_record"].should == false
end
it "host missing name parameter should not work" do
host_data = {:status => "pending"}.to_json
put '/h/host100.domain.com', host_data, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "host missing status parameter should not work" do
host_data = {:name => "host100.domain.com"}.to_json
put '/h/host100.domain.com', host_data, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "host with invalid status parameter should not work" do
host_data = {:name => "host100.domain.com", :status => "fscked"}.to_json
put '/h/host100.domain.com', host_data, "CONTENT_TYPE" => "application/json"
last_response.should_not be_ok
response = last_response.should return_json
response["result"].should == "failure"
response["error_message"].should == "[[:status, :not_member]]"
end
end
describe "DELETE" do
before(:all) do
@h = Host.create(:name => 'h', :status => 'up')
sparms = {:name => 's', :status => "up"}
@h.services << Service.create(sparms.merge({:host => @h}))
@h.save
@s = @h.services.first
end
it "existing host should work" do
svc_size = @h.services.size
delete "/h/#{@h.name}"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["id"].should == @h.id
response["name"].should == @h.name
response["service_count"].should == svc_size.to_s
end
it "invalid host should not work" do
delete "/h/#{@h.name}"
last_response.should be_missing
end
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Using the Service API", :reset_redis => false, :populate_sample_data => true do
before(:all) do
@sample_host = {:name => 'rspec_sample_host', :status => 'up'}
@sample_service = {:name => 'rspec_sample_service', :status => 'up'}
@h = Host.create(:name => 'rspec_sample_host', :status => 'up')
@h.services << Service.create({:host => @h}.merge(@sample_service))
@h.save
@s = Service.find(@sample_service).first
end
describe "calling" do
describe "GET" do
it "all services should work" do
get '/s'
last_response.should be_ok
response = last_response.should return_json
response.is_a?(Array).should == true
end
it "all named services should work" do
get "/s/#{@sample_service[:name]}"
last_response.should be_ok
response = last_response.should return_json
response.is_a?(Array).should == true
s = response.first
s["id"].should == @s.id
s["name"].should == @s.name
s["status"].should == @s.status
s["host"].should == @h.name
end
it "named service for host should work" do
get "/s/#{@sample_service[:name]}/#{@sample_host[:name]}"
last_response.should be_ok
response = last_response.should return_json
response["id"].should == @s.id
response["name"].should == @s.name
response["status"].should == @s.status
response["host"].should == @h.name
end
it "missing service for host should not work" do
get '/s/foobar/baz'
last_response.should be_missing
end
end
describe "PUT" do
before(:all) do
@payload = {:name => 'another_rspec_service', :status => 'up', :host => @h.name}
end
it "new service should work" do
put "/s/#{@payload[:name]}/", @payload.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "create"
response["id"].nil?.should == false
response["name"].should == @payload[:name]
response["host"].should == @payload[:host]
Service.find(:name => @payload[:name]).size.should == 1
Service.find(:name => @payload[:name]).first.is_new?.should == true
end
it "new service without host should not work" do
put "/s/foobar", {:name => "foobar", :status => "up"}.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "new service with invalid status should not work" do
put "/s/foobar", {:name => "foobar", :status => "fsck", :host => @h.name}.to_json, "CONTENT_TYPE" => "application/json"
last_response.should_not be_ok
response = last_response.should return_json
response["error_message"].should == "[[:status, :not_member]]"
end
it "new service with missing name should not work" do
put "/s/foobar", {:status => "fsck", :host => @h.name}.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "new service with missing status should not work" do
put "/s/foobar", {:name => "foobar", :host => @h.name}.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_invalid
end
it "existing service should work" do
sleep 3
put "/s/#{@payload[:name]}", {:name => @payload[:name], :status => "down", :host => @payload[:host]}.to_json, "CONTENT_TYPE" => "application/json"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "update"
response["id"].nil?.should == false
response["name"].should == @payload[:name]
response["host"].should == @payload[:host]
Service.find(:name => @payload[:name]).size.should == 1
Service.find(:name => @payload[:name]).first.is_new?.should == false
end
end
describe "DELETE" do
before(:all) do
@h = Host.create(:name => "h1", :status => "up")
@h.services << Service.create(:name => "s1", :status => "up", :host => @h)
@h.save
@s = @h.services.first
end
it "existing host should work" do
delete "/s/#{@s.name}/#{@h.name}"
last_response.should be_ok
response = last_response.should return_json
response["result"].should == "success"
response["action"].should == "delete"
response["id"].should == @s.id
response["host"].should == @h.name
response["service"].should == @s.name
end
it "invalid host should not work" do
delete "/s/#{@s.name}/#{@h.name}"
last_response.should be_missing
end
end
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Noah App Basics", :reset_redis => true do
it "should show the index page" do
get '/'
last_response.should be_ok
last_response.body.include?("Noah Start Page").should == true
end
it "should test the 404 message" do
get '/foo'
last_response.status.should == 404
last_response.headers["Content-Type"].should == "application/json"
response = JSON.parse(last_response.body)
response["result"].should == "failure"
response["error_message"].should == "Resource not found"
end
end
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Noah Service Model", :reset_redis => true do
describe "should" do
it "create a new Service" do
servicename = "myservice"
servicestatus = "up"
hostname = "mytesthost1"
hoststatus = "up"
host = Host.create(:name => hostname, :status => hoststatus)
host.save
service = Service.create(:name => servicename, :status => servicestatus, :host => host)
service.valid?.should == true
service.save
service.name.should == servicename
service.status.should == servicestatus
service.host_id.should == host.id
host.services[1].name.should == servicename
end
it "create a new Service with find_or_create" do
host = Host.create(:name => "h1", :status => "up")
host.save
service = Service.find_or_create(:name => "s1", :status => "up", :host => host)
service.save
service.is_new?.should == true
end
it "update an existing Service with find_or_create" do
host = Host.create(:name => "h2", :status => "up")
host.save
service = Service.find_or_create(:name => "s2", :status => "up", :host => host)
service.save
sleep 1
service2 = Service.find_or_create(:name => "s2", :status => "up", :host => host)
service2.save
service2.is_new?.should == false
end
it "delete a Service" do
h = Host.create(:name => "h1", :status => "up")
h.save
s = Service.create(:name => "s1", :status => "up", :host => h)
s.save
s = Service.find(:name => "s1").first
s.delete
s = Service.find(:name => "s1").first
s.should == nil
end
it "find multiple Services" do
h = Host.create(:name => "h1", :status => "up")
if h.valid?
h.services << Service.create(:name => "s1", :status => "up", :host => h)
h.services << Service.create(:name => "s2", :status => "up", :host => h)
h.save
end
Services.all.size.should == 2
Services.all.first.name.should == "s1"
Services.all.last.name.should == "s2"
end
end
describe "should not" do
it "create a new Service when missing a Host" do
servicename = "myservice1"
servicestatus = "up"
service = Service.create(:name => servicename, :status => servicestatus)
service.valid?.should == false
service.errors.should == [[:host_id, :not_present]]
end
it "create a new Service when missing a name" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:status => "up", :host => host)
service.valid?.should == false
service.errors.should == [[:name, :not_present]]
end
it "create a new Service when missing a status" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:name => 'foo', :host => host)
service.valid?.should == false
service.errors.should == [[:status, :not_present], [:status, :not_member]]
end
it "create a new Service with an invalid status" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:name => "myservice", :status => "invalid_status", :host => host)
service.valid?.should == false
service.errors.should == [[:status, :not_member]]
end
it "create a duplicate Service" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
s = Service.create(:name => "myservice", :status => "up", :host => host)
s.save
s1 = Service.create(:name => "myservice", :status => "up", :host => host)
s1.valid?.should == false
s1.errors.should == [[[:name, :host_id], :not_unique]]
end
end
end
require 'bundler/setup'
require 'ohm'
begin
require 'yajl'
rescue LoadError
require 'json'
end
ENV['RACK_ENV'] = 'test'
ENV['REDIS_URL'] = 'redis://localhost:6379/3'
Ohm::connect
require File.join(File.dirname(__FILE__), '..', 'lib', 'noah')
require File.join(File.dirname(__FILE__), '..', 'lib', 'noah', 'app')
require 'rspec'
require 'rack/test'
RSpec.configure do |config|
config.color_enabled = true
config.formatter = "documentation"
config.before(:each, :reset_redis => true) { Ohm::redis.flushdb }
config.after(:each, :reset_redis => true) {Ohm::redis.flushdb }
config.after(:all, :populate_sample_data => true) {Ohm::redis.flushdb }
config.before(:all, :populate_sample_data => true) do
Ohm::redis.flushdb
h = Host.create(:name => 'localhost', :status => "up")
if h.save
%w[redis noah].each do |service|
s = Service.create(:name => service, :status => "up", :host => h)
h.services << s
end
end
a = Application.create(:name => 'noah')
if a.save
cr = Configuration.create(:name => 'redis', :format => 'string', :body => 'redis://127.0.0.1:6379/0', :application => a)
ch = Configuration.create(:name => 'host', :format => 'string', :body => 'localhost', :application => a)
cp = Configuration.create(:name => 'port', :format => 'string', :body => '9292', :application => a)
%w[cr ch cp].each do |c|
a.configurations << eval(c)
end
end
my_yaml = <<EOY
development:
database: development_database
adapter: mysql
username: dev_user
password: dev_password
EOY
my_json = <<EOJ
{
"id":"hostname",
"data":"localhost"
}
EOJ
a1 = Application.create(:name => 'myrailsapp1')
if a1.save
c1 = Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
a1.configurations << c1
end
a2 = Application.create(:name => 'myrestapp1')
if a2.save
c2 = Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
a2.configurations << c2
end
end
config.include Rack::Test::Methods
end
def app
Noah::App
end
RSpec::Matchers.define :return_json do
match do |last_response|
last_response.headers["Content-Type"].should == "application/json"
response = JSON.parse(last_response.body)
end
failure_message_for_should do
"Response was not valid JSON"
end
end
RSpec::Matchers.define :be_missing do
match do |last_response|
last_response.headers["Content-Type"].should == "application/json"
last_response.status.should == 404
response = JSON.parse(last_response.body)
response["result"].should == "failure"
response["error_message"].should == "Resource not found"
end
end
RSpec::Matchers.define :be_invalid do
match do |last_response|
last_response.headers["Content-Type"].should == "application/json"
response = JSON.parse(last_response.body)
response["result"].should == "failure"
response["error_message"].should == "Missing Parameters"
end
end
<%= {:status => "success"}.merge(api_call_results).to_json %>
{"result":"failure","error_message":"Resource not found"}
{"result":"failure","error_message":"<%= request.env['sinatra.error'].message %>"}
%html
%head
%title Noah Start Page
#header
%h1 Sample links
#header
%h2 Hosts
%ul
%li
%a{:href => "/h/"} All registered Hosts
%li
%a{:href => "/h/localhost"} localhost (this server)
%li
%a{:href => "/h/localhost/noah"} localhost noah service
#header
%h2 Services
%ul
%li
%a{:href => "/s/"} All registered Services
%li
%a{:href => "/s/http"} All hosts providing 'http'
%li
%a{:href => "/s/noah/localhost"} localhost noah service
#header
%h2 Applications
%ul
%li
%a{:href => "/a/"} All registered Applications
%li
%a{:href => "/a/noah"} Noah Application entry
%li
%a{:href => "/a/noah/redis"} Noah Redis configuration entry
#header
%h2 Configurations
%ul
%li
%a{:href => "/c/"} All registered Configurations
%li
%a{:href => "/c/noah"} Noah Configuration entry
%li
%a{:href => "/c/myrailsapp1"} myrailsapp1 Configuration entry
%li
%a{:href => "/c/myrestapp1"} myrestapp1 Configuration entry
%li
%a{:href => "/c/myrailsapp1/database.yml"} database.yml file for myrailsapp1 (should return the proper content-type)
%li
%a{:href => "/c/myrestapp1/config.json"} config.json file for myrestapp1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment