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.
Files changed (148) hide show
  1. data/doc/CONTRIBUTORS +106 -0
  2. data/doc/LICENSE +32 -0
  3. data/doc/coding_conventions.txt +11 -0
  4. data/lib/raw.rb +42 -0
  5. data/lib/raw/adapter.rb +113 -0
  6. data/lib/raw/adapter/cgi.rb +41 -0
  7. data/lib/raw/adapter/fastcgi.rb +48 -0
  8. data/lib/raw/adapter/mongrel.rb +146 -0
  9. data/lib/raw/adapter/script.rb +94 -0
  10. data/lib/raw/adapter/webrick.rb +144 -0
  11. data/lib/raw/adapter/webrick/vcr.rb +91 -0
  12. data/lib/raw/cgi.rb +323 -0
  13. data/lib/raw/cgi/cookie.rb +47 -0
  14. data/lib/raw/cgi/http.rb +62 -0
  15. data/lib/raw/compiler.rb +138 -0
  16. data/lib/raw/compiler/filter/cleanup.rb +21 -0
  17. data/lib/raw/compiler/filter/elements.rb +166 -0
  18. data/lib/raw/compiler/filter/elements/element.rb +210 -0
  19. data/lib/raw/compiler/filter/localization.rb +23 -0
  20. data/lib/raw/compiler/filter/markup.rb +32 -0
  21. data/lib/raw/compiler/filter/morph.rb +123 -0
  22. data/lib/raw/compiler/filter/morph/each.rb +34 -0
  23. data/lib/raw/compiler/filter/morph/for.rb +11 -0
  24. data/lib/raw/compiler/filter/morph/if.rb +26 -0
  25. data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
  26. data/lib/raw/compiler/filter/morph/standard.rb +55 -0
  27. data/lib/raw/compiler/filter/morph/times.rb +27 -0
  28. data/lib/raw/compiler/filter/script.rb +116 -0
  29. data/lib/raw/compiler/filter/squeeze.rb +16 -0
  30. data/lib/raw/compiler/filter/static_include.rb +74 -0
  31. data/lib/raw/compiler/filter/template.rb +121 -0
  32. data/lib/raw/compiler/reloader.rb +96 -0
  33. data/lib/raw/context.rb +154 -0
  34. data/lib/raw/context/flash.rb +157 -0
  35. data/lib/raw/context/global.rb +88 -0
  36. data/lib/raw/context/request.rb +338 -0
  37. data/lib/raw/context/response.rb +57 -0
  38. data/lib/raw/context/session.rb +198 -0
  39. data/lib/raw/context/session/drb.rb +11 -0
  40. data/lib/raw/context/session/file.rb +15 -0
  41. data/lib/raw/context/session/memcached.rb +13 -0
  42. data/lib/raw/context/session/memory.rb +12 -0
  43. data/lib/raw/context/session/og.rb +15 -0
  44. data/lib/raw/context/session/pstore.rb +13 -0
  45. data/lib/raw/control.rb +18 -0
  46. data/lib/raw/control/attribute.rb +91 -0
  47. data/lib/raw/control/attribute/checkbox.rb +25 -0
  48. data/lib/raw/control/attribute/datetime.rb +21 -0
  49. data/lib/raw/control/attribute/file.rb +20 -0
  50. data/lib/raw/control/attribute/fixnum.rb +26 -0
  51. data/lib/raw/control/attribute/float.rb +26 -0
  52. data/lib/raw/control/attribute/options.rb +38 -0
  53. data/lib/raw/control/attribute/password.rb +16 -0
  54. data/lib/raw/control/attribute/text.rb +16 -0
  55. data/lib/raw/control/attribute/textarea.rb +16 -0
  56. data/lib/raw/control/none.rb +16 -0
  57. data/lib/raw/control/relation.rb +59 -0
  58. data/lib/raw/control/relation/belongs_to.rb +0 -0
  59. data/lib/raw/control/relation/has_many.rb +97 -0
  60. data/lib/raw/control/relation/joins_many.rb +0 -0
  61. data/lib/raw/control/relation/many_to_many.rb +0 -0
  62. data/lib/raw/control/relation/refers_to.rb +29 -0
  63. data/lib/raw/controller.rb +37 -0
  64. data/lib/raw/controller/publishable.rb +160 -0
  65. data/lib/raw/dispatcher.rb +209 -0
  66. data/lib/raw/dispatcher/format.rb +108 -0
  67. data/lib/raw/dispatcher/format/atom.rb +31 -0
  68. data/lib/raw/dispatcher/format/css.rb +0 -0
  69. data/lib/raw/dispatcher/format/html.rb +42 -0
  70. data/lib/raw/dispatcher/format/json.rb +31 -0
  71. data/lib/raw/dispatcher/format/rss.rb +33 -0
  72. data/lib/raw/dispatcher/format/xoxo.rb +31 -0
  73. data/lib/raw/dispatcher/mounter.rb +60 -0
  74. data/lib/raw/dispatcher/router.rb +111 -0
  75. data/lib/raw/errors.rb +19 -0
  76. data/lib/raw/helper.rb +86 -0
  77. data/lib/raw/helper/benchmark.rb +23 -0
  78. data/lib/raw/helper/buffer.rb +60 -0
  79. data/lib/raw/helper/cookie.rb +32 -0
  80. data/lib/raw/helper/debug.rb +28 -0
  81. data/lib/raw/helper/default.rb +16 -0
  82. data/lib/raw/helper/feed.rb +451 -0
  83. data/lib/raw/helper/form.rb +284 -0
  84. data/lib/raw/helper/javascript.rb +59 -0
  85. data/lib/raw/helper/layout.rb +40 -0
  86. data/lib/raw/helper/navigation.rb +87 -0
  87. data/lib/raw/helper/pager.rb +305 -0
  88. data/lib/raw/helper/table.rb +247 -0
  89. data/lib/raw/helper/xhtml.rb +218 -0
  90. data/lib/raw/helper/xml.rb +125 -0
  91. data/lib/raw/mixin/magick.rb +35 -0
  92. data/lib/raw/mixin/sweeper.rb +71 -0
  93. data/lib/raw/mixin/thumbnails.rb +1 -0
  94. data/lib/raw/mixin/webfile.rb +165 -0
  95. data/lib/raw/render.rb +271 -0
  96. data/lib/raw/render/builder.rb +26 -0
  97. data/lib/raw/render/caching.rb +81 -0
  98. data/lib/raw/render/call.rb +43 -0
  99. data/lib/raw/render/send_file.rb +46 -0
  100. data/lib/raw/render/stream.rb +39 -0
  101. data/lib/raw/scaffold.rb +13 -0
  102. data/lib/raw/scaffold/controller.rb +25 -0
  103. data/lib/raw/scaffold/model.rb +157 -0
  104. data/lib/raw/test.rb +5 -0
  105. data/lib/raw/test/assertions.rb +169 -0
  106. data/lib/raw/test/context.rb +55 -0
  107. data/lib/raw/test/testcase.rb +79 -0
  108. data/lib/raw/util/attr.rb +128 -0
  109. data/lib/raw/util/encode_uri.rb +149 -0
  110. data/lib/raw/util/html_filter.rb +538 -0
  111. data/lib/raw/util/markup.rb +130 -0
  112. data/test/glue/tc_webfile.rb +1 -0
  113. data/test/nitro/CONFIG.rb +3 -0
  114. data/test/nitro/adapter/raw_post1.bin +9 -0
  115. data/test/nitro/adapter/tc_webrick.rb +16 -0
  116. data/test/nitro/cgi/tc_cookie.rb +14 -0
  117. data/test/nitro/cgi/tc_request.rb +61 -0
  118. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  119. data/test/nitro/compiler/tc_compiler.rb +25 -0
  120. data/test/nitro/dispatcher/tc_mounter.rb +47 -0
  121. data/test/nitro/helper/tc_feed.rb +135 -0
  122. data/test/nitro/helper/tc_navbar.rb +74 -0
  123. data/test/nitro/helper/tc_pager.rb +35 -0
  124. data/test/nitro/helper/tc_table.rb +68 -0
  125. data/test/nitro/helper/tc_xhtml.rb +19 -0
  126. data/test/nitro/tc_caching.rb +19 -0
  127. data/test/nitro/tc_cgi.rb +222 -0
  128. data/test/nitro/tc_context.rb +17 -0
  129. data/test/nitro/tc_controller.rb +103 -0
  130. data/test/nitro/tc_controller_aspect.rb +32 -0
  131. data/test/nitro/tc_controller_params.rb +885 -0
  132. data/test/nitro/tc_dispatcher.rb +109 -0
  133. data/test/nitro/tc_element.rb +85 -0
  134. data/test/nitro/tc_flash.rb +59 -0
  135. data/test/nitro/tc_helper.rb +47 -0
  136. data/test/nitro/tc_render.rb +119 -0
  137. data/test/nitro/tc_router.rb +61 -0
  138. data/test/nitro/tc_server.rb +35 -0
  139. data/test/nitro/tc_session.rb +66 -0
  140. data/test/nitro/tc_template.rb +71 -0
  141. data/test/nitro/util/tc_encode_url.rb +87 -0
  142. data/test/nitro/util/tc_markup.rb +31 -0
  143. data/test/public/blog/another/very_litle/index.xhtml +1 -0
  144. data/test/public/blog/inc1.xhtml +2 -0
  145. data/test/public/blog/inc2.xhtml +1 -0
  146. data/test/public/blog/list.xhtml +9 -0
  147. data/test/public/dummy_mailer/registration.xhtml +5 -0
  148. 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
@@ -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
@@ -0,0 +1,5 @@
1
+ $NITRO_NO_ENVIRONMENT = true
2
+ $NITRO_NO_INVOKE = true
3
+
4
+ require 'nitro/test/assertions'
5
+ require 'nitro/test/testcase'
@@ -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