mosquito 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +25 -0
- data/README +104 -0
- data/Rakefile +14 -0
- data/lib/mosquito.rb +215 -0
- data/public/blog.rb +58 -0
- data/public/blog/controllers.rb +107 -0
- data/public/blog/models.rb +12 -0
- data/public/blog/views.rb +115 -0
- data/public/homepage.rb +64 -0
- data/test/fixtures/blog_comments.yml +6 -0
- data/test/fixtures/blog_posts.yml +11 -0
- data/test/fixtures/blog_users.yml +6 -0
- data/test/test_blog.rb +110 -0
- data/test/test_homepage.rb +23 -0
- metadata +75 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2005 Geoffrey Grosenbach boss@topfunky.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Rakefile
|
2
|
+
README
|
3
|
+
MIT-LICENSE
|
4
|
+
Manifest.txt
|
5
|
+
lib
|
6
|
+
lib/mosquito.rb
|
7
|
+
public
|
8
|
+
|
9
|
+
public/blog
|
10
|
+
public/blog.rb
|
11
|
+
public/blog/controllers.rb
|
12
|
+
public/blog/models.rb
|
13
|
+
public/blog/views.rb
|
14
|
+
|
15
|
+
public/homepage.rb
|
16
|
+
|
17
|
+
test
|
18
|
+
test/test_blog.rb
|
19
|
+
|
20
|
+
test/test_homepage.rb
|
21
|
+
|
22
|
+
test/fixtures
|
23
|
+
test/fixtures/blog_comments.yml
|
24
|
+
test/fixtures/blog_posts.yml
|
25
|
+
test/fixtures/blog_users.yml
|
data/README
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
Mosquito, for Bug-Free Camping
|
2
|
+
=============================
|
3
|
+
|
4
|
+
A testing helper for those times when you go Camping.
|
5
|
+
|
6
|
+
Apply on the face, neck, and any exposed areas such as your Models and Controllers.
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
Make a few files and directories like this:
|
11
|
+
|
12
|
+
public/
|
13
|
+
blog.rb
|
14
|
+
test/
|
15
|
+
test_blog.rb
|
16
|
+
fixtures/
|
17
|
+
blog_comments.yml
|
18
|
+
blog_posts.yml
|
19
|
+
blog_users.yml
|
20
|
+
|
21
|
+
Setup +test_blog.rb+ like this:
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'mosquito'
|
25
|
+
require File.dirname(__FILE__) + "/../public/blog"
|
26
|
+
|
27
|
+
Blog.create
|
28
|
+
include Blog::Models
|
29
|
+
|
30
|
+
class TestBlog < Camping::FunctionalTest
|
31
|
+
|
32
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
33
|
+
|
34
|
+
def setup
|
35
|
+
super
|
36
|
+
# Do other stuff here
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_index
|
40
|
+
get
|
41
|
+
assert_response :success
|
42
|
+
assert_match_body %r!>blog<!
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_view
|
46
|
+
get '/view/1'
|
47
|
+
assert_response :success
|
48
|
+
assert_match_body %r!The quick fox jumped over the lazy dog!
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# A unit test
|
54
|
+
class TestPost < Camping::UnitTest
|
55
|
+
|
56
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
57
|
+
|
58
|
+
def test_create
|
59
|
+
post = Post.create( :user_id => 1,
|
60
|
+
:title => "Title",
|
61
|
+
:body => "Body")
|
62
|
+
assert post.valid?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_assoc
|
66
|
+
post = Post.find :first
|
67
|
+
assert_kind_of User, post.user
|
68
|
+
assert_equal 1, post.user.id
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
== Details
|
75
|
+
|
76
|
+
Inherit from +Camping::FunctionalTest+ or +Camping::UnitTest+. If you define +setup+, be sure to call +super+ so the parent class can do its thing.
|
77
|
+
|
78
|
+
You should also call the +MyApp.create+ method if you have one. You will also need to +include MyApp::Models+ at the top of your test file if you want to use Models in your assertions.
|
79
|
+
|
80
|
+
Make fixtures in +test/fixtures+. Remember that Camping models use the name of the mount plus the model name: +blog_posts+ for the +Post+ model.
|
81
|
+
|
82
|
+
See +blog_test.rb+ for an example of both Functional and Unit tests.
|
83
|
+
|
84
|
+
== Warning: Your are Camping, not Rail-riding
|
85
|
+
|
86
|
+
Test files start with +test_+ (test_blog.rb). Test classes start with +Test+ (TestBlog).
|
87
|
+
|
88
|
+
Model and Controller test classes can both go in the same file.
|
89
|
+
|
90
|
+
A Sqlite3 :memory: database is automatically used for tests that require a database.
|
91
|
+
|
92
|
+
You can run your tests by executing the test file with Ruby or by running the autotest command with no arguments (from the ZenTest gem).
|
93
|
+
|
94
|
+
ruby test/test_blog.rb
|
95
|
+
|
96
|
+
or
|
97
|
+
|
98
|
+
autotest
|
99
|
+
|
100
|
+
|
101
|
+
== Author
|
102
|
+
|
103
|
+
Geoffrey Grosenbach http://nubyonrails.com
|
104
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require 'lib/mosquito'
|
4
|
+
|
5
|
+
Hoe.new('Mosquito', Mosquito::VERSION) do |p|
|
6
|
+
p.name = "mosquito"
|
7
|
+
p.author = "Geoffrey Grosenbach"
|
8
|
+
p.description = "A library for writing tests for your Camping app."
|
9
|
+
p.email = 'boss@topfunky.com'
|
10
|
+
p.summary = "A Camping test library."
|
11
|
+
p.url = "http://mosquito.rubyforge.org"
|
12
|
+
end
|
13
|
+
|
14
|
+
|
data/lib/mosquito.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
%w(
|
2
|
+
rubygems
|
3
|
+
test/unit
|
4
|
+
active_record
|
5
|
+
active_record/fixtures
|
6
|
+
active_support/binding_of_caller
|
7
|
+
camping
|
8
|
+
fileutils
|
9
|
+
stringio
|
10
|
+
cgi
|
11
|
+
).each { |lib| require lib }
|
12
|
+
|
13
|
+
module Mosquito
|
14
|
+
VERSION = '0.1.0'
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
18
|
+
ActiveRecord::Base.logger = Logger.new("test/test.log")
|
19
|
+
|
20
|
+
Test::Unit::TestCase.fixture_path = "test/fixtures/"
|
21
|
+
|
22
|
+
class Test::Unit::TestCase #:nodoc:
|
23
|
+
def create_fixtures(*table_names)
|
24
|
+
if block_given?
|
25
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
26
|
+
else
|
27
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
32
|
+
self.use_transactional_fixtures = true
|
33
|
+
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
|
34
|
+
self.use_instantiated_fixtures = false
|
35
|
+
end
|
36
|
+
|
37
|
+
class MockRequest
|
38
|
+
def initialize
|
39
|
+
@headers = {
|
40
|
+
'SERVER_NAME' => 'localhost',
|
41
|
+
'PATH_INFO' => '',
|
42
|
+
'HTTP_ACCEPT_ENCODING' => 'gzip,deflate',
|
43
|
+
'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060214 Camino/1.0',
|
44
|
+
'SCRIPT_NAME' => '/',
|
45
|
+
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
46
|
+
'HTTP_CACHE_CONTROL' => 'max-age=0',
|
47
|
+
'HTTP_ACCEPT_LANGUAGE' => 'en,ja;q=0.9,fr;q=0.9,de;q=0.8,es;q=0.7,it;q=0.7,nl;q=0.6,sv;q=0.5,nb;q=0.5,da;q=0.4,fi;q=0.3,pt;q=0.3,zh-Hans;q=0.2,zh-Hant;q=0.1,ko;q=0.1',
|
48
|
+
'HTTP_HOST' => 'localhost',
|
49
|
+
'REMOTE_ADDR' => '127.0.0.1',
|
50
|
+
'SERVER_SOFTWARE' => 'Mongrel 0.3.12.4',
|
51
|
+
'HTTP_KEEP_ALIVE' => '300',
|
52
|
+
'HTTP_REFERER' => 'http://localhost/',
|
53
|
+
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
54
|
+
'HTTP_VERSION' => 'HTTP/1.1',
|
55
|
+
'REQUEST_URI' => '/',
|
56
|
+
'SERVER_PORT' => '80',
|
57
|
+
'GATEWAY_INTERFACE' => 'CGI/1.2',
|
58
|
+
'HTTP_ACCEPT' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
|
59
|
+
'HTTP_CONNECTION' => 'keep-alive',
|
60
|
+
'REQUEST_METHOD' => 'GET',
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def set(key, val)
|
65
|
+
@headers[key] = val
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_hash
|
69
|
+
@headers
|
70
|
+
end
|
71
|
+
|
72
|
+
def [](key)
|
73
|
+
@headers[key]
|
74
|
+
end
|
75
|
+
|
76
|
+
def []=(key, value)
|
77
|
+
@headers[key] = value
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Allow getters like this:
|
82
|
+
# o.REQUEST_METHOD
|
83
|
+
|
84
|
+
def method_missing(method_name, *args)
|
85
|
+
if @headers.has_key?(method_name.to_s)
|
86
|
+
return @headers[method_name.to_s]
|
87
|
+
else
|
88
|
+
super(method_name, args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
module Camping
|
96
|
+
|
97
|
+
class Test < Test::Unit::TestCase
|
98
|
+
|
99
|
+
def test_dummy; end
|
100
|
+
|
101
|
+
def deny(condition, message='')
|
102
|
+
assert !condition, message
|
103
|
+
end
|
104
|
+
|
105
|
+
# http://project.ioni.st/post/217#post-217
|
106
|
+
#
|
107
|
+
# def test_new_publication
|
108
|
+
# assert_difference(Publication, :count) do
|
109
|
+
# post :create, :publication_title => ...
|
110
|
+
# # ...
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Is the number of items different?
|
115
|
+
#
|
116
|
+
# Can be used for increment and decrement.
|
117
|
+
#
|
118
|
+
def assert_difference(object, method = :count, difference = 1)
|
119
|
+
initial_value = object.send(method)
|
120
|
+
yield
|
121
|
+
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
|
122
|
+
end
|
123
|
+
def assert_no_difference(object, method, &block)
|
124
|
+
assert_difference object, method, 0, &block
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
class FunctionalTest < Test
|
130
|
+
|
131
|
+
def setup
|
132
|
+
@class_name_abbr = self.class.name.gsub(/^Test/, '')
|
133
|
+
@request = MockRequest.new
|
134
|
+
@cookies = @response = {}
|
135
|
+
end
|
136
|
+
|
137
|
+
def get(url='/')
|
138
|
+
send_request url, {}, 'GET'
|
139
|
+
end
|
140
|
+
|
141
|
+
def post(url, post_vars={})
|
142
|
+
send_request url, post_vars, 'POST'
|
143
|
+
end
|
144
|
+
|
145
|
+
def send_request(url, post_vars, method)
|
146
|
+
@request['REQUEST_METHOD'] = method
|
147
|
+
@request['SCRIPT_NAME'] = '/' + @class_name_abbr.downcase
|
148
|
+
@request['PATH_INFO'] = '/' + url
|
149
|
+
@request['REQUEST_URI'] = [@request.SCRIPT_NAME, @request.PATH_INFO].join('')
|
150
|
+
|
151
|
+
@request['HTTP_COOKIE'] = @cookies.map {|k,v| "#{k}=#{v}" }.join('; ') if @cookies
|
152
|
+
|
153
|
+
@response = eval("#{@class_name_abbr}.run StringIO.new('#{qs_build(post_vars)}'), @request")
|
154
|
+
|
155
|
+
@cookies = @response.headers['Set-Cookie'].inject(@cookies||{}) do |res,header|
|
156
|
+
data = header.split(';').first
|
157
|
+
name, value = data.split('=')
|
158
|
+
res[name] = value
|
159
|
+
res
|
160
|
+
end
|
161
|
+
|
162
|
+
if @response.headers['X-Sendfile']
|
163
|
+
@response.body = File.read(@response.headers['X-Sendfile'])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def assert_response(status_code)
|
168
|
+
case status_code
|
169
|
+
when :success
|
170
|
+
assert_equal 200, @response.status
|
171
|
+
when :redirect
|
172
|
+
assert_equal 302, @response.status
|
173
|
+
when :error
|
174
|
+
assert @response.status >= 500
|
175
|
+
else
|
176
|
+
assert_equal status_code, @response.status
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def assert_match_body(regex, message=nil)
|
181
|
+
assert_match regex, @response.body, message
|
182
|
+
end
|
183
|
+
def assert_no_match_body(regex, message=nil)
|
184
|
+
assert_no_match regex, @response.body, message
|
185
|
+
end
|
186
|
+
|
187
|
+
def assert_redirected_to(url, message=nil)
|
188
|
+
assert_equal url,
|
189
|
+
@response.headers['Location'].path.gsub(%r!/#{@class_name_abbr.downcase}!, ''),
|
190
|
+
message
|
191
|
+
end
|
192
|
+
|
193
|
+
def assert_cookie(name, pat, message=nil)
|
194
|
+
assert_match pat, @cookies[name], message
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_dummy; end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def qs_build(var_hash)
|
202
|
+
var_hash.map do |k, v|
|
203
|
+
[Camping.escape(k.to_s), Camping.escape(v.to_s)].join('=')
|
204
|
+
end.join('&')
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
class UnitTest < Test
|
210
|
+
|
211
|
+
def test_dummy; end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
data/public/blog.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require_gem 'camping', '>=1.4'
|
5
|
+
require 'camping'
|
6
|
+
require 'camping/session'
|
7
|
+
|
8
|
+
Camping.goes :Blog
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/blog/models'
|
11
|
+
require File.dirname(__FILE__) + '/blog/views'
|
12
|
+
require File.dirname(__FILE__) + '/blog/controllers'
|
13
|
+
|
14
|
+
module Blog
|
15
|
+
include Camping::Session
|
16
|
+
end
|
17
|
+
|
18
|
+
Blog::Models.schema do
|
19
|
+
create_table :blog_posts, :force => true do |t|
|
20
|
+
t.column :id, :integer, :null => false
|
21
|
+
t.column :user_id, :integer, :null => false
|
22
|
+
t.column :title, :string, :limit => 255
|
23
|
+
t.column :body, :text
|
24
|
+
end
|
25
|
+
create_table :blog_users, :force => true do |t|
|
26
|
+
t.column :id, :integer, :null => false
|
27
|
+
t.column :username, :string
|
28
|
+
t.column :password, :string
|
29
|
+
end
|
30
|
+
create_table :blog_comments, :force => true do |t|
|
31
|
+
t.column :id, :integer, :null => false
|
32
|
+
t.column :post_id, :integer, :null => false
|
33
|
+
t.column :username, :string
|
34
|
+
t.column :body, :text
|
35
|
+
end
|
36
|
+
execute "INSERT INTO blog_users (username, password) VALUES ('admin', 'camping')"
|
37
|
+
end
|
38
|
+
|
39
|
+
def Blog.create
|
40
|
+
Camping::Models::Session.create_schema
|
41
|
+
unless Blog::Models::Post.table_exists?
|
42
|
+
ActiveRecord::Schema.define(&Blog::Models.schema)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if __FILE__ == $0
|
47
|
+
require 'mongrel/camping'
|
48
|
+
|
49
|
+
Blog::Models::Base.establish_connection :adapter => 'sqlite3', :database => 'blog.db'
|
50
|
+
Blog::Models::Base.logger = Logger.new('camping.log')
|
51
|
+
Blog::Models::Base.threaded_connections=false
|
52
|
+
Blog.create
|
53
|
+
|
54
|
+
server = Mongrel::Camping::start("0.0.0.0",3002,"/blog",Blog)
|
55
|
+
puts "** Blog example is running at http://localhost:3002/blog"
|
56
|
+
puts "** Default username is `admin', password is `camping'"
|
57
|
+
server.run.join
|
58
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
module Blog::Controllers
|
3
|
+
class Index < R '/'
|
4
|
+
def get
|
5
|
+
@posts = Post.find :all
|
6
|
+
render :index
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Add
|
11
|
+
def get
|
12
|
+
unless @state.user_id.blank?
|
13
|
+
@user = User.find @state.user_id
|
14
|
+
@post = Post.new
|
15
|
+
end
|
16
|
+
render :add
|
17
|
+
end
|
18
|
+
def post
|
19
|
+
post = Post.create :title => input.post_title,
|
20
|
+
:body => input.post_body,
|
21
|
+
:user_id => @state.user_id
|
22
|
+
redirect View, post
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Info < R '/info/(\d+)', '/info/(\w+)/(\d+)', '/info', '/info/(\d+)/(\d+)/(\d+)/([\w-]+)'
|
27
|
+
def get(*args)
|
28
|
+
div do
|
29
|
+
code args.inspect; br; br
|
30
|
+
code ENV.inspect; br
|
31
|
+
code "Link: #{R(Info, 1, 2)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class View < R '/view/(\d+)'
|
37
|
+
def get post_id
|
38
|
+
@post = Post.find post_id
|
39
|
+
@comments = Models::Comment.find_all_by_post_id post_id
|
40
|
+
render :view
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Edit < R '/edit/(\d+)', '/edit'
|
45
|
+
def get post_id
|
46
|
+
unless @state.user_id.blank?
|
47
|
+
@user = User.find @state.user_id
|
48
|
+
end
|
49
|
+
@post = Post.find post_id
|
50
|
+
render :edit
|
51
|
+
end
|
52
|
+
|
53
|
+
def post
|
54
|
+
@post = Post.find input.post_id
|
55
|
+
@post.update_attributes :title => input.post_title, :body => input.post_body
|
56
|
+
redirect View, @post
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Comment
|
61
|
+
def post
|
62
|
+
Models::Comment.create( :username => input.post_username,
|
63
|
+
:body => input.post_body,
|
64
|
+
:post_id => input.post_id)
|
65
|
+
redirect View, input.post_id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Login
|
70
|
+
def post
|
71
|
+
@user = User.find :first, :conditions => ['username = ? AND password = ?', input.username, input.password]
|
72
|
+
|
73
|
+
if @user
|
74
|
+
@login = 'login success !'
|
75
|
+
@state.user_id = @user.id
|
76
|
+
else
|
77
|
+
@login = 'wrong user name or password'
|
78
|
+
end
|
79
|
+
render :login
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Logout
|
84
|
+
def get
|
85
|
+
@state.user_id = nil
|
86
|
+
render :logout
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Style < R '/styles.css'
|
91
|
+
def get
|
92
|
+
@headers["Content-Type"] = "text/css; charset=utf-8"
|
93
|
+
@body = %{
|
94
|
+
body {
|
95
|
+
font-family: Utopia, Georgia, serif;
|
96
|
+
}
|
97
|
+
h1.header {
|
98
|
+
background-color: #fef;
|
99
|
+
margin: 0; padding: 10px;
|
100
|
+
}
|
101
|
+
div.content {
|
102
|
+
padding: 10px;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
module Blog::Models
|
3
|
+
def self.schema(&block)
|
4
|
+
@@schema = block if block_given?
|
5
|
+
@@schema
|
6
|
+
end
|
7
|
+
|
8
|
+
class Post < Base; belongs_to :user; end
|
9
|
+
class Comment < Base; belongs_to :user; end
|
10
|
+
class User < Base; validates_presence_of :username; end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
module Blog::Views
|
3
|
+
|
4
|
+
def layout
|
5
|
+
html do
|
6
|
+
head do
|
7
|
+
title 'blog'
|
8
|
+
link :rel => 'stylesheet', :type => 'text/css',
|
9
|
+
:href => '/styles.css', :media => 'screen'
|
10
|
+
end
|
11
|
+
body do
|
12
|
+
h1.header { a 'blog', :href => R(Index) }
|
13
|
+
div.content do
|
14
|
+
self << yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def index
|
21
|
+
if @posts.empty?
|
22
|
+
p 'No posts found.'
|
23
|
+
p { a 'Add', :href => R(Add) }
|
24
|
+
else
|
25
|
+
for post in @posts
|
26
|
+
_post(post)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def login
|
32
|
+
p { b @login }
|
33
|
+
p { a 'Continue', :href => R(Add) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def logout
|
37
|
+
p "You have been logged out."
|
38
|
+
p { a 'Continue', :href => R(Index) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def add
|
42
|
+
if @user
|
43
|
+
_form(post, :action => R(Add))
|
44
|
+
else
|
45
|
+
_login
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def edit
|
50
|
+
if @user
|
51
|
+
_form(post, :action => R(Edit))
|
52
|
+
else
|
53
|
+
_login
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def view
|
58
|
+
_post(post)
|
59
|
+
|
60
|
+
p "Comment for this post:"
|
61
|
+
for c in @comments
|
62
|
+
h1 c.username
|
63
|
+
p c.body
|
64
|
+
end
|
65
|
+
|
66
|
+
form :action => R(Comment), :method => 'post' do
|
67
|
+
label 'Name', :for => 'post_username'; br
|
68
|
+
input :name => 'post_username', :type => 'text'; br
|
69
|
+
label 'Comment', :for => 'post_body'; br
|
70
|
+
textarea :name => 'post_body' do; end; br
|
71
|
+
input :type => 'hidden', :name => 'post_id', :value => post.id
|
72
|
+
input :type => 'submit'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# partials
|
77
|
+
def _login
|
78
|
+
form :action => R(Login), :method => 'post' do
|
79
|
+
label 'Username', :for => 'username'; br
|
80
|
+
input :name => 'username', :type => 'text'; br
|
81
|
+
|
82
|
+
label 'Password', :for => 'password'; br
|
83
|
+
input :name => 'password', :type => 'text'; br
|
84
|
+
|
85
|
+
input :type => 'submit', :name => 'login', :value => 'Login'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _post(post)
|
90
|
+
h1 post.title
|
91
|
+
p post.body
|
92
|
+
p do
|
93
|
+
a "Edit", :href => R(Edit, post)
|
94
|
+
a "View", :href => R(View, post)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def _form(post, opts)
|
99
|
+
p do
|
100
|
+
text "You are logged in as #{@user.username} | "
|
101
|
+
a 'Logout', :href => R(Logout)
|
102
|
+
end
|
103
|
+
form({:method => 'post'}.merge(opts)) do
|
104
|
+
label 'Title', :for => 'post_title'; br
|
105
|
+
input :name => 'post_title', :type => 'text',
|
106
|
+
:value => post.title; br
|
107
|
+
|
108
|
+
label 'Body', :for => 'post_body'; br
|
109
|
+
textarea post.body, :name => 'post_body'; br
|
110
|
+
|
111
|
+
input :type => 'hidden', :name => 'post_id', :value => post.id
|
112
|
+
input :type => 'submit'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/public/homepage.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/local/bin/ruby -rubygems
|
2
|
+
require 'camping'
|
3
|
+
|
4
|
+
Camping.goes :HomePage
|
5
|
+
|
6
|
+
module HomePage::Controllers
|
7
|
+
|
8
|
+
# The root slash shows the `index' view.
|
9
|
+
class Index < R '/'
|
10
|
+
def get
|
11
|
+
render :index
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Any other page name gets sent to the view
|
16
|
+
# of the same name.
|
17
|
+
#
|
18
|
+
# /index -> Views#index
|
19
|
+
# /sample -> Views#sample
|
20
|
+
#
|
21
|
+
class Page < R '/(\w+)'
|
22
|
+
def get(page_name)
|
23
|
+
render page_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module HomePage::Views
|
30
|
+
|
31
|
+
# If you have a `layout' method like this, it
|
32
|
+
# will wrap the HTML in the other methods. The
|
33
|
+
# `self << yield' is where the HTML is inserted.
|
34
|
+
def layout
|
35
|
+
html do
|
36
|
+
title { 'My HomePage' }
|
37
|
+
body { self << yield }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# The `index' view. Inside your views, you express
|
42
|
+
# the HTML in Ruby. See http://code.whytheluckystiff.net/markaby/.
|
43
|
+
def index
|
44
|
+
p 'Hi my name is Charles.'
|
45
|
+
p 'Here are some links:'
|
46
|
+
ul do
|
47
|
+
li { a 'Google', :href => 'http://google.com' }
|
48
|
+
li { a 'A sample page', :href => '/sample' }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The `sample' view.
|
53
|
+
def sample
|
54
|
+
p 'A sample page'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if __FILE__ == $0
|
59
|
+
require 'mongrel/camping'
|
60
|
+
|
61
|
+
server = Mongrel::Camping::start("0.0.0.0",3002,"/homepage",HomePage)
|
62
|
+
puts "** HomePage example is running at http://localhost:3002/homepage"
|
63
|
+
server.run.join
|
64
|
+
end
|
data/test/test_blog.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
|
+
require File.dirname(__FILE__) + "/../public/blog"
|
3
|
+
|
4
|
+
Blog.create
|
5
|
+
include Blog::Models
|
6
|
+
|
7
|
+
class TestBlog < Camping::FunctionalTest
|
8
|
+
|
9
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
# Do other stuff here
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_index
|
17
|
+
get
|
18
|
+
assert_response :success
|
19
|
+
assert_match_body %r!>blog<!
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_view
|
23
|
+
get '/view/1'
|
24
|
+
assert_response :success
|
25
|
+
assert_match_body %r!The quick fox jumped over the lazy dog!
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_styles
|
29
|
+
get 'styles.css'
|
30
|
+
assert_match_body %r!Utopia!
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_edit_should_require_login
|
34
|
+
get '/edit/1'
|
35
|
+
assert_response :success
|
36
|
+
assert_match_body 'login'
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_login
|
40
|
+
post 'login', :username => 'quentin', :password => 'password'
|
41
|
+
assert_match_body 'login success'
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_comment
|
45
|
+
assert_difference(Comment) {
|
46
|
+
post 'comment', :post_username => 'jim',
|
47
|
+
:post_body => 'Nice article.',
|
48
|
+
:post_id => 1
|
49
|
+
assert_response :redirect
|
50
|
+
assert_redirected_to '/view/1'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class TestPost < Camping::UnitTest
|
57
|
+
|
58
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
59
|
+
|
60
|
+
def test_create
|
61
|
+
post = create
|
62
|
+
assert post.valid?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_assoc
|
66
|
+
post = Post.find :first
|
67
|
+
assert_kind_of User, post.user
|
68
|
+
assert_equal 1, post.user.id
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_destroy
|
72
|
+
original_count = Post.count
|
73
|
+
Post.destroy 1
|
74
|
+
assert_equal original_count - 1, Post.count
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def create(options={})
|
80
|
+
Post.create({ :user_id => 1,
|
81
|
+
:title => "Title",
|
82
|
+
:body => "Body"}.merge(options))
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class TestUser < Camping::UnitTest
|
88
|
+
|
89
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
90
|
+
|
91
|
+
def test_create
|
92
|
+
user = create
|
93
|
+
assert user.valid?
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_required
|
97
|
+
user = create(:username => nil)
|
98
|
+
deny user.valid?
|
99
|
+
assert_not_nil user.errors.on(:username)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def create(options={})
|
105
|
+
User.create({ :username => 'godfrey',
|
106
|
+
:password => 'password' }.merge(options))
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
|
+
require File.dirname(__FILE__) + "/../public/homepage"
|
3
|
+
|
4
|
+
class TestHomePage < Camping::FunctionalTest
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
# Do other stuff here
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_index
|
12
|
+
get '/'
|
13
|
+
assert_response :success
|
14
|
+
assert_match_body %r!Charles!
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_page
|
18
|
+
get '/sample'
|
19
|
+
assert_response :success
|
20
|
+
assert_match_body %r!<p>A sample page</p>!
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: mosquito
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-11-15 00:00:00 +11:00
|
8
|
+
summary: A Camping test library.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
- test
|
12
|
+
email: boss@topfunky.com
|
13
|
+
homepage: http://mosquito.rubyforge.org
|
14
|
+
rubyforge_project: mosquito
|
15
|
+
description: A library for writing tests for your Camping app.
|
16
|
+
autorequire:
|
17
|
+
default_executable:
|
18
|
+
bindir: bin
|
19
|
+
has_rdoc: true
|
20
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
post_install_message:
|
30
|
+
authors:
|
31
|
+
- Geoffrey Grosenbach
|
32
|
+
files:
|
33
|
+
- Rakefile
|
34
|
+
- README
|
35
|
+
- MIT-LICENSE
|
36
|
+
- Manifest.txt
|
37
|
+
- lib
|
38
|
+
- lib/mosquito.rb
|
39
|
+
- public
|
40
|
+
- public/blog
|
41
|
+
- public/blog.rb
|
42
|
+
- public/blog/controllers.rb
|
43
|
+
- public/blog/models.rb
|
44
|
+
- public/blog/views.rb
|
45
|
+
- public/homepage.rb
|
46
|
+
- test
|
47
|
+
- test/test_blog.rb
|
48
|
+
- test/test_homepage.rb
|
49
|
+
- test/fixtures
|
50
|
+
- test/fixtures/blog_comments.yml
|
51
|
+
- test/fixtures/blog_posts.yml
|
52
|
+
- test/fixtures/blog_users.yml
|
53
|
+
test_files:
|
54
|
+
- test/test_blog.rb
|
55
|
+
- test/test_homepage.rb
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
extra_rdoc_files: []
|
59
|
+
|
60
|
+
executables: []
|
61
|
+
|
62
|
+
extensions: []
|
63
|
+
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
dependencies:
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: hoe
|
69
|
+
version_requirement:
|
70
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.1.3
|
75
|
+
version:
|