merb 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/README +138 -56
  2. data/Rakefile +23 -8
  3. data/app_generators/merb/templates/Rakefile +13 -0
  4. data/app_generators/merb/templates/app/helpers/global_helper.rb +1 -1
  5. data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +12 -3
  6. data/app_generators/merb/templates/config/merb.yml +14 -1
  7. data/app_generators/merb/templates/spec/spec_helper.rb +6 -0
  8. data/app_generators/merb/templates/test/test_helper.rb +1 -0
  9. data/lib/merb.rb +27 -7
  10. data/lib/merb/abstract_controller.rb +76 -36
  11. data/lib/merb/caching/store/memcache.rb +20 -0
  12. data/lib/merb/constants.rb +2 -4
  13. data/lib/merb/controller.rb +44 -2
  14. data/lib/merb/core_ext/get_args.rb +23 -4
  15. data/lib/merb/core_ext/hash.rb +16 -11
  16. data/lib/merb/core_ext/inflections.rb +1 -1
  17. data/lib/merb/core_ext/kernel.rb +106 -26
  18. data/lib/merb/core_ext/numeric.rb +1 -1
  19. data/lib/merb/core_ext/string.rb +10 -13
  20. data/lib/merb/dispatcher.rb +2 -2
  21. data/lib/merb/exceptions.rb +3 -1
  22. data/lib/merb/logger.rb +15 -6
  23. data/lib/merb/mail_controller.rb +18 -2
  24. data/lib/merb/mailer.rb +1 -1
  25. data/lib/merb/mixins/controller.rb +64 -228
  26. data/lib/merb/mixins/erubis_capture.rb +1 -1
  27. data/lib/merb/mixins/general_controller.rb +258 -0
  28. data/lib/merb/mixins/render.rb +45 -24
  29. data/lib/merb/mixins/responder.rb +89 -18
  30. data/lib/merb/mixins/view_context.rb +32 -5
  31. data/lib/merb/mixins/web_controller.rb +8 -1
  32. data/lib/merb/mongrel_handler.rb +27 -17
  33. data/lib/merb/part_controller.rb +10 -0
  34. data/lib/merb/request.rb +34 -14
  35. data/lib/merb/router.rb +77 -45
  36. data/lib/merb/server.rb +116 -72
  37. data/lib/merb/session/cookie_store.rb +14 -22
  38. data/lib/merb/session/mem_cache_session.rb +2 -2
  39. data/lib/merb/session/memory_session.rb +12 -1
  40. data/lib/merb/template/erubis.rb +31 -0
  41. data/lib/merb/template/haml.rb +4 -14
  42. data/lib/merb/template/xml_builder.rb +1 -1
  43. data/lib/merb/test/helper.rb +90 -18
  44. data/lib/merb/test/rspec.rb +145 -74
  45. data/lib/merb/version.rb +11 -0
  46. data/lib/merb/view_context.rb +3 -6
  47. data/lib/patch +69 -0
  48. data/lib/tasks/merb.rake +1 -1
  49. data/spec/fixtures/config/environments/environment_config_test.yml +1 -0
  50. data/spec/fixtures/controllers/render_spec_controllers.rb +63 -4
  51. data/spec/fixtures/views/examples/template_throw_content_without_block.html.erb +3 -0
  52. data/spec/fixtures/views/partials/_erubis.html.erb +1 -1
  53. data/spec/merb/abstract_controller_spec.rb +1 -0
  54. data/spec/merb/controller_filters_spec.rb +68 -3
  55. data/spec/merb/controller_spec.rb +35 -68
  56. data/spec/merb/cookie_store_spec.rb +7 -20
  57. data/spec/merb/core_ext_spec.rb +35 -1
  58. data/spec/merb/dispatch_spec.rb +8 -2
  59. data/spec/merb/generator_spec.rb +12 -4
  60. data/spec/merb/mail_controller_spec.rb +33 -0
  61. data/spec/merb/part_controller_spec.rb +33 -1
  62. data/spec/merb/render_spec.rb +74 -0
  63. data/spec/merb/request_spec.rb +43 -0
  64. data/spec/merb/responder_spec.rb +1 -0
  65. data/spec/merb/router_spec.rb +118 -13
  66. data/spec/merb/server_spec.rb +19 -0
  67. data/spec/merb/view_context_spec.rb +31 -3
  68. data/spec/spec_helper.rb +8 -0
  69. data/spec/spec_helpers/url_shared_behaviour.rb +112 -0
  70. metadata +124 -87
@@ -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/"].
@@ -9,7 +9,7 @@ module Haml
9
9
  module Helpers
10
10
 
11
11
  def _buffer( binding )
12
- @_buffer ||= eval( "_erbout", binding )
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
- locals_code = build_locals(opts[:locals])
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("#{locals_code}#{template}", opts)
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'] = 'application/xml'
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
@@ -2,30 +2,102 @@ require 'merb/test/fake_request'
2
2
  require 'merb/test/hpricot'
3
3
  include HpricotTestHelper
4
4
 
5
- # Create a FakeRequest suitable for passing to Controller.build
6
- def fake_request(path="/",method='GET')
7
- method = method.to_s.upcase
8
- Merb::Test::FakeRequest.with(path, :request_method => method)
9
- end
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
- def url(name, *args)
13
- Merb::Router.generate(name, *args)
14
- end
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
- response = StringIO.new
22
- @request = Merb::Test::FakeRequest.with(path, :request_method => (verb.to_s.upcase rescue 'GET'))
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
- yield @request if block_given?
28
+ yield @request if block_given?
25
29
 
26
- @controller, @action = Merb::Dispatcher.handle @request, response
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
- def get(path)
30
- request("GET",path)
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
@@ -1,93 +1,164 @@
1
1
  require 'hpricot'
2
2
  require 'spec'
3
- module MerbRspecMatchers
4
- class HaveSelector
5
- def initialize(expected)
6
- @expected = expected
7
- end
8
-
9
- def matches?(stringlike)
10
- @document = case stringlike
11
- when Hpricot::Elem
12
- stringlike
13
- when StringIO
14
- Hpricot.parse(stringlike.string)
15
- else
16
- Hpricot.parse(stringlike)
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
- def negative_failure_message
26
- "expected following text to not match selector #{@expected}:\n#{@document}"
27
- end
28
- end
29
-
30
- class MatchTag
31
- def initialize(name, attrs)
32
- @name, @attrs = name, attrs
33
- end
34
-
35
- def matches?(target)
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
- @attrs.each do |attr, val|
41
- unless target.include?("#{attr}=\"#{val}\"")
42
- @errors << "Expected #{attr}=\"#{val}\", but was #{target}"
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
- @errors.size == 0
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
- def failure_message
49
- @errors[0]
50
- end
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
- def negative_failure_message
53
- "Expected not to match against <#{@name} #{@attrs.map{ |a,v| "#{a}=\"#{v}\"" }.join(" ")}> tag, but it matched"
54
- end
55
- end
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
- class NotMatchTag
58
- def initialize(attrs)
59
- @attrs = attrs
60
- end
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
- def matches?(target)
63
- @errors = []
64
- @attrs.each do |attr, val|
65
- if target.include?("#{attr}=\"#{val}\"")
66
- @errors << "Should not include #{attr}=\"#{val}\", but was #{target}"
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
- @errors.size == 0
70
- end
129
+
130
+ class NotMatchTag
131
+ def initialize(attrs)
132
+ @attrs = attrs
133
+ end
71
134
 
72
- def failure_message
73
- @errors[0]
74
- end
75
- end
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
- def match_tag(name, attrs)
78
- MatchTag.new(name, attrs)
79
- end
80
- def not_match_tag(attrs)
81
- NotMatchTag.new(attrs)
82
- end
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
- def have_selector(expected)
85
- HaveSelector.new(expected)
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
@@ -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