junebug-wiki 0.0.19
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/History.txt +105 -0
- data/Manifest.txt +36 -0
- data/README.txt +87 -0
- data/RELEASE_NOTES.txt +25 -0
- data/Rakefile +63 -0
- data/bin/junebug +12 -0
- data/deploy/Rakefile +10 -0
- data/deploy/config.yml +15 -0
- data/deploy/console +8 -0
- data/deploy/public/images/feed-icon-14x14.png +0 -0
- data/deploy/public/style/base.css +274 -0
- data/deploy/public/style/yui/fonts.css +34 -0
- data/deploy/public/style/yui/grids.css +88 -0
- data/deploy/public/style/yui/reset.css +14 -0
- data/deploy/wiki +12 -0
- data/dump/junebug_pages.yml +148 -0
- data/lib/junebug.rb +55 -0
- data/lib/junebug/config.rb +15 -0
- data/lib/junebug/controllers.rb +187 -0
- data/lib/junebug/ext/acts_as_versioned.rb +509 -0
- data/lib/junebug/ext/diff.rb +317 -0
- data/lib/junebug/ext/mosquito.rb +210 -0
- data/lib/junebug/generator.rb +65 -0
- data/lib/junebug/helpers.rb +48 -0
- data/lib/junebug/models.rb +98 -0
- data/lib/junebug/tasks.rb +4 -0
- data/lib/junebug/tasks/dump.rake +34 -0
- data/lib/junebug/tasks/update.rake +27 -0
- data/lib/junebug/version.rb +9 -0
- data/lib/junebug/views.rb +307 -0
- data/setup.rb +1585 -0
- data/test/fixtures/junebug_page_versions.yml +0 -0
- data/test/fixtures/junebug_pages.yml +5 -0
- data/test/fixtures/junebug_users.yml +5 -0
- data/test/test_helper.rb +2 -0
- data/test/wiki_test.rb +270 -0
- metadata +134 -0
@@ -0,0 +1,317 @@
|
|
1
|
+
module HTMLDiff
|
2
|
+
|
3
|
+
Match = Struct.new(:start_in_old, :start_in_new, :size)
|
4
|
+
class Match
|
5
|
+
def end_in_old
|
6
|
+
self.start_in_old + self.size
|
7
|
+
end
|
8
|
+
|
9
|
+
def end_in_new
|
10
|
+
self.start_in_new + self.size
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Operation = Struct.new(:action, :start_in_old, :end_in_old, :start_in_new, :end_in_new)
|
15
|
+
|
16
|
+
class DiffBuilder
|
17
|
+
|
18
|
+
def initialize(old_version, new_version)
|
19
|
+
@old_version, @new_version = old_version, new_version
|
20
|
+
@content = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def build
|
24
|
+
split_inputs_to_words
|
25
|
+
index_new_words
|
26
|
+
operations.each { |op| perform_operation(op) }
|
27
|
+
return @content.join
|
28
|
+
end
|
29
|
+
|
30
|
+
def split_inputs_to_words
|
31
|
+
@old_words = convert_html_to_list_of_words(explode(@old_version))
|
32
|
+
@new_words = convert_html_to_list_of_words(explode(@new_version))
|
33
|
+
end
|
34
|
+
|
35
|
+
def index_new_words
|
36
|
+
@word_indices = Hash.new { |h, word| h[word] = [] }
|
37
|
+
@new_words.each_with_index { |word, i| @word_indices[word] << i }
|
38
|
+
end
|
39
|
+
|
40
|
+
def operations
|
41
|
+
position_in_old = position_in_new = 0
|
42
|
+
operations = []
|
43
|
+
|
44
|
+
matches = matching_blocks
|
45
|
+
# an empty match at the end forces the loop below to handle the unmatched tails
|
46
|
+
# I'm sure it can be done more gracefully, but not at 23:52
|
47
|
+
matches << Match.new(@old_words.length, @new_words.length, 0)
|
48
|
+
|
49
|
+
matches.each_with_index do |match, i|
|
50
|
+
match_starts_at_current_position_in_old = (position_in_old == match.start_in_old)
|
51
|
+
match_starts_at_current_position_in_new = (position_in_new == match.start_in_new)
|
52
|
+
|
53
|
+
action_upto_match_positions =
|
54
|
+
case [match_starts_at_current_position_in_old, match_starts_at_current_position_in_new]
|
55
|
+
when [false, false]
|
56
|
+
:replace
|
57
|
+
when [true, false]
|
58
|
+
:insert
|
59
|
+
when [false, true]
|
60
|
+
:delete
|
61
|
+
else
|
62
|
+
# this happens if the first few words are same in both versions
|
63
|
+
:none
|
64
|
+
end
|
65
|
+
|
66
|
+
if action_upto_match_positions != :none
|
67
|
+
operation_upto_match_positions =
|
68
|
+
Operation.new(action_upto_match_positions,
|
69
|
+
position_in_old, match.start_in_old,
|
70
|
+
position_in_new, match.start_in_new)
|
71
|
+
operations << operation_upto_match_positions
|
72
|
+
end
|
73
|
+
if match.size != 0
|
74
|
+
match_operation = Operation.new(:equal,
|
75
|
+
match.start_in_old, match.end_in_old,
|
76
|
+
match.start_in_new, match.end_in_new)
|
77
|
+
operations << match_operation
|
78
|
+
end
|
79
|
+
|
80
|
+
position_in_old = match.end_in_old
|
81
|
+
position_in_new = match.end_in_new
|
82
|
+
end
|
83
|
+
|
84
|
+
operations
|
85
|
+
end
|
86
|
+
|
87
|
+
def matching_blocks
|
88
|
+
matching_blocks = []
|
89
|
+
recursively_find_matching_blocks(0, @old_words.size, 0, @new_words.size, matching_blocks)
|
90
|
+
matching_blocks
|
91
|
+
end
|
92
|
+
|
93
|
+
def recursively_find_matching_blocks(start_in_old, end_in_old, start_in_new, end_in_new, matching_blocks)
|
94
|
+
match = find_match(start_in_old, end_in_old, start_in_new, end_in_new)
|
95
|
+
if match
|
96
|
+
if start_in_old < match.start_in_old and start_in_new < match.start_in_new
|
97
|
+
recursively_find_matching_blocks(
|
98
|
+
start_in_old, match.start_in_old, start_in_new, match.start_in_new, matching_blocks)
|
99
|
+
end
|
100
|
+
matching_blocks << match
|
101
|
+
if match.end_in_old < end_in_old and match.end_in_new < end_in_new
|
102
|
+
recursively_find_matching_blocks(
|
103
|
+
match.end_in_old, end_in_old, match.end_in_new, end_in_new, matching_blocks)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def find_match(start_in_old, end_in_old, start_in_new, end_in_new)
|
109
|
+
|
110
|
+
best_match_in_old = start_in_old
|
111
|
+
best_match_in_new = start_in_new
|
112
|
+
best_match_size = 0
|
113
|
+
|
114
|
+
match_length_at = Hash.new { |h, index| h[index] = 0 }
|
115
|
+
|
116
|
+
start_in_old.upto(end_in_old - 1) do |index_in_old|
|
117
|
+
|
118
|
+
new_match_length_at = Hash.new { |h, index| h[index] = 0 }
|
119
|
+
|
120
|
+
@word_indices[@old_words[index_in_old]].each do |index_in_new|
|
121
|
+
next if index_in_new < start_in_new
|
122
|
+
break if index_in_new >= end_in_new
|
123
|
+
|
124
|
+
new_match_length = match_length_at[index_in_new - 1] + 1
|
125
|
+
new_match_length_at[index_in_new] = new_match_length
|
126
|
+
|
127
|
+
if new_match_length > best_match_size
|
128
|
+
best_match_in_old = index_in_old - new_match_length + 1
|
129
|
+
best_match_in_new = index_in_new - new_match_length + 1
|
130
|
+
best_match_size = new_match_length
|
131
|
+
end
|
132
|
+
end
|
133
|
+
match_length_at = new_match_length_at
|
134
|
+
end
|
135
|
+
|
136
|
+
# best_match_in_old, best_match_in_new, best_match_size = add_matching_words_left(
|
137
|
+
# best_match_in_old, best_match_in_new, best_match_size, start_in_old, start_in_new)
|
138
|
+
# best_match_in_old, best_match_in_new, match_size = add_matching_words_right(
|
139
|
+
# best_match_in_old, best_match_in_new, best_match_size, end_in_old, end_in_new)
|
140
|
+
|
141
|
+
return (best_match_size != 0 ? Match.new(best_match_in_old, best_match_in_new, best_match_size) : nil)
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_matching_words_left(match_in_old, match_in_new, match_size, start_in_old, start_in_new)
|
145
|
+
while match_in_old > start_in_old and
|
146
|
+
match_in_new > start_in_new and
|
147
|
+
@old_words[match_in_old - 1] == @new_words[match_in_new - 1]
|
148
|
+
match_in_old -= 1
|
149
|
+
match_in_new -= 1
|
150
|
+
match_size += 1
|
151
|
+
end
|
152
|
+
[match_in_old, match_in_new, match_size]
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_matching_words_right(match_in_old, match_in_new, match_size, end_in_old, end_in_new)
|
156
|
+
while match_in_old + match_size < end_in_old and
|
157
|
+
match_in_new + match_size < end_in_new and
|
158
|
+
@old_words[match_in_old + match_size] == @new_words[match_in_new + match_size]
|
159
|
+
match_size += 1
|
160
|
+
end
|
161
|
+
[match_in_old, match_in_new, match_size]
|
162
|
+
end
|
163
|
+
|
164
|
+
VALID_METHODS = [:replace, :insert, :delete, :equal]
|
165
|
+
|
166
|
+
def perform_operation(operation)
|
167
|
+
@operation = operation
|
168
|
+
self.send operation.action, operation
|
169
|
+
end
|
170
|
+
|
171
|
+
def replace(operation)
|
172
|
+
delete(operation, 'diffmod')
|
173
|
+
insert(operation, 'diffmod')
|
174
|
+
end
|
175
|
+
|
176
|
+
def insert(operation, tagclass = 'diffins')
|
177
|
+
insert_tag('ins', tagclass, @new_words[operation.start_in_new...operation.end_in_new])
|
178
|
+
end
|
179
|
+
|
180
|
+
def delete(operation, tagclass = 'diffdel')
|
181
|
+
insert_tag('del', tagclass, @old_words[operation.start_in_old...operation.end_in_old])
|
182
|
+
end
|
183
|
+
|
184
|
+
def equal(operation)
|
185
|
+
# no tags to insert, simply copy the matching words from one of the versions
|
186
|
+
@content += @new_words[operation.start_in_new...operation.end_in_new]
|
187
|
+
end
|
188
|
+
|
189
|
+
def opening_tag?(item)
|
190
|
+
item =~ %r!^\s*<[^>]+>\s*$!
|
191
|
+
end
|
192
|
+
|
193
|
+
def closing_tag?(item)
|
194
|
+
item =~ %r!^\s*</[^>]+>\s*$!
|
195
|
+
end
|
196
|
+
|
197
|
+
def tag?(item)
|
198
|
+
opening_tag?(item) or closing_tag?(item)
|
199
|
+
end
|
200
|
+
|
201
|
+
def extract_consecutive_words(words, &condition)
|
202
|
+
index_of_first_tag = nil
|
203
|
+
words.each_with_index do |word, i|
|
204
|
+
if !condition.call(word)
|
205
|
+
index_of_first_tag = i
|
206
|
+
break
|
207
|
+
end
|
208
|
+
end
|
209
|
+
if index_of_first_tag
|
210
|
+
return words.slice!(0...index_of_first_tag)
|
211
|
+
else
|
212
|
+
return words.slice!(0..words.length)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# This method encloses words within a specified tag (ins or del), and adds this into @content,
|
217
|
+
# with a twist: if there are words contain tags, it actually creates multiple ins or del,
|
218
|
+
# so that they don't include any ins or del. This handles cases like
|
219
|
+
# old: '<p>a</p>'
|
220
|
+
# new: '<p>ab</p><p>c</b>'
|
221
|
+
# diff result: '<p>a<ins>b</ins></p><p><ins>c</ins></p>'
|
222
|
+
# this still doesn't guarantee valid HTML (hint: think about diffing a text containing ins or
|
223
|
+
# del tags), but handles correctly more cases than the earlier version.
|
224
|
+
#
|
225
|
+
# P.S.: Spare a thought for people who write HTML browsers. They live in this ... every day.
|
226
|
+
|
227
|
+
def insert_tag(tagname, cssclass, words)
|
228
|
+
loop do
|
229
|
+
break if words.empty?
|
230
|
+
non_tags = extract_consecutive_words(words) { |word| not tag?(word) }
|
231
|
+
@content << wrap_text(non_tags.join, tagname, cssclass) unless non_tags.empty?
|
232
|
+
|
233
|
+
break if words.empty?
|
234
|
+
@content += extract_consecutive_words(words) { |word| tag?(word) }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def wrap_text(text, tagname, cssclass)
|
239
|
+
%(<#{tagname} class="#{cssclass}">#{text}</#{tagname}>)
|
240
|
+
end
|
241
|
+
|
242
|
+
def explode(sequence)
|
243
|
+
sequence.is_a?(String) ? sequence.split(//) : sequence
|
244
|
+
end
|
245
|
+
|
246
|
+
def end_of_tag?(char)
|
247
|
+
char == '>'
|
248
|
+
end
|
249
|
+
|
250
|
+
def start_of_tag?(char)
|
251
|
+
char == '<'
|
252
|
+
end
|
253
|
+
|
254
|
+
def whitespace?(char)
|
255
|
+
char =~ /\s/
|
256
|
+
end
|
257
|
+
|
258
|
+
def convert_html_to_list_of_words(x, use_brackets = false)
|
259
|
+
mode = :char
|
260
|
+
current_word = ''
|
261
|
+
words = []
|
262
|
+
|
263
|
+
explode(x).each do |char|
|
264
|
+
case mode
|
265
|
+
when :tag
|
266
|
+
if end_of_tag? char
|
267
|
+
current_word << (use_brackets ? ']' : '>')
|
268
|
+
words << current_word
|
269
|
+
current_word = ''
|
270
|
+
if whitespace?(char)
|
271
|
+
mode = :whitespace
|
272
|
+
else
|
273
|
+
mode = :char
|
274
|
+
end
|
275
|
+
else
|
276
|
+
current_word << char
|
277
|
+
end
|
278
|
+
when :char
|
279
|
+
if start_of_tag? char
|
280
|
+
words << current_word unless current_word.empty?
|
281
|
+
current_word = (use_brackets ? '[' : '<')
|
282
|
+
mode = :tag
|
283
|
+
elsif /\s/.match char
|
284
|
+
words << current_word unless current_word.empty?
|
285
|
+
current_word = char
|
286
|
+
mode = :whitespace
|
287
|
+
else
|
288
|
+
current_word << char
|
289
|
+
end
|
290
|
+
when :whitespace
|
291
|
+
if start_of_tag? char
|
292
|
+
words << current_word unless current_word.empty?
|
293
|
+
current_word = (use_brackets ? '[' : '<')
|
294
|
+
mode = :tag
|
295
|
+
elsif /\s/.match char
|
296
|
+
current_word << char
|
297
|
+
else
|
298
|
+
words << current_word unless current_word.empty?
|
299
|
+
current_word = char
|
300
|
+
mode = :char
|
301
|
+
end
|
302
|
+
else
|
303
|
+
raise "Unknown mode #{mode.inspect}"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
words << current_word unless current_word.empty?
|
307
|
+
words
|
308
|
+
end
|
309
|
+
|
310
|
+
end # of class Diff Builder
|
311
|
+
|
312
|
+
def diff(a, b)
|
313
|
+
DiffBuilder.new(a, b).build
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
@@ -0,0 +1,210 @@
|
|
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
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
14
|
+
ActiveRecord::Base.logger = Logger.new("test/test.log")
|
15
|
+
|
16
|
+
Test::Unit::TestCase.fixture_path = "test/fixtures/"
|
17
|
+
|
18
|
+
class Test::Unit::TestCase #:nodoc:
|
19
|
+
def create_fixtures(*table_names)
|
20
|
+
if block_given?
|
21
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
22
|
+
else
|
23
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
28
|
+
self.use_transactional_fixtures = true
|
29
|
+
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
|
30
|
+
self.use_instantiated_fixtures = false
|
31
|
+
end
|
32
|
+
|
33
|
+
class MockRequest
|
34
|
+
def initialize
|
35
|
+
@headers = {
|
36
|
+
'SERVER_NAME' => 'localhost',
|
37
|
+
'PATH_INFO' => '',
|
38
|
+
'HTTP_ACCEPT_ENCODING' => 'gzip,deflate',
|
39
|
+
'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',
|
40
|
+
'SCRIPT_NAME' => '/',
|
41
|
+
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
42
|
+
'HTTP_CACHE_CONTROL' => 'max-age=0',
|
43
|
+
'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',
|
44
|
+
'HTTP_HOST' => 'localhost',
|
45
|
+
'REMOTE_ADDR' => '127.0.0.1',
|
46
|
+
'SERVER_SOFTWARE' => 'Mongrel 0.3.12.4',
|
47
|
+
'HTTP_KEEP_ALIVE' => '300',
|
48
|
+
'HTTP_REFERER' => 'http://localhost/',
|
49
|
+
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
50
|
+
'HTTP_VERSION' => 'HTTP/1.1',
|
51
|
+
'REQUEST_URI' => '/',
|
52
|
+
'SERVER_PORT' => '80',
|
53
|
+
'GATEWAY_INTERFACE' => 'CGI/1.2',
|
54
|
+
'HTTP_ACCEPT' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
|
55
|
+
'HTTP_CONNECTION' => 'keep-alive',
|
56
|
+
'REQUEST_METHOD' => 'GET',
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def set(key, val)
|
61
|
+
@headers[key] = val
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_hash
|
65
|
+
@headers
|
66
|
+
end
|
67
|
+
|
68
|
+
def [](key)
|
69
|
+
@headers[key]
|
70
|
+
end
|
71
|
+
|
72
|
+
def []=(key, value)
|
73
|
+
@headers[key] = value
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Allow getters like this:
|
78
|
+
# o.REQUEST_METHOD
|
79
|
+
|
80
|
+
def method_missing(method_name, *args)
|
81
|
+
if @headers.has_key?(method_name.to_s)
|
82
|
+
return @headers[method_name.to_s]
|
83
|
+
else
|
84
|
+
super(method_name, args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
module Camping
|
92
|
+
|
93
|
+
class Test < Test::Unit::TestCase
|
94
|
+
|
95
|
+
def test_dummy; end
|
96
|
+
|
97
|
+
def deny(condition, message='')
|
98
|
+
assert !condition, message
|
99
|
+
end
|
100
|
+
|
101
|
+
# http://project.ioni.st/post/217#post-217
|
102
|
+
#
|
103
|
+
# def test_new_publication
|
104
|
+
# assert_difference(Publication, :count) do
|
105
|
+
# post :create, :publication_title => ...
|
106
|
+
# # ...
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# Is the number of items different?
|
111
|
+
#
|
112
|
+
# Can be used for increment and decrement.
|
113
|
+
#
|
114
|
+
def assert_difference(object, method = :count, difference = 1)
|
115
|
+
initial_value = object.send(method)
|
116
|
+
yield
|
117
|
+
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
|
118
|
+
end
|
119
|
+
def assert_no_difference(object, method, &block)
|
120
|
+
assert_difference object, method, 0, &block
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class FunctionalTest < Test
|
126
|
+
|
127
|
+
def setup
|
128
|
+
@class_name_abbr = self.class.name.gsub(/Test$/, '')
|
129
|
+
@request = MockRequest.new
|
130
|
+
end
|
131
|
+
|
132
|
+
def get(url='/')
|
133
|
+
send_request url, {}, 'GET'
|
134
|
+
end
|
135
|
+
|
136
|
+
def post(url, post_vars={})
|
137
|
+
send_request url, post_vars, 'POST'
|
138
|
+
end
|
139
|
+
|
140
|
+
def send_request(url, post_vars, method)
|
141
|
+
@request['REQUEST_METHOD'] = method
|
142
|
+
@request['SCRIPT_NAME'] = '/' + @class_name_abbr.downcase
|
143
|
+
@request['PATH_INFO'] = '/' + url
|
144
|
+
@request['REQUEST_URI'] = [@request.SCRIPT_NAME, @request.PATH_INFO].join('')
|
145
|
+
|
146
|
+
@request['HTTP_COOKIE'] = @cookies.map {|k,v| "#{k}=#{v}" }.join('; ') if @cookies
|
147
|
+
|
148
|
+
@response = eval("#{@class_name_abbr}.run StringIO.new('#{qs_build(post_vars)}'), @request")
|
149
|
+
|
150
|
+
@cookies = @response.headers['Set-Cookie'].inject(@cookies||{}) do |res,header|
|
151
|
+
data = header.split(';').first
|
152
|
+
name, value = data.split('=')
|
153
|
+
res[name] = value
|
154
|
+
res
|
155
|
+
end
|
156
|
+
|
157
|
+
if @response.headers['X-Sendfile']
|
158
|
+
@response.body = File.read(@response.headers['X-Sendfile'])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def assert_response(status_code)
|
163
|
+
case status_code
|
164
|
+
when :success
|
165
|
+
assert_equal 200, @response.status
|
166
|
+
when :redirect
|
167
|
+
assert_equal 302, @response.status
|
168
|
+
when :error
|
169
|
+
assert @response.status >= 500
|
170
|
+
else
|
171
|
+
assert_equal status_code, @response.status
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def assert_match_body(regex, message=nil)
|
176
|
+
assert_match regex, @response.body, message
|
177
|
+
end
|
178
|
+
def assert_no_match_body(regex, message=nil)
|
179
|
+
assert_no_match regex, @response.body, message
|
180
|
+
end
|
181
|
+
|
182
|
+
def assert_redirected_to(url, message=nil)
|
183
|
+
assert_equal url,
|
184
|
+
@response.headers['Location'].path.gsub(%r!/#{@class_name_abbr.downcase}!, ''),
|
185
|
+
message
|
186
|
+
end
|
187
|
+
|
188
|
+
def assert_cookie(name, pat, message=nil)
|
189
|
+
assert_match pat, @cookies[name], message
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_dummy; end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def qs_build(var_hash)
|
197
|
+
var_hash.map do |k, v|
|
198
|
+
[Camping.escape(k.to_s), Camping.escape(v.to_s)].join('=')
|
199
|
+
end.join('&')
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
class UnitTest < Test
|
205
|
+
|
206
|
+
def test_dummy; end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|