ananke 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "sinatra", "~>1.1.2"
4
+
5
+ group :development, :test do
6
+ gem "colored", "~>1.2"
7
+ gem "json", "~>1.5.1"
8
+ gem "rack-test", "~>0.5.6"
9
+ gem "rake", "~>0.8.7"
10
+ gem "rspec", "~>2.5.0"
11
+ gem 'simplecov', '~>0.3.9'
12
+ end
13
+
14
+
15
+
data/README.rdoc ADDED
@@ -0,0 +1,114 @@
1
+ = Ananke
2
+
3
+ Ananke is a DSL that extends the functionality of Sinatra for easy creation of Restful Services and Resources:
4
+
5
+ #myapp.rb
6
+ require 'ananke'
7
+ require 'sinatra/main' #This is only for Demo purposes
8
+ #--------------------Repositories---------------------
9
+ module Repository
10
+ module User
11
+ @data = [{:id => '1', :name => 'One'}, {:id => '2', :name => 'Two'}]
12
+ def self.all
13
+ @data.to_s
14
+ end
15
+ def self.one(id)
16
+ index = @data.index{ |d| d[:id] == id}
17
+ (index.nil? && '') || @data[index].to_s
18
+ end
19
+ end
20
+ end
21
+ #-------------------REST Resources--------------------
22
+ rest :user do
23
+ id :id
24
+ end
25
+
26
+ Install the gem and run with:
27
+
28
+ gem install ananke
29
+ ruby -rubygems myapp.rb
30
+
31
+ All Users
32
+ http://localhost:4567/user
33
+ One User
34
+ http://localhost:4567/user/1
35
+
36
+ == REST Resources
37
+ `rest` defines a complete Resource, and constructs Sinatra Routes
38
+ based on what's available in it's respective Repository. Routes are:
39
+
40
+ get '/name/?' -> Repository::Capitalize(name).all
41
+ get '/name/id' -> Repository::Capitalize(name).one(id)
42
+ post '/name' -> Repository::Capitalize(name).new(data)
43
+ put '/name/id' -> Repository::Capitalize(name).edit(id, data)
44
+ delete '/name/id' -> Repository::Capitalize(name).delete(id)
45
+
46
+ The REST media type can be built up:
47
+
48
+ required :name
49
+ optional :country
50
+
51
+ == Repositories
52
+ The Default Repository can be changed:
53
+
54
+ ananke.default_repository = 'MyRepository'
55
+
56
+ == Validation
57
+ Validation can be added on any field by providing arguments after a field declaration:
58
+
59
+ required :name, :length => 4
60
+
61
+ This will cause the paramater to be validated against the method defined in Ananke::Rules. Custom Rules
62
+ can be added to the module and provided as arguments. Current Default included rules are:
63
+
64
+ length(min)
65
+
66
+ Validation Methods are Invoked in the Ananke::Rules context, and has access to a class variable named
67
+ value, which holds the value for the currently valuated Parameter.
68
+
69
+ To Add a Custom Rule:
70
+
71
+ rule :name, do
72
+ value == [expected] ? nil : 'Not Expected Value'
73
+ end
74
+
75
+ required :name, :name
76
+
77
+ or
78
+
79
+ module Ananke
80
+ module Rules
81
+ def validate_name
82
+ value == [expected] ? nil : 'Not Expected Value'
83
+ end
84
+ end
85
+ end
86
+
87
+ required :name, :name
88
+
89
+ or Advanced
90
+
91
+ module Ananke
92
+ module Rules
93
+ def validate_name(extra)
94
+ value == [expected using extra] ? nil : 'Not Expected Value'
95
+ end
96
+ end
97
+ end
98
+
99
+ required :name, :name => extra
100
+
101
+ == Future
102
+ A short list of future development:
103
+ - Refactor!
104
+ - Return Value Strategy
105
+ - Resource Exposes Media Type
106
+ - HyperMedia
107
+ - Lots more `bullet-proofing`
108
+ - ETag Support
109
+
110
+ == More
111
+
112
+ * {Project Website}[http://https://github.com/HasAndries/ananke/]
113
+ * {Issue tracker}[https://github.com/HasAndries/ananke/issues]
114
+ * {Twitter}[http://twitter.com/HasAndries]
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rubygems'
2
+ require "rake"
3
+ require "rake/rdoctask"
4
+ require 'rake/gempackagetask'
5
+ require "rspec/core/rake_task"
6
+
7
+ require File.expand_path("../lib/version", __FILE__)
8
+ gemspec = Gem::Specification.new do |gem|
9
+ gem.name = "ananke"
10
+ gem.version = Ananke::VERSION
11
+ gem.platform = Gem::Platform::RUBY
12
+ gem.authors = ["Andries Coetzee"]
13
+ gem.email = "andriesc@mixtel.com"
14
+ gem.summary = "#{gem.name}-#{Ananke::VERSION}"
15
+ gem.description = "Full REST Implementation on top of Sinatra"
16
+ gem.homepage = "https://github.com/HasAndries/ananke"
17
+
18
+ gem.rubygems_version = "1.5.0"
19
+
20
+ gem.files = FileList['lib/**/*', 'spec/**/*', 'Gemfile', 'Rakefile', 'README.rdoc']
21
+ gem.test_files = FileList['spec/**/*']
22
+ gem.extra_rdoc_files = [ "README.rdoc" ]
23
+ gem.rdoc_options = ["--charset=UTF-8"]
24
+ gem.require_path = "lib"
25
+
26
+ gem.post_install_message = %Q{**************************************************
27
+
28
+ Thank you for installing #{gem.summary}
29
+
30
+ Please be sure to look at README.rdoc to see what might have changed
31
+ since the last release and how to use this GEM.
32
+
33
+ **************************************************
34
+ }
35
+ gem.add_dependency "sinatra", "~> 1.1.2"
36
+
37
+ gem.add_development_dependency "colored", "~> 1.2"
38
+ gem.add_development_dependency "json", "~> 1.5.1"
39
+ gem.add_development_dependency "rack-test", "~> 0.5.6"
40
+ gem.add_development_dependency "rake", "~> 0.8.7"
41
+ gem.add_development_dependency "rspec", "~> 2.5.0"
42
+ gem.add_development_dependency "simplecov", "~> 0.3.9"
43
+ end
44
+
45
+ Rake::GemPackageTask.new(gemspec) do |pkg|
46
+ pkg.need_tar = true
47
+ end
48
+
49
+ desc %{Build the gemspec file.}
50
+ task :gemspec do
51
+ gemspec.validate
52
+ File.open("#{gemspec.name}.gemspec", 'w'){|f| f.write gemspec.to_ruby }
53
+ end
54
+
55
+ RSpec::Core::RakeTask.new(:test) do |t|
56
+ t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
57
+ t.pattern = 'spec/**/*_spec.rb'
58
+ end
59
+
60
+ RSpec::Core::RakeTask.new(:doc) do |t|
61
+ t.rspec_opts = ["-c" "-r ./spec/nice_formatter.rb", "-f NiceFormatter", "-o doc.htm"]
62
+ t.pattern = 'spec/**/*_spec.rb'
63
+ end
64
+
65
+ task :default => [:test]
data/lib/ananke.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'sinatra/base'
2
+
3
+ module Ananke
4
+ class << self
5
+ attr_accessor :default_repository, :rules
6
+ end
7
+
8
+ private
9
+ @default_repository = 'Repository'
10
+ @rules = [:length]
11
+
12
+ def build(path)
13
+ #TODO - Check if Modules Exist
14
+ mod = Module.const_get(Ananke.default_repository.to_sym).const_get("#{path.capitalize}".to_sym)
15
+ key = @id[:key]
16
+ fields = @fields
17
+ links = @links
18
+
19
+ #TODO - Check if Repository Supports Resource
20
+ Sinatra::Base.get "/#{path}/:#{key}" do
21
+ ret = mod.one(params[key]) if !params[key].nil?
22
+ #TODO - Hyper Links(Common place maybe?)
23
+ ret.respond_to?(:to_json) ? ret.to_json : ret
24
+ end
25
+
26
+ #TODO - Check if Repository Supports Resource
27
+ Sinatra::Base.get "/#{path}/?" do
28
+ ret = mod.all
29
+ #TODO - Hyper Links(Common place maybe?)
30
+ ret.respond_to?(:to_json) ? ret.to_json : ret
31
+ end
32
+
33
+ #TODO - Check if Repository Supports Resource
34
+ Sinatra::Base.post "/#{path}/?" do
35
+ #TODO - Parameter Validation
36
+ status, message = validate!(fields, params)
37
+ error status, message unless status.nil?
38
+ ret = mod.new(params)
39
+ status 201
40
+ #TODO - Hyper Links for Created Resource
41
+ end
42
+
43
+ #TODO - Check if Repository Supports Resource
44
+ Sinatra::Base.put "/#{path}/:#{key}" do
45
+ #TODO - Parameter Validation
46
+ mod.edit(params[key], params) if !params[key].nil?
47
+ status 200
48
+ #TODO - Hyper Links
49
+ end
50
+
51
+ #TODO - Check if Repository Supports Resource
52
+ Sinatra::Base.delete "/#{path}/:#{key}" do
53
+ #TODO - Parameter Validation
54
+ mod.delete(params[key]) if !params[key].nil?
55
+ status 200
56
+ end
57
+ end
58
+
59
+ def validate!(fields, params)
60
+ errors = []
61
+ fields.each do |field|
62
+ value = params[field[:key].to_s]
63
+ errors << "Missing Required Parameter: #{field[:key]}" if field[:type] == :required && value.nil?
64
+ Ananke::Rules.value = value
65
+ field[:rules].each do |r|
66
+ res = r.class == Hash ? Ananke::Rules.send("validate_#{r.first[0]}", r.first[1]) : Ananke::Rules.send("validate_#{r}")
67
+ errors << "#{field[:key]}: #{res}" unless res.nil?
68
+ end
69
+ end
70
+ return 400, errors unless errors.empty?
71
+ end
72
+
73
+ public
74
+
75
+ def rest(path, &block)
76
+ @id = {}
77
+ @fields = []
78
+ @links = []
79
+ yield block
80
+ build path
81
+ end
82
+
83
+ def id(key, *rules)
84
+ @id = {:key => key, :type => :id, :rules => rules}
85
+ end
86
+ def required(key, *rules)
87
+ @fields << {:key => key, :type => :required, :rules => rules}
88
+ end
89
+ def optional(key, *rules)
90
+ @fields << {:key => key, :type => :optional, :rules => rules}
91
+ end
92
+ def media(rel, method, resource, field)
93
+ @links << {:rel => rel, :method => method, :resource => resource, :field => field}
94
+ end
95
+ def rule(name, &block)
96
+ Ananke::Rules.send(:define_singleton_method, "validate_#{name}", block)
97
+ end
98
+
99
+ module Rules
100
+ class << self
101
+ attr_accessor :value
102
+ end
103
+ def self.validate_length(min)
104
+ value.length >= min ? nil : "Value must be at least #{min} characters long"
105
+ end
106
+ end
107
+ end
108
+
109
+ include Ananke
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Ananke
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.adapters.define 'cov' do
4
+ coverage_dir 'public/coverage'
5
+
6
+ add_filter '/dump/'
7
+ add_filter '/public/'
8
+ add_filter '/spec/'
9
+ end
@@ -0,0 +1,126 @@
1
+ require './spec/spec_helper'
2
+ require './lib/ananke'
3
+
4
+ describe 'Resource' do
5
+ include Rack::Test::Methods
6
+ include Ananke
7
+
8
+ def app
9
+ Sinatra::Base
10
+ end
11
+
12
+ before(:all) do
13
+ rest :user do
14
+ id :user_id
15
+ required :username
16
+ required 'email'
17
+ optional :country
18
+
19
+ media "Get All Vehicles", :get, :vehicles, :user_id
20
+ end
21
+ end
22
+
23
+ it """
24
+ Should setup the defaults for ReST
25
+ """ do
26
+ Ananke.default_repository.should == 'Repository'
27
+ end
28
+
29
+ it """
30
+ Should setup Routes
31
+ """ do
32
+ Sinatra::Base.routes["GET"][-1][0].inspect.include?('user').should == true
33
+ Sinatra::Base.routes["GET"].length.should == 2
34
+ Sinatra::Base.routes["POST"][-1][0].inspect.include?('user').should == true
35
+ Sinatra::Base.routes["PUT"][-1][0].inspect.include?('user').should == true
36
+ Sinatra::Base.routes["DELETE"][-1][0].inspect.include?('user').should == true
37
+ end
38
+
39
+ #----------------------------BASIC--------------------------------------
40
+ it """
41
+ GET /user
42
+ - code: 200
43
+ - content-type: text/plain
44
+ - body: [{:id=>1, :name=>'one'}, {:id => 2, :name => 'two'}]
45
+ """ do
46
+ get "/user"
47
+ last_response.status.should == 200
48
+ last_response.body.should == Repository::User.data.to_json
49
+ end
50
+
51
+ it """
52
+ GET /user/1
53
+ - code: 200
54
+ - content-type: text/plain
55
+ - body: {user_id: ,username: ,email: ,country: }
56
+ """ do
57
+ get "/user/1"
58
+ last_response.status.should == 200
59
+ last_response.body.should == Repository::User.data[0].to_json
60
+ end
61
+
62
+ it """
63
+ POST /user
64
+ - body: {user_id: ,username: ,email: ,country: }
65
+ RETURN
66
+ - code: 201
67
+ - content-type: text/json
68
+ - body:
69
+ """ do
70
+ post "/user", body={:user_id => 3, :username => 'three', :email => '3@three.com', :country => 'USA'}
71
+ last_response.status.should == 201
72
+ last_response.body.should == ''
73
+ end
74
+
75
+ it """
76
+ PUT /user/3
77
+ - body: {user_id: ,username: ,email: ,country: }
78
+ RETURN
79
+ - code: 200
80
+ - content-type: text/json
81
+ - body:
82
+ """ do
83
+ put "/user/3", body={:user_id => 3, :username => 'four', :email => '4@four.com', :country => 'Russia'}
84
+ last_response.status.should == 200
85
+ last_response.body.should == ''
86
+ end
87
+
88
+ it """
89
+ DELETE /user/3
90
+ RETURN
91
+ - code: 200
92
+ - content-type: text/json
93
+ - body:
94
+ """ do
95
+ delete "/user/3"
96
+ last_response.status.should == 200
97
+ last_response.body.should == ''
98
+ end
99
+ end
100
+
101
+ module Repository
102
+ module User
103
+ @data = [{:user_id => 1, :username => 'one', :email => '1@one.com', :country => 'South Africa'},
104
+ {:user_id => 2, :username => 'two', :email => '2@two.com', :country => 'England'}]
105
+
106
+ def self.data
107
+ @data
108
+ end
109
+
110
+ def self.one(id)
111
+ @data[@data.index{ |d| d[:user_id] == id.to_i}]
112
+ end
113
+ def self.all
114
+ @data
115
+ end
116
+ def self.new(data)
117
+ @data << data
118
+ end
119
+ def self.edit(id, data)
120
+ @data.each { |d| d = data if d[:user_id] == id}
121
+ end
122
+ def self.delete(id)
123
+ @data.delete_if { |i| i[:user_id] == id}
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,89 @@
1
+ require './spec/spec_helper'
2
+ require './lib/ananke'
3
+
4
+ describe 'Resource' do
5
+ include Rack::Test::Methods
6
+ include Ananke
7
+
8
+ def app
9
+ Sinatra::Base
10
+ end
11
+
12
+ #--------------------------VALIDATION-----------------------------------
13
+ it """
14
+ Should be able to use Predefined Validation:
15
+ length
16
+ """ do
17
+ module Repository
18
+ module Basic
19
+ def self.new(data)end
20
+ end
21
+ end
22
+ rest :basic do
23
+ id :user_id
24
+ required :username, :length => 4
25
+ end
26
+
27
+ post "/basic", body={:user_id => 1, :username => ''}
28
+ last_response.status.should == 400
29
+ last_response.body.should == 'username: Value must be at least 4 characters long'
30
+
31
+ post "/basic", body={:user_id => 1, :username => '1234'}
32
+ last_response.status.should == 201
33
+ end
34
+
35
+ it """
36
+ Should be able to use Explicitly Defined Rule
37
+ """ do
38
+ module Ananke
39
+ module Rules
40
+ def self.validate_email
41
+ value =~ /[\w._%-]+@[\w.-]+.[a-zA-Z]{2,4}/ ? nil : "Invalid Email: #{value}"
42
+ end
43
+ end
44
+ end
45
+ Ananke::Rules.respond_to?('validate_email').should == true
46
+
47
+ module Repository
48
+ module Explicit
49
+ def self.new(data)end
50
+ end
51
+ end
52
+ rest :explicit do
53
+ id :user_id
54
+ required :email, :email
55
+ end
56
+
57
+ post "/explicit", body={:user_id => 1, :email => 'some'}
58
+ last_response.status.should == 400
59
+ last_response.body.should == 'email: Invalid Email: some'
60
+
61
+ post "/explicit", body={:user_id => 1, :email => 'some1@some.com'}
62
+ last_response.status.should == 201
63
+ end
64
+
65
+ it """
66
+ Should be able to Add new Validations and Use them
67
+ """ do
68
+ rule :country, do
69
+ value == 'South Africa' ? nil : 'Not from South Africa'
70
+ end
71
+ module Repository
72
+ module Added
73
+ def self.new(data)end
74
+ end
75
+ end
76
+ rest :added do
77
+ id :user_id
78
+ required :country, :country
79
+ end
80
+ Ananke::Rules.respond_to?('validate_country').should == true
81
+
82
+ post "/added", body={:user_id => 1, :country => 'England'}
83
+ last_response.status.should == 400
84
+ last_response.body.should == 'country: Not from South Africa'
85
+
86
+ post "/added", body={:user_id => 1, :country => 'South Africa'}
87
+ last_response.status.should == 201
88
+ end
89
+ end
@@ -0,0 +1,346 @@
1
+ require 'erb'
2
+ require 'rspec/core/formatters/base_text_formatter'
3
+ require 'rspec/core/formatters/snippet_extractor'
4
+
5
+ class NiceFormatter < RSpec::Core::Formatters::BaseTextFormatter
6
+ include ERB::Util # for the #h method
7
+
8
+ def method_missing(m, *a, &b)
9
+ # no-op
10
+ end
11
+
12
+ def message(message)
13
+ end
14
+
15
+ def initialize(output)
16
+ super(output)
17
+ @example_group_number = 0
18
+ @example_number = 0
19
+ @header_red = nil
20
+ end
21
+
22
+ # The number of the currently running example_group
23
+ def example_group_number
24
+ @example_group_number
25
+ end
26
+
27
+ # The number of the currently running example (a global counter)
28
+ def example_number
29
+ @example_number
30
+ end
31
+
32
+ def start(example_count)
33
+ super(example_count)
34
+ @output.puts html_header
35
+ @output.puts report_header
36
+ @output.flush
37
+ end
38
+
39
+ def example_group_started(example_group)
40
+ super(example_group)
41
+ @example_group_red = false
42
+ @example_group_number += 1
43
+ unless example_group_number == 1
44
+ @output.puts " </dl>"
45
+ @output.puts "</div>"
46
+ end
47
+ @output.puts "<div class=\"example_group\">"
48
+ @output.puts " <dl>"
49
+ @output.puts " <dt id=\"example_group_#{example_group_number}\">#{example_group.description.gsub(/[\n]/, "<br />")}</dt>"
50
+ @output.flush
51
+ end
52
+
53
+ def start_dump
54
+ @output.puts " </dl>"
55
+ @output.puts "</div>"
56
+ @output.flush
57
+ end
58
+
59
+ def example_started(example)
60
+ super(example)
61
+ @example_number += 1
62
+ end
63
+
64
+ def example_passed(example)
65
+ move_progress
66
+ @output.puts " <dd class=\"spec passed\"><span class=\"passed_spec_name\">#{example.description.gsub(/[\n]/, "<br />")}</span></dd>"
67
+ @output.flush
68
+ end
69
+
70
+ def example_failed(example)
71
+ counter = 0
72
+ exception = example.metadata[:execution_result][:exception_encountered]
73
+ extra = extra_failure_content(exception)
74
+ failure_style = 'failed'
75
+ failure_style = RSpec::Core::PendingExampleFixedError === exception ? 'pending_fixed' : 'failed'
76
+ @output.puts " <script type=\"text/javascript\">makeRed('rspec-header');</script>" unless @header_red
77
+ @header_red = true
78
+ @output.puts " <script type=\"text/javascript\">makeRed('example_group_#{example_group_number}');</script>" unless @example_group_red
79
+ @example_group_red = true
80
+ move_progress
81
+ @output.puts " <dd class=\"spec #{failure_style}\">"
82
+ @output.puts " <span class=\"failed_spec_name\">#{h(example.description.gsub(/[\n]/, "<br />"))}</span>"
83
+ @output.puts " <div class=\"failure\" id=\"failure_#{counter}\">"
84
+ @output.puts " <div class=\"message\"><pre>#{h(exception.message)}</pre></div>" unless exception.nil?
85
+ @output.puts " <div class=\"backtrace\"><pre>#{format_backtrace(exception.backtrace, example).join("\n")}</pre></div>" if exception
86
+ @output.puts extra unless extra == ""
87
+ @output.puts " </div>"
88
+ @output.puts " </dd>"
89
+ @output.flush
90
+ end
91
+
92
+ def example_pending(example)
93
+ message = example.metadata[:execution_result][:pending_message]
94
+ @output.puts " <script type=\"text/javascript\">makeYellow('rspec-header');</script>" unless @header_red
95
+ @output.puts " <script type=\"text/javascript\">makeYellow('example_group_#{example_group_number}');</script>" unless @example_group_red
96
+ move_progress
97
+ @output.puts " <dd class=\"spec not_implemented\"><span class=\"not_implemented_spec_name\">#{example.description.gsub(/[\n]/, "<br />")} (PENDING: #{h(message)})</span></dd>"
98
+ @output.flush
99
+ end
100
+
101
+ # Override this method if you wish to output extra HTML for a failed spec. For example, you
102
+ # could output links to images or other files produced during the specs.
103
+ #
104
+ def extra_failure_content(exception)
105
+ require 'rspec/core/formatters/snippet_extractor'
106
+ @snippet_extractor ||= RSpec::Core::Formatters::SnippetExtractor.new
107
+ " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(exception)}</code></pre>"
108
+ end
109
+
110
+ def move_progress
111
+ @output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
112
+ @output.flush
113
+ end
114
+
115
+ def percent_done
116
+ result = 100.0
117
+ if @example_count > 0
118
+ result = ((example_number).to_f / @example_count.to_f * 1000).to_i / 10.0
119
+ end
120
+ result
121
+ end
122
+
123
+ def dump_failures
124
+ end
125
+
126
+ def dump_pending
127
+ end
128
+
129
+ def dump_summary(duration, example_count, failure_count, pending_count)
130
+ # TODO - kill dry_run?
131
+ if dry_run?
132
+ totals = "This was a dry-run"
133
+ else
134
+ totals = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
135
+ totals << ", #{pending_count} pending" if pending_count > 0
136
+ end
137
+ @output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{duration} seconds</strong>\";</script>"
138
+ @output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
139
+ @output.puts "</div>"
140
+ @output.puts "</div>"
141
+ @output.puts "</body>"
142
+ @output.puts "</html>"
143
+ @output.flush
144
+ end
145
+
146
+ def html_header
147
+ <<-EOF
148
+ <?xml version="1.0" encoding="UTF-8"?>
149
+ <!DOCTYPE html
150
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
151
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
152
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
153
+ <head>
154
+ <title>RSpec results</title>
155
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
156
+ <meta http-equiv="Expires" content="-1" />
157
+ <meta http-equiv="Pragma" content="no-cache" />
158
+ <style type="text/css">
159
+ body {
160
+ margin: 0;
161
+ padding: 0;
162
+ background: #fff;
163
+ font-size: 80%;
164
+ }
165
+ </style>
166
+ <script type="text/javascript">
167
+ // <![CDATA[
168
+ #{global_scripts}
169
+ // ]]>
170
+ </script>
171
+ <style type="text/css">
172
+ #{global_styles}
173
+ </style>
174
+ </head>
175
+ <body>
176
+ EOF
177
+ end
178
+
179
+ def report_header
180
+ <<-EOF
181
+ <div class="rspec-report">
182
+
183
+ <div id="rspec-header">
184
+ <div id="label">
185
+ <h1>RSpec Code Examples</h1>
186
+ </div>
187
+
188
+ <div id="summary">
189
+ <p id="totals">&nbsp;</p>
190
+ <p id="duration">&nbsp;</p>
191
+ </div>
192
+ </div>
193
+
194
+ <div class="results">
195
+ EOF
196
+ end
197
+
198
+ def global_scripts
199
+ <<-EOF
200
+ function moveProgressBar(percentDone) {
201
+ document.getElementById("rspec-header").style.width = percentDone +"%";
202
+ }
203
+ function makeRed(element_id) {
204
+ document.getElementById(element_id).style.background = '#C40D0D';
205
+ document.getElementById(element_id).style.color = '#FFFFFF';
206
+ }
207
+
208
+ function makeYellow(element_id) {
209
+ if (element_id == "rspec-header" && document.getElementById(element_id).style.background != '#C40D0D')
210
+ {
211
+ document.getElementById(element_id).style.background = '#FAF834';
212
+ document.getElementById(element_id).style.color = '#000000';
213
+ }
214
+ else
215
+ {
216
+ document.getElementById(element_id).style.background = '#FAF834';
217
+ document.getElementById(element_id).style.color = '#000000';
218
+ }
219
+ }
220
+ EOF
221
+ end
222
+
223
+ def global_styles
224
+ <<-EOF
225
+ #rspec-header {
226
+ background: #65C400; color: #fff; height: 4em;
227
+ }
228
+
229
+ .rspec-report h1 {
230
+ margin: 0px 10px 0px 10px;
231
+ padding: 10px;
232
+ font-family: "Lucida Grande", Helvetica, sans-serif;
233
+ font-size: 1.8em;
234
+ position: absolute;
235
+ }
236
+
237
+ #summary {
238
+ margin: 0; padding: 5px 10px;
239
+ font-family: "Lucida Grande", Helvetica, sans-serif;
240
+ text-align: right;
241
+ top: 0px;
242
+ right: 0px;
243
+ float:right;
244
+ }
245
+
246
+ #summary p {
247
+ margin: 0 0 0 2px;
248
+ }
249
+
250
+ #summary #totals {
251
+ font-size: 1.2em;
252
+ }
253
+
254
+ .example_group {
255
+ margin: 0 10px 5px;
256
+ background: #fff;
257
+ }
258
+
259
+ dl {
260
+ margin: 0; padding: 0 0 5px;
261
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
262
+ }
263
+
264
+ dt {
265
+ padding: 3px;
266
+ background: #65C400;
267
+ color: #fff;
268
+ font-weight: bold;
269
+ }
270
+
271
+ dd {
272
+ margin: 5px 0 5px 5px;
273
+ padding: 3px 3px 3px 18px;
274
+ }
275
+
276
+ dd.spec.passed {
277
+ border-left: 5px solid #65C400;
278
+ border-bottom: 1px solid #65C400;
279
+ background: #DBFFB4; color: #3D7700;
280
+ }
281
+
282
+ dd.spec.failed {
283
+ border-left: 5px solid #C20000;
284
+ border-bottom: 1px solid #C20000;
285
+ color: #C20000; background: #FFFBD3;
286
+ }
287
+
288
+ dd.spec.not_implemented {
289
+ border-left: 5px solid #FAF834;
290
+ border-bottom: 1px solid #FAF834;
291
+ background: #FCFB98; color: #131313;
292
+ }
293
+
294
+ dd.spec.pending_fixed {
295
+ border-left: 5px solid #0000C2;
296
+ border-bottom: 1px solid #0000C2;
297
+ color: #0000C2; background: #D3FBFF;
298
+ }
299
+
300
+ .backtrace {
301
+ color: #000;
302
+ font-size: 12px;
303
+ }
304
+
305
+ a {
306
+ color: #BE5C00;
307
+ }
308
+
309
+ /* Ruby code, style similar to vibrant ink */
310
+ .ruby {
311
+ font-size: 12px;
312
+ font-family: monospace;
313
+ color: white;
314
+ background-color: black;
315
+ padding: 0.1em 0 0.2em 0;
316
+ }
317
+
318
+ .ruby .keyword { color: #FF6600; }
319
+ .ruby .constant { color: #339999; }
320
+ .ruby .attribute { color: white; }
321
+ .ruby .global { color: white; }
322
+ .ruby .module { color: white; }
323
+ .ruby .class { color: white; }
324
+ .ruby .string { color: #66FF00; }
325
+ .ruby .ident { color: white; }
326
+ .ruby .method { color: #FFCC00; }
327
+ .ruby .number { color: white; }
328
+ .ruby .char { color: white; }
329
+ .ruby .comment { color: #9933CC; }
330
+ .ruby .symbol { color: white; }
331
+ .ruby .regex { color: #44B4CC; }
332
+ .ruby .punct { color: white; }
333
+ .ruby .escape { color: white; }
334
+ .ruby .interp { color: white; }
335
+ .ruby .expr { color: white; }
336
+
337
+ .ruby .offending { background-color: gray; }
338
+ .ruby .linenum {
339
+ width: 75px;
340
+ padding: 0.1em 1em 0.2em 0;
341
+ color: #000000;
342
+ background-color: #FFFBD3;
343
+ }
344
+ EOF
345
+ end
346
+ end
@@ -0,0 +1,17 @@
1
+ #=========================CODE COVERAGE========================
2
+ require './spec/cov_adapter'
3
+ SimpleCov.start 'cov'
4
+
5
+ #===========================REQUIRES===========================
6
+ require 'colored'
7
+ require 'json'
8
+ require 'rack'
9
+ require 'rspec'
10
+ require 'rack/test'
11
+
12
+ extend Colored
13
+
14
+ #==================SETUP TEST ENVIRONMENT======================
15
+ ENV['RACK_ENV'] = 'test'
16
+
17
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), ".."))
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ananke
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Andries Coetzee
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-07 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: sinatra
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 1.1.2
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: colored
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: "1.2"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: json
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 1.5.1
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rack-test
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 0.5.6
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rake
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.7
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 2.5.0
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: simplecov
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 0.3.9
91
+ type: :development
92
+ version_requirements: *id007
93
+ description: Full REST Implementation on top of Sinatra
94
+ email: andriesc@mixtel.com
95
+ executables: []
96
+
97
+ extensions: []
98
+
99
+ extra_rdoc_files:
100
+ - README.rdoc
101
+ files:
102
+ - lib/ananke.rb
103
+ - lib/version.rb
104
+ - spec/cov_adapter.rb
105
+ - spec/lib/ananke_spec.rb
106
+ - spec/lib/validation_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/nice_formatter.rb
109
+ - Gemfile
110
+ - Rakefile
111
+ - README.rdoc
112
+ has_rdoc: true
113
+ homepage: https://github.com/HasAndries/ananke
114
+ licenses: []
115
+
116
+ post_install_message: |
117
+ **************************************************
118
+
119
+ Thank you for installing ananke-0.0.1
120
+
121
+ Please be sure to look at README.rdoc to see what might have changed
122
+ since the last release and how to use this GEM.
123
+
124
+ **************************************************
125
+
126
+ rdoc_options:
127
+ - --charset=UTF-8
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: "0"
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: "0"
142
+ requirements: []
143
+
144
+ rubyforge_project:
145
+ rubygems_version: 1.5.0
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: ananke-0.0.1
149
+ test_files:
150
+ - spec/cov_adapter.rb
151
+ - spec/lib/ananke_spec.rb
152
+ - spec/lib/validation_spec.rb
153
+ - spec/spec_helper.rb
154
+ - spec/nice_formatter.rb