merb 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -2,6 +2,7 @@ module Merb
|
|
2
2
|
|
3
3
|
class AbstractController
|
4
4
|
include Merb::RenderMixin
|
5
|
+
include Merb::GeneralControllerMixin
|
5
6
|
|
6
7
|
class_inheritable_accessor :before_filters
|
7
8
|
class_inheritable_accessor :after_filters
|
@@ -112,12 +113,20 @@ module Merb
|
|
112
113
|
end
|
113
114
|
return :filter_chain_completed
|
114
115
|
end
|
115
|
-
|
116
|
-
|
116
|
+
|
117
|
+
# +finalize_session+ is called at the end of a request to finalize/store any data placed in the session.
|
118
|
+
# Mixins/Classes wishing to define a new session store must implement this method.
|
119
|
+
# See merb/lib/sessions/* for examples of built in session stores.
|
120
|
+
|
121
|
+
def finalize_session #:nodoc:
|
117
122
|
# noop
|
118
123
|
end
|
119
|
-
|
120
|
-
|
124
|
+
|
125
|
+
# +setup_session+ is called at the beginning of a request to load the current session data.
|
126
|
+
# Mixins/Classes wishing to define a new session store must implement this method.
|
127
|
+
# See merb/lib/sessions/* for examples of built in session stores.
|
128
|
+
|
129
|
+
def setup_session #:nodoc:
|
121
130
|
# noop
|
122
131
|
end
|
123
132
|
|
@@ -175,21 +184,7 @@ module Merb
|
|
175
184
|
# throw :halt, Proc.new {|c| Tidy.new(c.index) }
|
176
185
|
#
|
177
186
|
def self.before(filter, opts={})
|
178
|
-
|
179
|
-
"You can specify either :only or :exclude but
|
180
|
-
not both at the same time for the same filter."
|
181
|
-
) if opts.has_key?(:only) && opts.has_key?(:exclude)
|
182
|
-
|
183
|
-
opts = shuffle_filters!(opts)
|
184
|
-
|
185
|
-
case filter
|
186
|
-
when Symbol, String, Proc
|
187
|
-
(self.before_filters ||= []) << [filter, opts]
|
188
|
-
else
|
189
|
-
raise(ArgumentError,
|
190
|
-
'filters need to be either a Symbol, String or a Proc'
|
191
|
-
)
|
192
|
-
end
|
187
|
+
add_filter((self.before_filters ||= []), filter, opts)
|
193
188
|
end
|
194
189
|
|
195
190
|
# #after is a class method that allows you to specify after filters in your
|
@@ -199,24 +194,82 @@ module Merb
|
|
199
194
|
# When you use a proc as a filter it needs to take one parameter. You can
|
200
195
|
# gain access to the response body like so: after Proc.new {|c|
|
201
196
|
# Tidy.new(c.body) }, :only => :index
|
197
|
+
|
202
198
|
def self.after(filter, opts={})
|
199
|
+
add_filter((self.after_filters ||= []), filter, opts)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Call #skip_before to remove an already declared (before) filter from your controller.
|
203
|
+
#
|
204
|
+
# Example:
|
205
|
+
# class Application < Merb::Controller
|
206
|
+
# before :require_login
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# class Login < Merb::Controller
|
210
|
+
# skip_before :require_login # Users shouldn't have to be logged in to log in
|
211
|
+
# end
|
212
|
+
|
213
|
+
def self.skip_before(filter)
|
214
|
+
skip_filter((self.before_filters || []), filter)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Exactly like #skip_before, but for after filters
|
218
|
+
|
219
|
+
def self.skip_after(filter)
|
220
|
+
skip_filter((self.after_filters || []), filter)
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.default_thrown_content
|
224
|
+
Hash.new{ |hash, key| hash[key] = "" }
|
225
|
+
end
|
226
|
+
|
227
|
+
# Set here to respond when rendering to cover the provides syntax of setting the content_type
|
228
|
+
def content_type_set?
|
229
|
+
false
|
230
|
+
end
|
231
|
+
|
232
|
+
def content_type
|
233
|
+
params[:format] || :html
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def self.add_filter(filters, filter, opts={})
|
203
239
|
raise(ArgumentError,
|
204
240
|
"You can specify either :only or :exclude but
|
205
241
|
not both at the same time for the same filter."
|
206
242
|
) if opts.has_key?(:only) && opts.has_key?(:exclude)
|
207
|
-
|
243
|
+
|
208
244
|
opts = shuffle_filters!(opts)
|
209
|
-
|
245
|
+
|
210
246
|
case filter
|
211
247
|
when Symbol, Proc, String
|
212
|
-
|
248
|
+
if existing_filter = filters.find {|f| f.first.to_s[filter.to_s]}
|
249
|
+
existing_filter.last.replace(opts)
|
250
|
+
else
|
251
|
+
filters << [filter, opts]
|
252
|
+
end
|
213
253
|
else
|
214
254
|
raise(ArgumentError,
|
215
|
-
'
|
255
|
+
'Filters need to be either a Symbol, String or a Proc'
|
216
256
|
)
|
217
257
|
end
|
218
258
|
end
|
219
259
|
|
260
|
+
def self.skip_filter(filters, filter)
|
261
|
+
raise(ArgumentError,
|
262
|
+
'You can only skip filters that have a String or Symbol name.'
|
263
|
+
) unless [Symbol, String].include? filter.class
|
264
|
+
|
265
|
+
MERB_LOGGER.warn("Filter #{filter} was not found in your filter chain."
|
266
|
+
) unless filters.reject! {|f| f.first.to_s[filter.to_s] }
|
267
|
+
end
|
268
|
+
|
269
|
+
# Ensures that the passed in hash values are always arrays.
|
270
|
+
#
|
271
|
+
# shuffle_filters!(:only => :new) #=> {:only => [:new]}
|
272
|
+
|
220
273
|
def self.shuffle_filters!(opts={})
|
221
274
|
if opts[:only] && opts[:only].is_a?(Symbol)
|
222
275
|
opts[:only] = [opts[:only]]
|
@@ -227,19 +280,6 @@ module Merb
|
|
227
280
|
return opts
|
228
281
|
end
|
229
282
|
|
230
|
-
def self.default_thrown_content
|
231
|
-
Hash.new{ |hash, key| hash[key] = "" }
|
232
|
-
end
|
233
|
-
|
234
|
-
# Set here to respond when rendering to cover the provides syntax of setting the content_type
|
235
|
-
def content_type_set?
|
236
|
-
false
|
237
|
-
end
|
238
|
-
|
239
|
-
|
240
|
-
def content_type
|
241
|
-
params[:format] || :html
|
242
|
-
end
|
243
283
|
end
|
244
284
|
|
245
285
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Merb
|
2
|
+
module Caching
|
3
|
+
module MemcachedStore
|
4
|
+
|
5
|
+
|
6
|
+
def get(name)
|
7
|
+
::Cache.get("fragment:#{name}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def put(name, content = nil)
|
11
|
+
::Cache.put("fragment:#{name}", content)
|
12
|
+
content
|
13
|
+
end
|
14
|
+
|
15
|
+
def expire_fragment(name)
|
16
|
+
::Cache.delete(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/merb/constants.rb
CHANGED
@@ -25,10 +25,8 @@ module Merb
|
|
25
25
|
MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
|
26
26
|
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
27
27
|
QUERY_STRING = 'QUERY_STRING'.freeze
|
28
|
-
|
29
|
-
|
30
|
-
APPLICATION_XML = 'application/xml'.freeze
|
31
|
-
TEXT_XML = 'text/xml'.freeze
|
28
|
+
JSON_MIME_TYPE_REGEXP = %r{^application/json|^text/x-json}.freeze
|
29
|
+
XML_MIME_TYPE_REGEXP = %r{^application/xml|^text/xml}.freeze
|
32
30
|
FORM_URL_ENCODED_REGEXP = %r{^application/x-www-form-urlencoded}.freeze
|
33
31
|
UPCASE_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
34
32
|
CONTENT_TYPE = "Content-Type".freeze
|
data/lib/merb/controller.rb
CHANGED
@@ -6,17 +6,49 @@ module Merb
|
|
6
6
|
# stream it into a tempfile and pass in the filename and tempfile object
|
7
7
|
# to your controller via params. It also parses the ?query=string and
|
8
8
|
# puts that into params as well.
|
9
|
+
#
|
10
|
+
# == Sessions
|
11
|
+
#
|
12
|
+
# Session data can be accessed through the +session+ hash:
|
13
|
+
#
|
14
|
+
# session[:user_id] = @user.id
|
15
|
+
#
|
16
|
+
# Session data is available until the user's cookie gets deleted/expires,
|
17
|
+
# or until your specific session store expires the data.
|
18
|
+
#
|
19
|
+
# === Session Store
|
20
|
+
#
|
21
|
+
# The session store is set in MERB_ROOT/config/merb.yml :
|
22
|
+
#
|
23
|
+
# :session_store: your_store
|
24
|
+
#
|
25
|
+
# Out of the box merb supports three session stores
|
26
|
+
#
|
27
|
+
# cookie:: All data (max 4kb) stored directly in cookie. Data integrity is checked on each request to prevent tampering. (Merb::CookieStore)
|
28
|
+
# memory:: Data stored in a class in memory. (Merb::MemorySession)
|
29
|
+
# mem_cache:: Data stored in mem_cache. (Merb::MemCacheSession)
|
30
|
+
#
|
31
|
+
# See the documentation on each session store for more information.
|
32
|
+
#
|
33
|
+
# You can also use a session store provided by a plugin. For instance, if you have DataMapper you can set
|
34
|
+
#
|
35
|
+
# :session_store: datamapper
|
36
|
+
#
|
37
|
+
# In this case session data will be stored in the database, as defined by the merb_datamapper plugin.
|
38
|
+
# Similar functionality exists for +activerecord+ and +sequel+ currently.
|
39
|
+
|
9
40
|
class Controller < AbstractController
|
10
41
|
class_inheritable_accessor :_session_id_key, :_session_expiry
|
11
|
-
cattr_accessor :_subclasses
|
42
|
+
cattr_accessor :_subclasses, :session_secret_key
|
12
43
|
self._subclasses = []
|
13
|
-
|
44
|
+
self.session_secret_key = nil
|
14
45
|
self._session_id_key = '_session_id'
|
15
46
|
self._session_expiry = Time.now + Merb::Const::WEEK * 2
|
16
47
|
|
17
48
|
include Merb::ControllerMixin
|
18
49
|
include Merb::ResponderMixin
|
19
50
|
include Merb::ControllerExceptions
|
51
|
+
include Merb::Caching::Actions
|
20
52
|
|
21
53
|
class << self
|
22
54
|
def inherited(klass)
|
@@ -173,6 +205,16 @@ module Merb
|
|
173
205
|
res.size == 1 ? res[0] : res
|
174
206
|
end
|
175
207
|
|
208
|
+
private
|
209
|
+
|
210
|
+
# This method is here to overwrite the one in the general_controller mixin
|
211
|
+
# The method ensures that when a url is generated with a hash, it contains a controller
|
212
|
+
def get_controller_for_url_generation(options)
|
213
|
+
controller = options[:controller] || params[:controller]
|
214
|
+
controller = params[:controller] if controller == :current
|
215
|
+
controller
|
216
|
+
end
|
217
|
+
|
176
218
|
end
|
177
219
|
|
178
220
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
begin
|
2
2
|
require 'ruby2ruby'
|
3
|
-
|
4
|
-
class ParseTreeArray < Array
|
3
|
+
|
4
|
+
class ParseTreeArray < Array #:nodoc:
|
5
5
|
def self.translate(*args)
|
6
6
|
self.new(ParseTree.translate(*args))
|
7
7
|
end
|
@@ -36,7 +36,26 @@ begin
|
|
36
36
|
|
37
37
|
end
|
38
38
|
|
39
|
+
# Used in mapping controller arguments to the params hash.
|
40
|
+
# NOTE: You must have the 'ruby2ruby' gem installed for this to work.
|
41
|
+
# Example:
|
42
|
+
# (In PostsController)
|
43
|
+
# def show(id) #=> id is the same as params[:id]
|
44
|
+
|
39
45
|
module GetArgs
|
46
|
+
|
47
|
+
# Returns an array of method arguments and their default values
|
48
|
+
# Example:
|
49
|
+
# class Example
|
50
|
+
# def hello(one,two="two",three)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# def goodbye
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Example.instance_method(:hello).get_args #=> [[:one], [:two, "two"], [:three, "three"]]
|
58
|
+
# Example.instance_method(:goodbye).get_args #=> nil
|
40
59
|
def get_args
|
41
60
|
klass, meth = self.to_s.split(/ /).to_a[1][0..-2].split("#")
|
42
61
|
# Remove stupidity for #<Method: Class(Object)#foo>
|
@@ -45,11 +64,11 @@ begin
|
|
45
64
|
end
|
46
65
|
end
|
47
66
|
|
48
|
-
class UnboundMethod
|
67
|
+
class UnboundMethod #:nodoc:
|
49
68
|
include GetArgs
|
50
69
|
end
|
51
70
|
|
52
|
-
class Method
|
71
|
+
class Method #:nodoc:
|
53
72
|
include GetArgs
|
54
73
|
end
|
55
74
|
rescue LoadError
|
data/lib/merb/core_ext/hash.rb
CHANGED
@@ -21,7 +21,7 @@ class Hash
|
|
21
21
|
#
|
22
22
|
# == Examples
|
23
23
|
#
|
24
|
-
# ===Standard
|
24
|
+
# === Standard
|
25
25
|
# <user gender='m'>
|
26
26
|
# <age type='integer'>35</age>
|
27
27
|
# <name>Home Simpson</name>
|
@@ -76,6 +76,8 @@ class Hash
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# convert this hash to a query string param
|
79
|
+
# {:name => "Bob", :address => {:street => '111 Ruby Ave.', :city => 'Ruby Central', :phones => ['111-111-1111', '222-222-2222']}}
|
80
|
+
# #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
|
79
81
|
def to_params
|
80
82
|
result = ''
|
81
83
|
stack = []
|
@@ -97,24 +99,24 @@ class Hash
|
|
97
99
|
end
|
98
100
|
|
99
101
|
# lets through the keys in the argument
|
100
|
-
#
|
101
|
-
#
|
102
|
+
# $ {:one => 1, :two => 2, :three => 3}.pass(:one)
|
103
|
+
# #=> {:one=>1}
|
102
104
|
def pass(*allowed)
|
103
105
|
self.reject { |k,v| ! allowed.include?(k) }
|
104
106
|
end
|
105
107
|
alias only pass
|
106
108
|
|
107
109
|
# blocks the keys in the arguments
|
108
|
-
#
|
109
|
-
#
|
110
|
+
# $ {:one => 1, :two => 2, :three => 3}.block(:one)
|
111
|
+
# #=> {:two=>2, :three=>3}
|
110
112
|
def block(*rejected)
|
111
113
|
self.reject { |k,v| rejected.include?(k) }
|
112
114
|
end
|
113
115
|
alias except block
|
114
116
|
|
115
117
|
# Converts the hash into xml attributes
|
116
|
-
#
|
117
|
-
#
|
118
|
+
# { :one => "ONE", "two"=>"TWO" }.to_xml_attributes
|
119
|
+
# #=> 'one="ONE" two="TWO"'
|
118
120
|
def to_xml_attributes
|
119
121
|
map do |k,v|
|
120
122
|
"#{k.to_s.camelize.sub(/^(.{1,1})/){|m| m.downcase}}=\"#{v}\""
|
@@ -128,10 +130,10 @@ class Hash
|
|
128
130
|
# or create the key and add this as the first class
|
129
131
|
#
|
130
132
|
# Example
|
131
|
-
#
|
132
|
-
#
|
133
|
+
# @hash[:class] #=> nil
|
134
|
+
# @hash.add_html_class!(:selected) #=> @hash[:class] == "selected"
|
133
135
|
#
|
134
|
-
#
|
136
|
+
# @hash.add_html_class!("class1 class2") #=> @hash[:class] == "selected class1 class2"
|
135
137
|
def add_html_class!(html_class)
|
136
138
|
if self[:class]
|
137
139
|
self[:class] = "#{self[:class]} #{html_class}"
|
@@ -154,6 +156,9 @@ class Hash
|
|
154
156
|
self
|
155
157
|
end
|
156
158
|
|
159
|
+
# Converts every key to an uppercase string (non-recursive.)
|
160
|
+
# {:name => "Bob", "age" => 12, "nick" => "Bobinator"}.environmentize_keys!
|
161
|
+
# #=> {"NAME"=>"Bob", "NICK"=>"Bobinator", "AGE"=>12}
|
157
162
|
def environmentize_keys!
|
158
163
|
self.each do |key, value|
|
159
164
|
self[key.to_s.upcase] = delete(key)
|
@@ -161,7 +166,7 @@ class Hash
|
|
161
166
|
self
|
162
167
|
end
|
163
168
|
|
164
|
-
def method_missing(m,*a)
|
169
|
+
def method_missing(m,*a) #:nodoc:
|
165
170
|
m.to_s =~ /=$/ ? self[$`]=a[0] : a==[] ? self[m] : raise(NoMethodError,"#{m}")
|
166
171
|
end
|
167
172
|
|
data/lib/merb/core_ext/kernel.rb
CHANGED
@@ -1,39 +1,79 @@
|
|
1
1
|
module Kernel
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
|
3
|
+
# Loads both gem and library dependencies that are passed in as arguments.
|
4
|
+
# Each argument can be:
|
5
|
+
# String - single dependency
|
6
|
+
# Hash - name => version
|
7
|
+
# Array - string dependencies
|
8
|
+
|
9
|
+
def dependencies(*args)
|
10
|
+
args.each do |arg|
|
11
|
+
case arg
|
12
|
+
when String : dependency(arg)
|
13
|
+
when Hash : arg.each { |r,v| dependency(r, v) }
|
14
|
+
when Array : arg.each { |r| dependency(r) }
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
# Loads the given string as a gem.
|
20
|
+
# An optional second parameter of a version string can be specified and is passed to rubygems.
|
21
|
+
# If rubygems cannot find the gem it requires the string as a library.
|
22
|
+
|
23
|
+
def dependency(name, *ver)
|
24
|
+
begin
|
25
|
+
Gem.activate(name, true, *ver)
|
26
|
+
message = "#{Time.now.httpdate}: loading gem '#{name}' from #{__app_file_trace__.first} ..."
|
27
|
+
puts(message)
|
28
|
+
MERB_LOGGER.info(message)
|
29
|
+
rescue LoadError
|
30
|
+
# Failed requiring as a gem, let's try loading with a normal require.
|
31
|
+
requires(name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Requires the library string passed in.
|
36
|
+
# If the library fails to load then it will display a helpful message.
|
37
|
+
|
38
|
+
def requires(library)
|
39
|
+
# TODO: Extract messages out into a messages file. This will also be the first step towards internationalization.
|
40
|
+
# TODO: adjust this message once logging refactor is complete.
|
41
|
+
require(library)
|
42
|
+
message = "#{Time.now.httpdate}: loading library '#{library}' from #{__app_file_trace__.first} ..."
|
43
|
+
puts(message)
|
44
|
+
MERB_LOGGER.info(message)
|
45
|
+
rescue LoadError
|
46
|
+
# TODO: adjust the two messages below to use merb's logger.error/info once logging refactor is complete.
|
47
|
+
message = "#{Time.now.httpdate}: <e> Could not find '#{library}' as either a library or gem, loaded from #{__app_file_trace__.first}.\n"
|
48
|
+
puts(message)
|
49
|
+
MERB_LOGGER.error(message)
|
50
|
+
|
51
|
+
# Print a helpful message
|
52
|
+
message = "#{Time.now.httpdate}: <i> Please be sure that if '#{library}': \n"
|
53
|
+
message << "#{Time.now.httpdate}: <i> * is a normal ruby library (file), be sure that the path of the library it is present in the $LOAD_PATH via $LOAD_PATH.unshift(\"/path/to/#{library}\") \n"
|
54
|
+
message << "#{Time.now.httpdate}: <i> * is included within a gem, be sure that you are specifying the gem as a dependency \n"
|
55
|
+
puts(message)
|
56
|
+
MERB_LOGGER.error(message)
|
57
|
+
exit() # Missing library/gem must be addressed.
|
58
|
+
end
|
59
|
+
|
17
60
|
# does a basic require, and prints the message passed as an optional
|
18
61
|
# second parameter if an error occurs.
|
62
|
+
|
19
63
|
def rescue_require(sym, message = nil)
|
20
64
|
require sym
|
21
65
|
rescue LoadError, RuntimeError
|
22
66
|
puts message if message
|
23
67
|
end
|
24
68
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def dependency(gem, *ver)
|
35
|
-
Gem.activate(gem, true, *ver)
|
36
|
-
end
|
69
|
+
# Used in MERB_ROOT/dependencies.yml
|
70
|
+
# Tells merb which ORM (Object Relational Mapper) you wish to use.
|
71
|
+
# Currently merb has plugins to support ActiveRecord, DataMapper, and Sequel.
|
72
|
+
#
|
73
|
+
# Example
|
74
|
+
# $ sudo gem install merb_datamapper # or merb_activerecord or merb_sequel
|
75
|
+
# use_orm :datamapper # this line goes in dependencies.yml
|
76
|
+
# $ ruby script/generate model MyModel # will use the appropriate generator for your ORM
|
37
77
|
|
38
78
|
def use_orm(orm)
|
39
79
|
raise "Don't call use_orm more than once" unless
|
@@ -45,6 +85,15 @@ module Kernel
|
|
45
85
|
Kernel.dependency(orm_plugin)
|
46
86
|
end
|
47
87
|
|
88
|
+
# Used in MERB_ROOT/dependencies.yml
|
89
|
+
# Tells merb which testing framework to use.
|
90
|
+
# Currently merb supports rspec and test_unit for testing
|
91
|
+
#
|
92
|
+
# Example
|
93
|
+
# $ sudo gem install rspec
|
94
|
+
# use_test :rspec # this line goes in dependencies.yml (or use_test :test_unit)
|
95
|
+
# $ ruby script/generate controller MyController # will use the appropriate generator for tests
|
96
|
+
|
48
97
|
def use_test(test_framework)
|
49
98
|
test_framework = test_framework.to_sym
|
50
99
|
raise "use_test only supports :rspec and :test_unit currently" unless
|
@@ -54,8 +103,20 @@ module Kernel
|
|
54
103
|
Merb::GENERATOR_SCOPE.push(test_framework)
|
55
104
|
end
|
56
105
|
|
106
|
+
# Returns an array with a stack trace of the application's files.
|
107
|
+
|
108
|
+
def __app_file_trace__
|
109
|
+
caller.select do |call|
|
110
|
+
call.include?(MERB_ROOT) && !call.include?(MERB_ROOT + "/framework")
|
111
|
+
end.map do |call|
|
112
|
+
file, line = call.scan(Regexp.new("#{MERB_ROOT}/(.*):(.*)")).first
|
113
|
+
"#{file}:#{line}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
57
117
|
# Gives you back the file, line and method of the caller number i
|
58
|
-
#
|
118
|
+
#
|
119
|
+
# Example
|
59
120
|
# __caller_info__(1) # -> ['/usr/lib/ruby/1.8/irb/workspace.rb', '52', 'irb_binding']
|
60
121
|
|
61
122
|
def __caller_info__(i = 1)
|
@@ -75,7 +136,7 @@ module Kernel
|
|
75
136
|
# ...,
|
76
137
|
# ...
|
77
138
|
# ]
|
78
|
-
# Example
|
139
|
+
# Example
|
79
140
|
# __caller_lines__('/usr/lib/ruby/1.8/debug.rb', 122, 2) # ->
|
80
141
|
# [
|
81
142
|
# [ 120, " def check_suspend", false ],
|
@@ -108,6 +169,19 @@ module Kernel
|
|
108
169
|
area
|
109
170
|
end
|
110
171
|
|
172
|
+
# Requires ruby-prof (<tt>sudo gem install ruby-prof</tt>)
|
173
|
+
# Takes a block and profiles the results of running the block 100 times.
|
174
|
+
# The resulting profile is written out to MERB_ROOT/log/#{name}.html.
|
175
|
+
# <tt>min</tt> specifies the minimum percentage of the total time a method must take for it to be included in the result.
|
176
|
+
#
|
177
|
+
# Example
|
178
|
+
# __profile__("MyProfile", 5) do
|
179
|
+
# 30.times { rand(10)**rand(10) }
|
180
|
+
# puts "Profile run"
|
181
|
+
# end
|
182
|
+
# Assuming that the total time taken for #puts calls was less than 5% of the total time to run, #puts won't appear
|
183
|
+
# in the profilel report.
|
184
|
+
|
111
185
|
def __profile__(name, min=1)
|
112
186
|
require 'ruby-prof' unless defined?(RubyProf)
|
113
187
|
return_result = ''
|
@@ -124,8 +198,14 @@ module Kernel
|
|
124
198
|
end
|
125
199
|
|
126
200
|
# Extracts an options hash if it is the last item in the args array
|
201
|
+
# Used internally in methods that take *args
|
202
|
+
#
|
203
|
+
# Example
|
204
|
+
# def render(*args,&blk)
|
205
|
+
# opts = extract_options_from_args!(args) || {}
|
206
|
+
|
127
207
|
def extract_options_from_args!(args)
|
128
|
-
args.pop if args.last
|
208
|
+
args.pop if Hash === args.last
|
129
209
|
end
|
130
210
|
|
131
211
|
end
|