raw 0.49.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/doc/CONTRIBUTORS +106 -0
- data/doc/LICENSE +32 -0
- data/doc/coding_conventions.txt +11 -0
- data/lib/raw.rb +42 -0
- data/lib/raw/adapter.rb +113 -0
- data/lib/raw/adapter/cgi.rb +41 -0
- data/lib/raw/adapter/fastcgi.rb +48 -0
- data/lib/raw/adapter/mongrel.rb +146 -0
- data/lib/raw/adapter/script.rb +94 -0
- data/lib/raw/adapter/webrick.rb +144 -0
- data/lib/raw/adapter/webrick/vcr.rb +91 -0
- data/lib/raw/cgi.rb +323 -0
- data/lib/raw/cgi/cookie.rb +47 -0
- data/lib/raw/cgi/http.rb +62 -0
- data/lib/raw/compiler.rb +138 -0
- data/lib/raw/compiler/filter/cleanup.rb +21 -0
- data/lib/raw/compiler/filter/elements.rb +166 -0
- data/lib/raw/compiler/filter/elements/element.rb +210 -0
- data/lib/raw/compiler/filter/localization.rb +23 -0
- data/lib/raw/compiler/filter/markup.rb +32 -0
- data/lib/raw/compiler/filter/morph.rb +123 -0
- data/lib/raw/compiler/filter/morph/each.rb +34 -0
- data/lib/raw/compiler/filter/morph/for.rb +11 -0
- data/lib/raw/compiler/filter/morph/if.rb +26 -0
- data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
- data/lib/raw/compiler/filter/morph/standard.rb +55 -0
- data/lib/raw/compiler/filter/morph/times.rb +27 -0
- data/lib/raw/compiler/filter/script.rb +116 -0
- data/lib/raw/compiler/filter/squeeze.rb +16 -0
- data/lib/raw/compiler/filter/static_include.rb +74 -0
- data/lib/raw/compiler/filter/template.rb +121 -0
- data/lib/raw/compiler/reloader.rb +96 -0
- data/lib/raw/context.rb +154 -0
- data/lib/raw/context/flash.rb +157 -0
- data/lib/raw/context/global.rb +88 -0
- data/lib/raw/context/request.rb +338 -0
- data/lib/raw/context/response.rb +57 -0
- data/lib/raw/context/session.rb +198 -0
- data/lib/raw/context/session/drb.rb +11 -0
- data/lib/raw/context/session/file.rb +15 -0
- data/lib/raw/context/session/memcached.rb +13 -0
- data/lib/raw/context/session/memory.rb +12 -0
- data/lib/raw/context/session/og.rb +15 -0
- data/lib/raw/context/session/pstore.rb +13 -0
- data/lib/raw/control.rb +18 -0
- data/lib/raw/control/attribute.rb +91 -0
- data/lib/raw/control/attribute/checkbox.rb +25 -0
- data/lib/raw/control/attribute/datetime.rb +21 -0
- data/lib/raw/control/attribute/file.rb +20 -0
- data/lib/raw/control/attribute/fixnum.rb +26 -0
- data/lib/raw/control/attribute/float.rb +26 -0
- data/lib/raw/control/attribute/options.rb +38 -0
- data/lib/raw/control/attribute/password.rb +16 -0
- data/lib/raw/control/attribute/text.rb +16 -0
- data/lib/raw/control/attribute/textarea.rb +16 -0
- data/lib/raw/control/none.rb +16 -0
- data/lib/raw/control/relation.rb +59 -0
- data/lib/raw/control/relation/belongs_to.rb +0 -0
- data/lib/raw/control/relation/has_many.rb +97 -0
- data/lib/raw/control/relation/joins_many.rb +0 -0
- data/lib/raw/control/relation/many_to_many.rb +0 -0
- data/lib/raw/control/relation/refers_to.rb +29 -0
- data/lib/raw/controller.rb +37 -0
- data/lib/raw/controller/publishable.rb +160 -0
- data/lib/raw/dispatcher.rb +209 -0
- data/lib/raw/dispatcher/format.rb +108 -0
- data/lib/raw/dispatcher/format/atom.rb +31 -0
- data/lib/raw/dispatcher/format/css.rb +0 -0
- data/lib/raw/dispatcher/format/html.rb +42 -0
- data/lib/raw/dispatcher/format/json.rb +31 -0
- data/lib/raw/dispatcher/format/rss.rb +33 -0
- data/lib/raw/dispatcher/format/xoxo.rb +31 -0
- data/lib/raw/dispatcher/mounter.rb +60 -0
- data/lib/raw/dispatcher/router.rb +111 -0
- data/lib/raw/errors.rb +19 -0
- data/lib/raw/helper.rb +86 -0
- data/lib/raw/helper/benchmark.rb +23 -0
- data/lib/raw/helper/buffer.rb +60 -0
- data/lib/raw/helper/cookie.rb +32 -0
- data/lib/raw/helper/debug.rb +28 -0
- data/lib/raw/helper/default.rb +16 -0
- data/lib/raw/helper/feed.rb +451 -0
- data/lib/raw/helper/form.rb +284 -0
- data/lib/raw/helper/javascript.rb +59 -0
- data/lib/raw/helper/layout.rb +40 -0
- data/lib/raw/helper/navigation.rb +87 -0
- data/lib/raw/helper/pager.rb +305 -0
- data/lib/raw/helper/table.rb +247 -0
- data/lib/raw/helper/xhtml.rb +218 -0
- data/lib/raw/helper/xml.rb +125 -0
- data/lib/raw/mixin/magick.rb +35 -0
- data/lib/raw/mixin/sweeper.rb +71 -0
- data/lib/raw/mixin/thumbnails.rb +1 -0
- data/lib/raw/mixin/webfile.rb +165 -0
- data/lib/raw/render.rb +271 -0
- data/lib/raw/render/builder.rb +26 -0
- data/lib/raw/render/caching.rb +81 -0
- data/lib/raw/render/call.rb +43 -0
- data/lib/raw/render/send_file.rb +46 -0
- data/lib/raw/render/stream.rb +39 -0
- data/lib/raw/scaffold.rb +13 -0
- data/lib/raw/scaffold/controller.rb +25 -0
- data/lib/raw/scaffold/model.rb +157 -0
- data/lib/raw/test.rb +5 -0
- data/lib/raw/test/assertions.rb +169 -0
- data/lib/raw/test/context.rb +55 -0
- data/lib/raw/test/testcase.rb +79 -0
- data/lib/raw/util/attr.rb +128 -0
- data/lib/raw/util/encode_uri.rb +149 -0
- data/lib/raw/util/html_filter.rb +538 -0
- data/lib/raw/util/markup.rb +130 -0
- data/test/glue/tc_webfile.rb +1 -0
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/raw_post1.bin +9 -0
- data/test/nitro/adapter/tc_webrick.rb +16 -0
- data/test/nitro/cgi/tc_cookie.rb +14 -0
- data/test/nitro/cgi/tc_request.rb +61 -0
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +25 -0
- data/test/nitro/dispatcher/tc_mounter.rb +47 -0
- data/test/nitro/helper/tc_feed.rb +135 -0
- data/test/nitro/helper/tc_navbar.rb +74 -0
- data/test/nitro/helper/tc_pager.rb +35 -0
- data/test/nitro/helper/tc_table.rb +68 -0
- data/test/nitro/helper/tc_xhtml.rb +19 -0
- data/test/nitro/tc_caching.rb +19 -0
- data/test/nitro/tc_cgi.rb +222 -0
- data/test/nitro/tc_context.rb +17 -0
- data/test/nitro/tc_controller.rb +103 -0
- data/test/nitro/tc_controller_aspect.rb +32 -0
- data/test/nitro/tc_controller_params.rb +885 -0
- data/test/nitro/tc_dispatcher.rb +109 -0
- data/test/nitro/tc_element.rb +85 -0
- data/test/nitro/tc_flash.rb +59 -0
- data/test/nitro/tc_helper.rb +47 -0
- data/test/nitro/tc_render.rb +119 -0
- data/test/nitro/tc_router.rb +61 -0
- data/test/nitro/tc_server.rb +35 -0
- data/test/nitro/tc_session.rb +66 -0
- data/test/nitro/tc_template.rb +71 -0
- data/test/nitro/util/tc_encode_url.rb +87 -0
- data/test/nitro/util/tc_markup.rb +31 -0
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- data/test/public/blog/inc1.xhtml +2 -0
- data/test/public/blog/inc2.xhtml +1 -0
- data/test/public/blog/list.xhtml +9 -0
- data/test/public/dummy_mailer/registration.xhtml +5 -0
- metadata +244 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
require "facets/more/settings"
|
|
4
|
+
require "facets/core/file/self/write"
|
|
5
|
+
require "facets/core/kernel/eigenclass"
|
|
6
|
+
|
|
7
|
+
module Raw
|
|
8
|
+
|
|
9
|
+
# Add output caching to the Render.
|
|
10
|
+
|
|
11
|
+
module Caching
|
|
12
|
+
|
|
13
|
+
# Enable or disable view caching.
|
|
14
|
+
|
|
15
|
+
setting :enabled, :default => true, :doc => "Enable view caching"
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Cache the output (view) of the current action.
|
|
20
|
+
|
|
21
|
+
def cache_output
|
|
22
|
+
return unless (caching_enabled? and caching_allowed?)
|
|
23
|
+
|
|
24
|
+
if self.class.ann(@action, :cache) # or self.class.ann(:self, :cache)
|
|
25
|
+
path = File.join(@context.application.public_dir, @context.path)
|
|
26
|
+
FileUtils.makedirs(File.dirname(path))
|
|
27
|
+
Logger.debug "Caching '#{path}'" if $DBG
|
|
28
|
+
File.write(path, @out)
|
|
29
|
+
end
|
|
30
|
+
rescue => ex
|
|
31
|
+
Logger.error ex.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Explicitly expire the given cached file. If the filename has
|
|
35
|
+
# no extension attach .* to expire the cached files for
|
|
36
|
+
# all format representations.
|
|
37
|
+
#--
|
|
38
|
+
# TODO: use encode_uri
|
|
39
|
+
#++
|
|
40
|
+
|
|
41
|
+
def expire_output(*args)
|
|
42
|
+
path = encode_uri(*args)
|
|
43
|
+
|
|
44
|
+
if File.extname(path).blank?
|
|
45
|
+
# Clear all cached representations.
|
|
46
|
+
if path =~ %r{/$}
|
|
47
|
+
path << "index.*"
|
|
48
|
+
else
|
|
49
|
+
path << ".*"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
path = File.join(Context.current.application.public_dir, path)
|
|
54
|
+
|
|
55
|
+
Logger.debug "Expiring cache files '#{path}'" if $DBG
|
|
56
|
+
FileUtils.rm_rf(path)
|
|
57
|
+
rescue => ex
|
|
58
|
+
# drink it!
|
|
59
|
+
end
|
|
60
|
+
alias_method :delete_output, :expire_output
|
|
61
|
+
|
|
62
|
+
# Enable or disable caching. Can be overriden per controller
|
|
63
|
+
# for extra fine grained caching control.
|
|
64
|
+
|
|
65
|
+
def caching_enabled?
|
|
66
|
+
Caching.enabled
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Is caching allowed for this action (page)? The default
|
|
70
|
+
# implementation does not cache post request or request
|
|
71
|
+
# with query parameters. You can work arround the second
|
|
72
|
+
# 'limitation' by cleverly using Nitro's implicit support
|
|
73
|
+
# for 'nice' URIs.
|
|
74
|
+
|
|
75
|
+
def caching_allowed?
|
|
76
|
+
not (@context.post? or @context.uri =~ /\?/)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Seaside style call/answer methods.
|
|
5
|
+
#++
|
|
6
|
+
|
|
7
|
+
module Render
|
|
8
|
+
|
|
9
|
+
# Call redirects to the given URI but push the original
|
|
10
|
+
# URI in a callstack, so that the target can return by
|
|
11
|
+
# executing answer.
|
|
12
|
+
#
|
|
13
|
+
#--
|
|
14
|
+
# FIXME: dont use yet, you have to encode the branch to
|
|
15
|
+
# make this safe for use.
|
|
16
|
+
#++
|
|
17
|
+
|
|
18
|
+
def call(*args)
|
|
19
|
+
(session[:CALL_STACK] ||= []).push(request.uri)
|
|
20
|
+
redirect(*args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns from a call by poping the callstack.
|
|
24
|
+
# Use force = false to make this mechanism more flexible.
|
|
25
|
+
#--
|
|
26
|
+
# FIXME: don't use yet.
|
|
27
|
+
#++
|
|
28
|
+
|
|
29
|
+
def answer(force = false, status = 303)
|
|
30
|
+
if stack = session[:CALL_STACK] and not stack.empty?
|
|
31
|
+
redirect(stack.pop, :status => status)
|
|
32
|
+
else
|
|
33
|
+
if force
|
|
34
|
+
raise 'Cannot answer, call stack is empty'
|
|
35
|
+
else
|
|
36
|
+
redirect_to_home
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require "raw/render"
|
|
2
|
+
|
|
3
|
+
module Raw
|
|
4
|
+
|
|
5
|
+
module Render
|
|
6
|
+
|
|
7
|
+
# Send a file download to the client.
|
|
8
|
+
#
|
|
9
|
+
# Like render and redirect, the action is exited upon calling
|
|
10
|
+
#
|
|
11
|
+
# [+fname+] That name of the file
|
|
12
|
+
# [+path+] Specifying true mean fname contains the full path.
|
|
13
|
+
# The default, false, uses Server.public_root as the path.
|
|
14
|
+
#
|
|
15
|
+
# [+return+] true on success, false on failure
|
|
16
|
+
#
|
|
17
|
+
# === Examples
|
|
18
|
+
#
|
|
19
|
+
# require "raw/render/send_file"
|
|
20
|
+
#
|
|
21
|
+
# class MyController < Nitro:Controller
|
|
22
|
+
# def download(fname)
|
|
23
|
+
# send_file(fname)
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# class MyController < Nitro:Controller
|
|
28
|
+
# def download
|
|
29
|
+
# send_file("/etc/password", true)
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
|
|
33
|
+
def send_file(fname = nil, fullpath = false)
|
|
34
|
+
fname = fullpath ? fname : "#{@context.application.public_dir}/#{fname}"
|
|
35
|
+
f = File.open(fname, "rb")
|
|
36
|
+
@context.response_headers["Cache-control"] = "private"
|
|
37
|
+
@context.response_headers["Content-Length"] = "#{File.size?(f) || 0}"
|
|
38
|
+
@context.response_headers["Content-Type"] = "application/force-download"
|
|
39
|
+
@context.output_buffer = f
|
|
40
|
+
raise RenderExit
|
|
41
|
+
end
|
|
42
|
+
alias_method :sendfile, :send_file
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "raw/render"
|
|
2
|
+
|
|
3
|
+
module Raw
|
|
4
|
+
|
|
5
|
+
module Render
|
|
6
|
+
|
|
7
|
+
# Enable streaming mode for the current HTTP Response.
|
|
8
|
+
# You can optionally provide an existing IO object for
|
|
9
|
+
# streaming.
|
|
10
|
+
#--
|
|
11
|
+
# This code is considered a hack fix. But it still is useful
|
|
12
|
+
# so for the moment it stays in the distribution.
|
|
13
|
+
#++
|
|
14
|
+
|
|
15
|
+
def stream(io = nil)
|
|
16
|
+
if io
|
|
17
|
+
# Reuse an existing IO if it exists.
|
|
18
|
+
@context.output_buffer = io
|
|
19
|
+
else
|
|
20
|
+
r, w = IO.pipe
|
|
21
|
+
|
|
22
|
+
@context.output_buffer = r
|
|
23
|
+
@out = w
|
|
24
|
+
r.sync = true
|
|
25
|
+
w.class.send(:define_method, :empty?) { false }
|
|
26
|
+
|
|
27
|
+
Thread.new do
|
|
28
|
+
begin
|
|
29
|
+
yield
|
|
30
|
+
ensure
|
|
31
|
+
w.close
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
data/lib/raw/scaffold.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
# Scaffolding is one facet of Nitro's Rapid Application
|
|
4
|
+
# Develpoment (RAD) features. The scaffolder automatically
|
|
5
|
+
# generates common code for managed object and their
|
|
6
|
+
# controllers.
|
|
7
|
+
|
|
8
|
+
module Scaffold
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require "raw/scaffold/model"
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
module Scaffold
|
|
4
|
+
|
|
5
|
+
# Automatically creates a scaffold controller to handle the
|
|
6
|
+
# given models.
|
|
7
|
+
|
|
8
|
+
def self.controller(*models)
|
|
9
|
+
for m in models
|
|
10
|
+
scaffold_controller(m)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.enchant_controller(model)
|
|
15
|
+
eval %{
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Controller
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
# Scaffolding is one facet of Nitro's Rapid Application
|
|
4
|
+
# Develpoment (RAD) features. The scaffolder automatically
|
|
5
|
+
# generates common code for managed object and their
|
|
6
|
+
# controllers.
|
|
7
|
+
|
|
8
|
+
module Scaffold
|
|
9
|
+
|
|
10
|
+
# Automatically enchant all models ?
|
|
11
|
+
|
|
12
|
+
setting :enchant_all_models, :default => false, :doc => 'Automatically enchant all models?'
|
|
13
|
+
|
|
14
|
+
# Enchant all models.
|
|
15
|
+
|
|
16
|
+
def self.all_models
|
|
17
|
+
if Scaffold.enchant_all_models
|
|
18
|
+
self.model(*Og.manager.managed_classes)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# 'Enchant' a model class (typically a managed class,
|
|
23
|
+
# entity). A collection of useful methods are magically
|
|
24
|
+
# added to the class and/or the class instances.
|
|
25
|
+
#
|
|
26
|
+
# * to_s
|
|
27
|
+
# * to_href
|
|
28
|
+
# * to_link
|
|
29
|
+
# * to_edit_href
|
|
30
|
+
# * to_admin_href
|
|
31
|
+
#--
|
|
32
|
+
# to_xxx is used instead of xxx in an attempt to avoid
|
|
33
|
+
# colisions with user defined methods.
|
|
34
|
+
#++
|
|
35
|
+
|
|
36
|
+
def self.model(*classes)
|
|
37
|
+
for c in classes
|
|
38
|
+
enchant_model c
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#--
|
|
43
|
+
# Actually enchant the given class. Override this method
|
|
44
|
+
# to customize for your application. The string calculation
|
|
45
|
+
# code is deliberatly dynamic to work with Ruby's OO
|
|
46
|
+
# features.
|
|
47
|
+
#++
|
|
48
|
+
|
|
49
|
+
def self.enchant_model(klass)
|
|
50
|
+
# Find the controller that handles this model. Unless no
|
|
51
|
+
# controller annotation is defined, try to find a controller
|
|
52
|
+
# of the form: Model::Controller. Some examples:
|
|
53
|
+
#
|
|
54
|
+
# class Ticket
|
|
55
|
+
# ann :self, :controller => SpecialTicketController
|
|
56
|
+
# ..
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# or try to find a Ticket::Controller class.
|
|
60
|
+
|
|
61
|
+
controller = klass.ann(:self, :controller) || klass.constant('Controller')
|
|
62
|
+
|
|
63
|
+
# If the class defines a text_key use it to create more
|
|
64
|
+
# readable (and SEO friendly) URIs.
|
|
65
|
+
|
|
66
|
+
key = klass.ann(:self, :text_key) || 'oid'
|
|
67
|
+
|
|
68
|
+
# to_s
|
|
69
|
+
|
|
70
|
+
if klass.instance_methods.include? 'title'
|
|
71
|
+
define_instance_method klass, :to_s, %{ title }, force = true
|
|
72
|
+
elsif klass.instance_methods.include? 'name'
|
|
73
|
+
define_instance_method klass, :to_s, %{ name }, force = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# to_href
|
|
77
|
+
# ex: /articles/23
|
|
78
|
+
|
|
79
|
+
define_instance_method klass, :to_href, %{
|
|
80
|
+
"\#{#{controller}.mount_path}/read/\#{#{key}}".squeeze('/')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# to_link
|
|
84
|
+
# ex: <a href="/articles/23">The article's title</a>
|
|
85
|
+
|
|
86
|
+
define_instance_method klass, :to_link, %{
|
|
87
|
+
%|<a href="\#{to_href}">\#{to_s}</a>|
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# to_link
|
|
91
|
+
# ex: <a href="/articles/23">The article's title</a>
|
|
92
|
+
|
|
93
|
+
define_class_method klass, :controller, %{
|
|
94
|
+
#{controller}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if defined? OgAdminController
|
|
98
|
+
self.extend(OgAdminHelper)
|
|
99
|
+
|
|
100
|
+
# to_edit_href
|
|
101
|
+
# ex: admin/update/Article/23
|
|
102
|
+
|
|
103
|
+
define_instance_method klass, :to_edit_href, %{
|
|
104
|
+
"\#{OgAdminController.mount_path}/update/#{class_to_name(klass)}/\#{oid}"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# to_admin_href
|
|
108
|
+
# ex: admin/list/Article
|
|
109
|
+
|
|
110
|
+
define_instance_method klass, :to_admin_href, %{
|
|
111
|
+
"\#{AdminController.mount_path}/list/#{class_to_name(klass)}"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# to_admin_href
|
|
115
|
+
# ex: admin/list/Article
|
|
116
|
+
|
|
117
|
+
define_class_method klass, :to_admin_href, %{
|
|
118
|
+
"\#{AdminController.mount_path}/list/#{class_to_name(klass)}"
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#--
|
|
124
|
+
# This helper defines an instance method for the
|
|
125
|
+
# scaffolded class. The method is only defined if the klass
|
|
126
|
+
# does not already respond to it.
|
|
127
|
+
#++
|
|
128
|
+
|
|
129
|
+
def self.define_instance_method(klass, meth, body, force = false)
|
|
130
|
+
if force or (!klass.instance_methods.include? meth.to_s)
|
|
131
|
+
klass.module_eval %{
|
|
132
|
+
def #{meth}
|
|
133
|
+
#{body}
|
|
134
|
+
end
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#--
|
|
140
|
+
# This helper defines an class method for the
|
|
141
|
+
# scaffolded class. The method is only defined if the klass
|
|
142
|
+
# does not already respond to it.
|
|
143
|
+
#++
|
|
144
|
+
|
|
145
|
+
def self.define_class_method(klass, meth, body, force = false)
|
|
146
|
+
if force or (!klass.respond_to? meth.to_s)
|
|
147
|
+
klass.module_eval %{
|
|
148
|
+
def self.#{meth}
|
|
149
|
+
#{body}
|
|
150
|
+
end
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
data/lib/raw/test.rb
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'test/unit/assertions'
|
|
3
|
+
require 'rexml/document'
|
|
4
|
+
|
|
5
|
+
module Test::Unit::Assertions
|
|
6
|
+
|
|
7
|
+
STATUS_MAP = {
|
|
8
|
+
:success => 200,
|
|
9
|
+
:ok => 200,
|
|
10
|
+
:redirect => 307
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# :section: General assertions.
|
|
14
|
+
|
|
15
|
+
# Check the status of the response.
|
|
16
|
+
|
|
17
|
+
def assert_response(options = {})
|
|
18
|
+
unless options.is_a? Hash
|
|
19
|
+
options = { :status => options }
|
|
20
|
+
end
|
|
21
|
+
msg = options[:msg]
|
|
22
|
+
if status = options.fetch(:status, :success)
|
|
23
|
+
status = STATUS_MAP[status] if STATUS_MAP.has_key?(status)
|
|
24
|
+
assert_status(status, msg)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def assert_status(status, msg)
|
|
29
|
+
msg = format_msg("Status not '#{status}'", msg)
|
|
30
|
+
assert_block(msg) { @context.status == status }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#--
|
|
34
|
+
# Compile some helpers.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
for m in [:get, :post, :put, :delete, :head]
|
|
38
|
+
eval %{
|
|
39
|
+
def assert_#{m}(uri, headers = {}, params = {}, session = nil)
|
|
40
|
+
#{m}(uri, headers, params, session)
|
|
41
|
+
assert_response :success
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def assert_output(options = {})
|
|
47
|
+
msg = options[:msg]
|
|
48
|
+
if re = options[:match] || options[:contains]
|
|
49
|
+
assert_output_match(re, msg)
|
|
50
|
+
end
|
|
51
|
+
if re = options[:no_match] || options[:contains_no]
|
|
52
|
+
assert_output_not_match(re, msg)
|
|
53
|
+
end
|
|
54
|
+
if content_type = options[:content_type]
|
|
55
|
+
assert_content_type(content_type, msg)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def assert_output_match(re, msg)
|
|
60
|
+
msg = format_msg("Rendered output does not match '#{re.source}'", msg)
|
|
61
|
+
assert_block(msg) { @context.body =~ Regexp.new(re) }
|
|
62
|
+
end
|
|
63
|
+
alias_method :assert_output_contains, :assert_output_match
|
|
64
|
+
|
|
65
|
+
def assert_output_not_match(re, msg)
|
|
66
|
+
msg = format_msg("Rendered output matches '#{re.source}'", msg)
|
|
67
|
+
assert_block(msg) { @context.out =~ Regexp.new(re) }
|
|
68
|
+
end
|
|
69
|
+
alias_method :assert_output_contains_not, :assert_output_match
|
|
70
|
+
|
|
71
|
+
def assert_content_type(ctype, msg)
|
|
72
|
+
msg = format_msg("Content type is not '#{ctype}' as expected", msg)
|
|
73
|
+
assert_block(msg) { @context.content_type == ctype }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# :section: Session related assertions.
|
|
77
|
+
|
|
78
|
+
def assert_session(options = {})
|
|
79
|
+
msg = options[:msg]
|
|
80
|
+
if key = options[:has]
|
|
81
|
+
assert_session_has(key, msg)
|
|
82
|
+
end
|
|
83
|
+
if key = options[:has_no] || options[:no]
|
|
84
|
+
assert_session_has_no(key, msg)
|
|
85
|
+
end
|
|
86
|
+
if key = options[:key] and value = options[:value]
|
|
87
|
+
assert_session_equal(key, value, msg)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def assert_session_has(key, msg = nil)
|
|
92
|
+
msg = format_msg("Object '#{key}' not found in session", msg)
|
|
93
|
+
assert_block(msg) { @context.session[key] }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def assert_session_has_no(key, msg = nil)
|
|
97
|
+
msg = format_msg("Unexpected object '#{key}' found in session", msg)
|
|
98
|
+
assert_block(msg) { !@context.session[key] }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def assert_session_equal(key, value, msg = nil)
|
|
102
|
+
msg = format_msg("The value of session object '#{key}' is '#{@context.session[key]}' but was expected '#{value}'", msg)
|
|
103
|
+
assert_block(msg) { @context.session[key] == value }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# :section: Cookies related assertions.
|
|
107
|
+
|
|
108
|
+
def assert_cookie(options = {})
|
|
109
|
+
msg = options[:msg]
|
|
110
|
+
if key = options[:has]
|
|
111
|
+
assert_cookie_has(key, msg)
|
|
112
|
+
end
|
|
113
|
+
if key = options[:has_no] || options[:no]
|
|
114
|
+
assert_cookie_has_no(key, msg)
|
|
115
|
+
end
|
|
116
|
+
if key = options[:key] and value = options[:value]
|
|
117
|
+
assert_cookie_equal(key, value, msg)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def assert_cookie_has(name, msg = nil)
|
|
122
|
+
msg = format_msg("Cookie '#{name}' not found", msg)
|
|
123
|
+
assert_block(msg) { @context.response_cookie(name) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def assert_cookie_has_no(name, msg = nil)
|
|
127
|
+
msg = format_msg("Unexpected cookie '#{name}' found", msg)
|
|
128
|
+
assert_block(msg) { !@context.response_cookie(name) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def assert_cookie_equal(name, value, msg = nil)
|
|
132
|
+
unless cookie = @context.response_cookie(name)
|
|
133
|
+
msg = format_msg("Cookie '#{name}' not found", msg)
|
|
134
|
+
assert_block(msg) { false }
|
|
135
|
+
end
|
|
136
|
+
msg = format_msg("The value of cookie '#{name}' is '#{cookie.value}' but was expected '#{value}'", msg)
|
|
137
|
+
assert_block(msg) { cookie.value == value }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# :section: Nitro::Template related assertions.
|
|
141
|
+
|
|
142
|
+
# :section: Redirection assertions.
|
|
143
|
+
|
|
144
|
+
def assert_redirected(options = {})
|
|
145
|
+
msg = options[:msg]
|
|
146
|
+
|
|
147
|
+
msg = format_msg("No redirection (status = #{@context.status})", msg)
|
|
148
|
+
assert_block(msg) { @context.redirect? }
|
|
149
|
+
|
|
150
|
+
if to = options[:to]
|
|
151
|
+
msg = format_msg("Not redirected to '#{to}'", msg)
|
|
152
|
+
assert_block(msg) { @context.response_headers['location'] == "http://#{to}" }
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def assert_not_redirected(options = {})
|
|
157
|
+
msg = options[:msg]
|
|
158
|
+
msg = format_msg("Unexpected redirection (location = '#{@context.response_headers['location']}')", msg)
|
|
159
|
+
assert_block(msg) { !@context.redirect? }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# :section: Utility methods
|
|
163
|
+
|
|
164
|
+
def format_msg(message, extra) # :nodoc:
|
|
165
|
+
extra += ', ' if extra
|
|
166
|
+
return "#{extra}#{message}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|