merb 0.4.1 → 0.4.2
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/README +138 -56
- data/Rakefile +23 -8
- data/app_generators/merb/templates/Rakefile +13 -0
- data/app_generators/merb/templates/app/helpers/global_helper.rb +1 -1
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +12 -3
- data/app_generators/merb/templates/config/merb.yml +14 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +6 -0
- data/app_generators/merb/templates/test/test_helper.rb +1 -0
- data/lib/merb.rb +27 -7
- data/lib/merb/abstract_controller.rb +76 -36
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/constants.rb +2 -4
- data/lib/merb/controller.rb +44 -2
- data/lib/merb/core_ext/get_args.rb +23 -4
- data/lib/merb/core_ext/hash.rb +16 -11
- data/lib/merb/core_ext/inflections.rb +1 -1
- data/lib/merb/core_ext/kernel.rb +106 -26
- data/lib/merb/core_ext/numeric.rb +1 -1
- data/lib/merb/core_ext/string.rb +10 -13
- data/lib/merb/dispatcher.rb +2 -2
- data/lib/merb/exceptions.rb +3 -1
- data/lib/merb/logger.rb +15 -6
- data/lib/merb/mail_controller.rb +18 -2
- data/lib/merb/mailer.rb +1 -1
- data/lib/merb/mixins/controller.rb +64 -228
- data/lib/merb/mixins/erubis_capture.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +258 -0
- data/lib/merb/mixins/render.rb +45 -24
- data/lib/merb/mixins/responder.rb +89 -18
- data/lib/merb/mixins/view_context.rb +32 -5
- data/lib/merb/mixins/web_controller.rb +8 -1
- data/lib/merb/mongrel_handler.rb +27 -17
- data/lib/merb/part_controller.rb +10 -0
- data/lib/merb/request.rb +34 -14
- data/lib/merb/router.rb +77 -45
- data/lib/merb/server.rb +116 -72
- data/lib/merb/session/cookie_store.rb +14 -22
- data/lib/merb/session/mem_cache_session.rb +2 -2
- data/lib/merb/session/memory_session.rb +12 -1
- data/lib/merb/template/erubis.rb +31 -0
- data/lib/merb/template/haml.rb +4 -14
- data/lib/merb/template/xml_builder.rb +1 -1
- data/lib/merb/test/helper.rb +90 -18
- data/lib/merb/test/rspec.rb +145 -74
- data/lib/merb/version.rb +11 -0
- data/lib/merb/view_context.rb +3 -6
- data/lib/patch +69 -0
- data/lib/tasks/merb.rake +1 -1
- data/spec/fixtures/config/environments/environment_config_test.yml +1 -0
- data/spec/fixtures/controllers/render_spec_controllers.rb +63 -4
- data/spec/fixtures/views/examples/template_throw_content_without_block.html.erb +3 -0
- data/spec/fixtures/views/partials/_erubis.html.erb +1 -1
- data/spec/merb/abstract_controller_spec.rb +1 -0
- data/spec/merb/controller_filters_spec.rb +68 -3
- data/spec/merb/controller_spec.rb +35 -68
- data/spec/merb/cookie_store_spec.rb +7 -20
- data/spec/merb/core_ext_spec.rb +35 -1
- data/spec/merb/dispatch_spec.rb +8 -2
- data/spec/merb/generator_spec.rb +12 -4
- data/spec/merb/mail_controller_spec.rb +33 -0
- data/spec/merb/part_controller_spec.rb +33 -1
- data/spec/merb/render_spec.rb +74 -0
- data/spec/merb/request_spec.rb +43 -0
- data/spec/merb/responder_spec.rb +1 -0
- data/spec/merb/router_spec.rb +118 -13
- data/spec/merb/server_spec.rb +19 -0
- data/spec/merb/view_context_spec.rb +31 -3
- data/spec/spec_helper.rb +8 -0
- data/spec/spec_helpers/url_shared_behaviour.rb +112 -0
- metadata +124 -87
data/lib/merb/template/erubis.rb
CHANGED
@@ -3,6 +3,37 @@ module Merb # :nodoc:
|
|
3
3
|
|
4
4
|
class ErubisViewContext < ViewContext
|
5
5
|
include ::Merb::ErubisCaptureMixin
|
6
|
+
|
7
|
+
def partial(template, opts={})
|
8
|
+
|
9
|
+
unless @_template_format
|
10
|
+
@web_controller.choose_template_format(Merb.available_mime_types, {})
|
11
|
+
end
|
12
|
+
|
13
|
+
template = @web_controller._cached_partials["#{template}.#{@_template_format}"] ||=
|
14
|
+
@web_controller.send(:find_partial, template)
|
15
|
+
|
16
|
+
template_method = template.gsub(/[^\.a-zA-Z0-9]/, "__").gsub(/\./, "_")
|
17
|
+
|
18
|
+
unless self.respond_to?(template_method)
|
19
|
+
raise Merb::ControllerExceptions::TemplateNotFound, "No template matched at #{template}"
|
20
|
+
end
|
21
|
+
|
22
|
+
if with = opts.delete(:with)
|
23
|
+
as = opts.delete(:as) || template.match(/(.*\/_)([^\.]*)/)[2]
|
24
|
+
@_merb_partial_locals = opts
|
25
|
+
sent_template = [with].flatten.map do |temp|
|
26
|
+
@_merb_partial_locals[as.to_sym] = temp
|
27
|
+
send(template_method)
|
28
|
+
end.join
|
29
|
+
else
|
30
|
+
@_merb_partial_locals = opts
|
31
|
+
sent_template = send(template_method)
|
32
|
+
end
|
33
|
+
|
34
|
+
return sent_template if sent_template
|
35
|
+
|
36
|
+
end
|
6
37
|
end
|
7
38
|
|
8
39
|
# Module to allow you to use Embedded Ruby templates through Erubis["http://www.kuwata-lab.com/erubis/"].
|
data/lib/merb/template/haml.rb
CHANGED
@@ -9,7 +9,7 @@ module Haml
|
|
9
9
|
module Helpers
|
10
10
|
|
11
11
|
def _buffer( binding )
|
12
|
-
@_buffer
|
12
|
+
@_buffer = eval( "_erbout", binding )
|
13
13
|
end
|
14
14
|
|
15
15
|
alias_method :capture, :capture_haml
|
@@ -35,12 +35,12 @@ module Merb
|
|
35
35
|
opts, text, file, view_context = options.values_at(:opts, :text, :file, :view_context)
|
36
36
|
|
37
37
|
begin
|
38
|
-
|
39
|
-
opts[:locals] = {}
|
38
|
+
|
39
|
+
opts[:locals] = {} # Merb handles the locals
|
40
40
|
|
41
41
|
template = text ? text : load_template(file)
|
42
42
|
|
43
|
-
haml = ::Haml::Engine.new(
|
43
|
+
haml = ::Haml::Engine.new(template, opts)
|
44
44
|
haml.to_html(view_context)
|
45
45
|
rescue
|
46
46
|
# ::Haml::Engine often inserts a bogus "(haml):#{line_number}" entry in the backtrace.
|
@@ -67,16 +67,6 @@ module Merb
|
|
67
67
|
return template
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
71
|
-
def build_locals(locals)
|
72
|
-
locals_code = ""
|
73
|
-
if locals
|
74
|
-
locals.keys.each do |key|
|
75
|
-
locals_code << "- #{key} = @_merb_partial_locals[:#{key}]\n"
|
76
|
-
end
|
77
|
-
end
|
78
|
-
locals_code
|
79
|
-
end
|
80
70
|
|
81
71
|
def cache_template?(path)
|
82
72
|
return false unless ::Merb::Server.config[:cache_templates]
|
@@ -28,7 +28,7 @@ module Merb
|
|
28
28
|
def transform(options = {})
|
29
29
|
opts, text, file, view_context = options.values_at(:opts, :text, :file, :view_context)
|
30
30
|
xml_body = text ? text : IO.read(file)
|
31
|
-
view_context.headers['Content-Type']
|
31
|
+
view_context.headers['Content-Type'] ||= 'application/xml'
|
32
32
|
view_context.headers['Encoding'] = 'UTF-8'
|
33
33
|
view_context.instance_eval %{
|
34
34
|
xml = Builder::XmlMarkup.new :indent => 2
|
data/lib/merb/test/helper.rb
CHANGED
@@ -2,30 +2,102 @@ require 'merb/test/fake_request'
|
|
2
2
|
require 'merb/test/hpricot'
|
3
3
|
include HpricotTestHelper
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Merb
|
6
|
+
module Test
|
7
|
+
module Helper
|
8
|
+
# Create a FakeRequest suitable for passing to Controller.build
|
9
|
+
def fake_request(path="/",method='GET')
|
10
|
+
method = method.to_s.upcase
|
11
|
+
Merb::Test::FakeRequest.with(path, :request_method => method)
|
12
|
+
end
|
10
13
|
|
11
|
-
# Turn a named route into a string with the path
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
# Turn a named route into a string with the path
|
15
|
+
# This is the same method as is found in the controller
|
16
|
+
def url(name, *args)
|
17
|
+
Merb::Router.generate(name, *args)
|
18
|
+
end
|
15
19
|
|
16
20
|
|
17
|
-
# For integration/functional testing
|
21
|
+
# For integration/functional testing
|
18
22
|
|
19
23
|
|
20
|
-
def request(verb, path)
|
21
|
-
|
22
|
-
|
24
|
+
def request(verb, path)
|
25
|
+
response = StringIO.new
|
26
|
+
@request = Merb::Test::FakeRequest.with(path, :request_method => (verb.to_s.upcase rescue 'GET'))
|
23
27
|
|
24
|
-
|
28
|
+
yield @request if block_given?
|
25
29
|
|
26
|
-
|
27
|
-
end
|
30
|
+
@controller, @action = Merb::Dispatcher.handle @request, response
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(path)
|
34
|
+
request("GET",path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def controller
|
38
|
+
@controller
|
39
|
+
end
|
40
|
+
|
41
|
+
[:body, :status, :params, :cookies, :headers,
|
42
|
+
:session, :response, :route].each do |method|
|
43
|
+
define_method method do
|
44
|
+
controller ? controller.send(method) : nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks that a route is made to the correct controller etc
|
49
|
+
#
|
50
|
+
# === Example
|
51
|
+
# with_route("/pages/1", "PUT") do |params|
|
52
|
+
# params[:controller].should == "pages"
|
53
|
+
# params[:action].should == "update"
|
54
|
+
# params[:id].should == "1"
|
55
|
+
# end
|
56
|
+
def with_route(the_path, _method = "GET")
|
57
|
+
result = Merb::Router.match(fake_request(the_path, _method), {})
|
58
|
+
yield result[1] if block_given?
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
def fixtures(*files)
|
63
|
+
files.each do |name|
|
64
|
+
klass = Kernel::const_get(Inflector.classify(Inflector.singularize(name)))
|
65
|
+
entries = YAML::load_file(File.dirname(__FILE__) + "/fixtures/#{name}.yaml")
|
66
|
+
entries.each do |name, entry|
|
67
|
+
klass::create(entry)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Dispatches an action to a controller. Defaults to index.
|
74
|
+
# The opts hash, if provided will act as the params hash in the controller
|
75
|
+
# and the params method in the controller is infact the provided opts hash
|
76
|
+
# This controller is based on a fake_request and does not go through the router
|
77
|
+
#
|
78
|
+
# === Simple Example
|
79
|
+
# {{{
|
80
|
+
# controller, result = dispatch_to(Pages, :show, :id => 1, :title => "blah")
|
81
|
+
# }}}
|
82
|
+
#
|
83
|
+
# === Complex Example
|
84
|
+
# By providing a block to the dispatch_to method, the controller may be stubbed or mocked prior to the
|
85
|
+
# actual dispatch action being called.
|
86
|
+
# {{{
|
87
|
+
# controller, result = dispatch_to(Pages, :show, :id => 1) do |controller|
|
88
|
+
# controller.stub!(:render).and_return("rendered response")
|
89
|
+
# end
|
90
|
+
# }}}
|
91
|
+
def dispatch_to(controller, action = :index, opts = {})
|
92
|
+
klass = controller.class == Class ? controller : controller.class
|
93
|
+
klass.stub!(:find_by_title).and_return(@page)
|
94
|
+
the_controller = klass.build(fake_request)
|
95
|
+
the_controller.stub!(:params).and_return(opts.merge!(:controller => "#{klass.name.downcase}", :action => action.to_s))
|
28
96
|
|
29
|
-
|
30
|
-
|
97
|
+
yield the_controller if block_given?
|
98
|
+
@controller = the_controller
|
99
|
+
[the_controller, the_controller.dispatch(action.to_sym)]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
31
103
|
end
|
data/lib/merb/test/rspec.rb
CHANGED
@@ -1,93 +1,164 @@
|
|
1
1
|
require 'hpricot'
|
2
2
|
require 'spec'
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
3
|
+
module Merb
|
4
|
+
module Test
|
5
|
+
module MerbRspecControllerRedirect
|
6
|
+
class BeRedirect
|
7
|
+
def matches?(target)
|
8
|
+
@target = target
|
9
|
+
target == 302
|
10
|
+
end
|
11
|
+
def failure_message
|
12
|
+
"expected to redirect"
|
13
|
+
end
|
14
|
+
def negative_failure_message
|
15
|
+
"expected not to redirect"
|
16
|
+
end
|
17
17
|
end
|
18
|
-
!@document.search(@expected).empty?
|
19
|
-
end
|
20
|
-
|
21
|
-
def failure_message
|
22
|
-
"expected following text to match selector #{@expected}:\n#{@document}"
|
23
|
-
end
|
24
18
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@errors = []
|
37
|
-
unless target.include?("<#{@name}")
|
38
|
-
@errors << "Expected a <#{@name}>, but was #{target}"
|
19
|
+
class Redirect
|
20
|
+
def matches?(target)
|
21
|
+
@target = target
|
22
|
+
BeRedirect.new.matches?(target.status)
|
23
|
+
end
|
24
|
+
def failure_message
|
25
|
+
"expected #{@target.inspect} to redirect"
|
26
|
+
end
|
27
|
+
def negative_failure_message
|
28
|
+
"expected #{@target.inspect} not to redirect"
|
29
|
+
end
|
39
30
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
31
|
+
|
32
|
+
class RedirectTo
|
33
|
+
def initialize(expected)
|
34
|
+
@expected = expected
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches?(target)
|
38
|
+
@target = target.headers['Location']
|
39
|
+
@redirected = BeRedirect.new.matches?(target.status)
|
40
|
+
@target == @expected
|
41
|
+
end
|
42
|
+
|
43
|
+
def failure_message
|
44
|
+
msg = "expected a redirect to <#{@expected}>, but "
|
45
|
+
if @redirected
|
46
|
+
msg << "found one to <#{@target}>"
|
47
|
+
else
|
48
|
+
msg << "there was no redirect"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def negative_failure_message
|
53
|
+
"expected not to redirect to <#{@expected}>, but did anyway"
|
43
54
|
end
|
44
55
|
end
|
45
|
-
|
56
|
+
|
57
|
+
def be_redirect
|
58
|
+
BeRedirect.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def redirect
|
62
|
+
Redirect.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def redirect_to(expected)
|
66
|
+
RedirectTo.new(expected)
|
67
|
+
end
|
46
68
|
end
|
69
|
+
|
70
|
+
module RspecMatchers
|
71
|
+
class HaveSelector
|
72
|
+
def initialize(expected)
|
73
|
+
@expected = expected
|
74
|
+
end
|
47
75
|
|
48
|
-
|
49
|
-
|
50
|
-
|
76
|
+
def matches?(stringlike)
|
77
|
+
@document = case stringlike
|
78
|
+
when Hpricot::Elem
|
79
|
+
stringlike
|
80
|
+
when StringIO
|
81
|
+
Hpricot.parse(stringlike.string)
|
82
|
+
else
|
83
|
+
Hpricot.parse(stringlike)
|
84
|
+
end
|
85
|
+
!@document.search(@expected).empty?
|
86
|
+
end
|
51
87
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
88
|
+
def failure_message
|
89
|
+
"expected following text to match selector #{@expected}:\n#{@document}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def negative_failure_message
|
93
|
+
"expected following text to not match selector #{@expected}:\n#{@document}"
|
94
|
+
end
|
95
|
+
end
|
56
96
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
97
|
+
class MatchTag
|
98
|
+
def initialize(name, attrs)
|
99
|
+
@name, @attrs = name, attrs
|
100
|
+
@content = @attrs.delete(:content)
|
101
|
+
end
|
102
|
+
|
103
|
+
def matches?(target)
|
104
|
+
@errors = []
|
105
|
+
unless target.include?("<#{@name}")
|
106
|
+
@errors << "Expected a <#{@name}>, but was #{target}"
|
107
|
+
end
|
108
|
+
@attrs.each do |attr, val|
|
109
|
+
unless target.include?("#{attr}=\"#{val}\"")
|
110
|
+
@errors << "Expected #{attr}=\"#{val}\", but was #{target}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if @content
|
114
|
+
unless target.include?(">#{@content}<")
|
115
|
+
@errors << "Expected #{target} to include #{@content}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@errors.size == 0
|
119
|
+
end
|
61
120
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
121
|
+
def failure_message
|
122
|
+
@errors[0]
|
123
|
+
end
|
124
|
+
|
125
|
+
def negative_failure_message
|
126
|
+
"Expected not to match against <#{@name} #{@attrs.map{ |a,v| "#{a}=\"#{v}\"" }.join(" ")}> tag, but it matched"
|
67
127
|
end
|
68
128
|
end
|
69
|
-
|
70
|
-
|
129
|
+
|
130
|
+
class NotMatchTag
|
131
|
+
def initialize(attrs)
|
132
|
+
@attrs = attrs
|
133
|
+
end
|
71
134
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
135
|
+
def matches?(target)
|
136
|
+
@errors = []
|
137
|
+
@attrs.each do |attr, val|
|
138
|
+
if target.include?("#{attr}=\"#{val}\"")
|
139
|
+
@errors << "Should not include #{attr}=\"#{val}\", but was #{target}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
@errors.size == 0
|
143
|
+
end
|
144
|
+
|
145
|
+
def failure_message
|
146
|
+
@errors[0]
|
147
|
+
end
|
148
|
+
end
|
76
149
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
150
|
+
def match_tag(name, attrs)
|
151
|
+
MatchTag.new(name, attrs)
|
152
|
+
end
|
153
|
+
def not_match_tag(attrs)
|
154
|
+
NotMatchTag.new(attrs)
|
155
|
+
end
|
83
156
|
|
84
|
-
|
85
|
-
|
157
|
+
def have_selector(expected)
|
158
|
+
HaveSelector.new(expected)
|
159
|
+
end
|
160
|
+
alias_method :match_selector, :have_selector
|
161
|
+
# alias_method :match_regex, :match
|
162
|
+
end
|
86
163
|
end
|
87
|
-
alias_method :match_selector, :have_selector
|
88
|
-
# alias_method :match_regex, :match
|
89
|
-
end
|
90
|
-
|
91
|
-
Spec::Runner.configure do |config|
|
92
|
-
config.include(MerbRspecMatchers)
|
93
164
|
end
|
data/lib/merb/version.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Merb
|
2
|
+
VERSION='0.4.2' unless defined?(::Merb::VERSION)
|
3
|
+
|
4
|
+
# Merb::RELEASE meanings:
|
5
|
+
# 'svn' : unreleased
|
6
|
+
# 'pre' : pre-release Gem candidates
|
7
|
+
# nil : released
|
8
|
+
# You should never check in to trunk with this changed. It should
|
9
|
+
# stay 'svn'. Change it to nil in release tags.
|
10
|
+
RELEASE='svn' unless defined?(::Merb::RELEASE)
|
11
|
+
end
|