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