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.
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