wycats-merb-core 0.9.8

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 (98) hide show
  1. data/CHANGELOG +992 -0
  2. data/CONTRIBUTORS +94 -0
  3. data/LICENSE +20 -0
  4. data/PUBLIC_CHANGELOG +142 -0
  5. data/README +21 -0
  6. data/Rakefile +458 -0
  7. data/TODO +0 -0
  8. data/bin/merb +11 -0
  9. data/bin/merb-specs +5 -0
  10. data/lib/merb-core.rb +598 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +717 -0
  13. data/lib/merb-core/config.rb +305 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +568 -0
  16. data/lib/merb-core/controller/exceptions.rb +315 -0
  17. data/lib/merb-core/controller/merb_controller.rb +256 -0
  18. data/lib/merb-core/controller/mime.rb +107 -0
  19. data/lib/merb-core/controller/mixins/authentication.rb +123 -0
  20. data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
  21. data/lib/merb-core/controller/mixins/controller.rb +319 -0
  22. data/lib/merb-core/controller/mixins/render.rb +513 -0
  23. data/lib/merb-core/controller/mixins/responder.rb +469 -0
  24. data/lib/merb-core/controller/template.rb +254 -0
  25. data/lib/merb-core/core_ext.rb +9 -0
  26. data/lib/merb-core/core_ext/hash.rb +7 -0
  27. data/lib/merb-core/core_ext/kernel.rb +340 -0
  28. data/lib/merb-core/dispatch/cookies.rb +130 -0
  29. data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
  30. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +176 -0
  34. data/lib/merb-core/dispatch/request.rb +729 -0
  35. data/lib/merb-core/dispatch/router.rb +151 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +566 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +191 -0
  39. data/lib/merb-core/dispatch/router/route.rb +511 -0
  40. data/lib/merb-core/dispatch/session.rb +222 -0
  41. data/lib/merb-core/dispatch/session/container.rb +74 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +173 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +68 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +99 -0
  45. data/lib/merb-core/dispatch/session/store_container.rb +150 -0
  46. data/lib/merb-core/dispatch/worker.rb +28 -0
  47. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  48. data/lib/merb-core/logger.rb +203 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +25 -0
  51. data/lib/merb-core/rack/adapter.rb +44 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  64. data/lib/merb-core/rack/middleware.rb +20 -0
  65. data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
  66. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  67. data/lib/merb-core/rack/middleware/csrf.rb +73 -0
  68. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  69. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  70. data/lib/merb-core/rack/middleware/static.rb +45 -0
  71. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  72. data/lib/merb-core/server.rb +284 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +229 -0
  75. data/lib/merb-core/tasks/merb.rb +1 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +11 -0
  79. data/lib/merb-core/test/helpers.rb +9 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +393 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +39 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +9 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
  89. data/lib/merb-core/test/run_specs.rb +49 -0
  90. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  91. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  92. data/lib/merb-core/test/test_ext/object.rb +14 -0
  93. data/lib/merb-core/test/test_ext/string.rb +14 -0
  94. data/lib/merb-core/vendor/facets.rb +2 -0
  95. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  96. data/lib/merb-core/vendor/facets/inflect.rb +342 -0
  97. data/lib/merb-core/version.rb +3 -0
  98. metadata +253 -0
@@ -0,0 +1,254 @@
1
+ module Merb::InlineTemplates
2
+ end
3
+
4
+ module Merb::Template
5
+
6
+ EXTENSIONS = {} unless defined?(EXTENSIONS)
7
+ METHOD_LIST = {} unless defined?(METHOD_LIST)
8
+ MTIMES = {} unless defined?(MTIMES)
9
+
10
+ class << self
11
+ # Get the template's method name from a full path. This replaces
12
+ # non-alphanumeric characters with __ and "." with "_"
13
+ #
14
+ # Collisions are potentially possible with something like:
15
+ # ~foo.bar and __foo.bar or !foo.bar.
16
+ #
17
+ # ==== Parameters
18
+ # path<String>:: A full path to convert to a valid Ruby method name
19
+ #
20
+ # ==== Returns
21
+ # String:: The template name.
22
+ #
23
+ #---
24
+ # We might want to replace this with something that varies the
25
+ # character replaced based on the non-alphanumeric character
26
+ # to avoid edge-case collisions.
27
+ def template_name(path)
28
+ path = File.expand_path(path)
29
+ path.gsub(/[^\.a-zA-Z0-9]/, "__").gsub(/\./, "_")
30
+ end
31
+
32
+ # For a given path, get an IO object that responds to #path.
33
+ #
34
+ # This is so that plugins can override this if they provide
35
+ # mechanisms for specifying templates that are not just simple
36
+ # files. The plugin is responsible for ensuring that the fake
37
+ # path provided will work with #template_for, and thus the
38
+ # RenderMixin in general.
39
+ #
40
+ # ==== Parameters
41
+ # path<String>:: A full path to find a template for. This is the
42
+ # path that the RenderMixin assumes it should find the template
43
+ # in.
44
+ #
45
+ # ==== Returns
46
+ # IO#path:: An IO object that responds to path (File or VirtualFile).
47
+ #---
48
+ # @semipublic
49
+ def load_template_io(path)
50
+ File.open(path, "r")
51
+ end
52
+
53
+ # Get the name of the template method for a particular path.
54
+ #
55
+ # ==== Parameters
56
+ # path<String>:: A full path to find a template method for.
57
+ # template_stack<Array>:: The template stack. Not used.
58
+ #
59
+ # ==== Returns
60
+ # <String>:: name of the method that inlines the template.
61
+ #---
62
+ # @semipublic
63
+ def template_for(path, template_stack = [])
64
+ path = File.expand_path(path)
65
+
66
+ ret =
67
+ if Merb::Config[:reload_templates]
68
+ file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
69
+ METHOD_LIST[path] = file ? inline_template(load_template_io(file)) : nil
70
+ else
71
+ METHOD_LIST[path] ||= begin
72
+ file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
73
+ file ? inline_template(load_template_io(file)) : nil
74
+ end
75
+ end
76
+
77
+ ret
78
+ end
79
+
80
+ # Get all known template extensions
81
+ #
82
+ # ==== Returns
83
+ # Array:: Extension strings.
84
+ #---
85
+ # @semipublic
86
+ def template_extensions
87
+ EXTENSIONS.keys
88
+ end
89
+
90
+ # Takes a template at a particular path and inlines it into a module and
91
+ # adds it to the METHOD_LIST table to speed lookup later.
92
+ #
93
+ # ==== Parameters
94
+ # io<#path>::
95
+ # An IO that responds to #path (File or VirtualFile)
96
+ # mod<Module>::
97
+ # The module to put the compiled method into. Defaults to
98
+ # Merb::InlineTemplates
99
+ #
100
+ # ==== Notes
101
+ # Even though this method supports inlining into any module, the method
102
+ # must be available to instances of AbstractController that will use it.
103
+ #---
104
+ # @public
105
+ def inline_template(io, mod = Merb::InlineTemplates)
106
+ path = File.expand_path(io.path)
107
+ ret = METHOD_LIST[path.gsub(/\.[^\.]*$/, "")] =
108
+ engine_for(path).compile_template(io, template_name(path), mod)
109
+ io.close
110
+ ret
111
+ end
112
+
113
+ # Finds the engine for a particular path.
114
+ #
115
+ # ==== Parameters
116
+ # path<String>:: The path of the file to find an engine for.
117
+ #
118
+ # ==== Returns
119
+ # Class:: The engine.
120
+ #---
121
+ # @semipublic
122
+ def engine_for(path)
123
+ path = File.expand_path(path)
124
+ EXTENSIONS[path.match(/\.([^\.]*)$/)[1]]
125
+ end
126
+
127
+ # Registers the extensions that will trigger a particular templating
128
+ # engine.
129
+ #
130
+ # ==== Parameters
131
+ # engine<Class>:: The class of the engine that is being registered
132
+ # extensions<Array[String]>::
133
+ # The list of extensions that will be registered with this templating
134
+ # language
135
+ #
136
+ # ==== Raises
137
+ # ArgumentError:: engine does not have a compile_template method.
138
+ #
139
+ # ==== Example
140
+ # Merb::Template.register_extensions(Merb::Template::Erubis, ["erb"])
141
+ #---
142
+ # @public
143
+ def register_extensions(engine, extensions)
144
+ raise ArgumentError, "The class you are registering does not have a compile_template method" unless
145
+ engine.respond_to?(:compile_template)
146
+ extensions.each{|ext| EXTENSIONS[ext] = engine }
147
+ Merb::AbstractController.class_eval <<-HERE
148
+ include #{engine}::Mixin
149
+ HERE
150
+ end
151
+ end
152
+
153
+ require 'erubis'
154
+
155
+ class Erubis
156
+ # ==== Parameters
157
+ # io<#path>:: An IO containing the full path of the template.
158
+ # name<String>:: The name of the method that will be created.
159
+ # mod<Module>:: The module that the compiled method will be placed into.
160
+ def self.compile_template(io, name, mod)
161
+ template = ::Erubis::BlockAwareEruby.new(io.read)
162
+
163
+ _old_verbose, $VERBOSE = $VERBOSE, nil
164
+ template.def_method(mod, name, File.expand_path(io.path))
165
+ $VERBOSE = _old_verbose
166
+
167
+ name
168
+ end
169
+
170
+ module Mixin
171
+
172
+ # ==== Parameters
173
+ # *args:: Arguments to pass to the block.
174
+ # &block:: The template block to call.
175
+ #
176
+ # ==== Returns
177
+ # String:: The output of the block.
178
+ #
179
+ # ==== Examples
180
+ # Capture being used in a .html.erb page:
181
+ #
182
+ # <% @foo = capture do %>
183
+ # <p>Some Foo content!</p>
184
+ # <% end %>
185
+ def capture_erb(*args, &block)
186
+ _old_buf, @_erb_buf = @_erb_buf, ""
187
+ block.call(*args)
188
+ ret = @_erb_buf
189
+ @_erb_buf = _old_buf
190
+ ret
191
+ end
192
+
193
+ # DOC
194
+ def concat_erb(string, binding)
195
+ @_erb_buf << string
196
+ end
197
+
198
+ end
199
+
200
+ Merb::Template.register_extensions(self, %w[erb])
201
+ end
202
+
203
+ end
204
+
205
+ module Erubis
206
+ module BlockAwareEnhancer
207
+ def add_preamble(src)
208
+ src << "_old_buf, @_erb_buf = @_erb_buf, ''; "
209
+ src << "@_engine = 'erb'; "
210
+ end
211
+
212
+ def add_postamble(src)
213
+ src << "\n" unless src[-1] == ?\n
214
+ src << "_ret = @_erb_buf; @_erb_buf = _old_buf; _ret.to_s;\n"
215
+ end
216
+
217
+ def add_text(src, text)
218
+ src << " @_erb_buf.concat('" << escape_text(text) << "'); "
219
+ end
220
+
221
+ def add_expr_escaped(src, code)
222
+ src << ' @_erb_buf.concat(' << escaped_expr(code) << ');'
223
+ end
224
+
225
+ def add_stmt2(src, code, tailch)
226
+ src << code
227
+ src << " ).to_s; " if tailch == "="
228
+ src << ';' unless code[-1] == ?\n
229
+ end
230
+
231
+ def add_expr_literal(src, code)
232
+ if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
233
+ src << ' @_erb_buf.concat( ' << code << "; "
234
+ else
235
+ src << ' @_erb_buf.concat((' << code << ').to_s);'
236
+ end
237
+ end
238
+ end
239
+
240
+ class BlockAwareEruby < Eruby
241
+ include BlockAwareEnhancer
242
+ end
243
+
244
+ # module RubyEvaluator
245
+ #
246
+ # # DOC
247
+ # def def_method(object, method_name, filename=nil)
248
+ # m = object.is_a?(Module) ? :module_eval : :instance_eval
249
+ # setup = "@_engine = 'erb'"
250
+ # object.__send__(m, "def #{method_name}(locals={}); #{setup}; #{@src}; end", filename || @filename || '(erubis)')
251
+ # end
252
+ #
253
+ # end
254
+ end
@@ -0,0 +1,9 @@
1
+ begin
2
+ require "extlib"
3
+ rescue LoadError => e
4
+ puts "Merb-core 0.9.4 and later uses extlib for Ruby core class extensions. Install it from github.com/sam/extlib."
5
+ exit
6
+ end
7
+
8
+ require File.dirname(__FILE__) / "core_ext" / "kernel"
9
+ require File.dirname(__FILE__) / "core_ext" / "hash"
@@ -0,0 +1,7 @@
1
+ class Hash
2
+ def extract!(*args)
3
+ args.map do |arg|
4
+ self.delete(arg)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,340 @@
1
+ require 'rubygems/dependency'
2
+
3
+ module Kernel
4
+
5
+ # Keep track of all required dependencies.
6
+ #
7
+ # @param name<String> The name of the gem to load.
8
+ # @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
9
+ # Version requirements to be passed to Gem::Dependency.new.
10
+ #
11
+ # @return <Gem::Dependency> Dependency information
12
+ #
13
+ # @api private
14
+ def track_dependency(name, *ver)
15
+ dep = Gem::Dependency.new(name, ver)
16
+ existing = Merb::BootLoader::Dependencies.dependencies.find { |d| d.name == dep.name }
17
+ if existing
18
+ index = Merb::BootLoader::Dependencies.dependencies.index(existing)
19
+ Merb::BootLoader::Dependencies.dependencies.delete(existing)
20
+ Merb::BootLoader::Dependencies.dependencies.insert(index, dep)
21
+ else
22
+ Merb::BootLoader::Dependencies.dependencies << dep
23
+ end
24
+ return dep
25
+ end
26
+
27
+ # Loads the given string as a gem. Execution is deferred until
28
+ # after the logger has been instantiated and the framework directory
29
+ # structure is defined.
30
+ #
31
+ # If that has already happened, the gem will be activated
32
+ # immediately, but it will still be registered.
33
+ #
34
+ # @param name<String> The name of the gem to load.
35
+ # @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
36
+ # Version requirements to be passed to Gem::Dependency.new.
37
+ #
38
+ # @return <Gem::Dependency> The dependency information.
39
+ def dependency(name, *ver)
40
+ if Merb::BootLoader.finished?(Merb::BootLoader::Dependencies)
41
+ load_dependency(name, *ver)
42
+ else
43
+ track_dependency(name, *ver)
44
+ end
45
+ end
46
+
47
+ # Loads the given string as a gem.
48
+ #
49
+ # This new version tries to load the file via ROOT/gems first before moving
50
+ # off to the system gems (so if you have a lower version of a gem in
51
+ # ROOT/gems, it'll still get loaded).
52
+ #
53
+ # @param name<String,Gem::Dependency>
54
+ # The name or dependency object of the gem to load.
55
+ # @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
56
+ # Version requirements to be passed to Gem.activate.
57
+ #
58
+ # @note
59
+ # If the gem cannot be found, the method will attempt to require the string
60
+ # as a library.
61
+ #
62
+ # @return <Gem::Dependency> The dependency information.
63
+ def load_dependency(name, *ver)
64
+ dep = name.is_a?(Gem::Dependency) ? name : track_dependency(name, *ver)
65
+ gem(dep)
66
+ rescue Gem::LoadError
67
+ ensure
68
+ require dep.name
69
+ Merb.logger.info!("loading gem '#{dep.name}' ...")
70
+ return dep # ensure needs explicit return
71
+ end
72
+
73
+ # Loads both gem and library dependencies that are passed in as arguments.
74
+ # Execution is deferred to the Merb::BootLoader::Dependencies.run during bootup.
75
+ #
76
+ # @param *args<String, Hash, Array> The dependencies to load.
77
+ def dependencies(*args)
78
+ args.map do |arg|
79
+ case arg
80
+ when String then dependency(arg)
81
+ when Hash then arg.map { |r,v| dependency(r, v) }
82
+ when Array then arg.map { |r| dependency(r) }
83
+ end
84
+ end
85
+ end
86
+
87
+ # Loads both gem and library dependencies that are passed in as arguments.
88
+ #
89
+ # @param *args<String, Hash, Array> The dependencies to load.
90
+ #
91
+ # @note
92
+ # Each argument can be:
93
+ # String:: Single dependency.
94
+ # Hash::
95
+ # Multiple dependencies where the keys are names and the values versions.
96
+ # Array:: Multiple string dependencies.
97
+ #
98
+ # @example dependencies "RedCloth" # Loads the the RedCloth gem
99
+ # @example dependencies "RedCloth", "merb_helpers" # Loads RedCloth and merb_helpers
100
+ # @example dependencies "RedCloth" => "3.0" # Loads RedCloth 3.0
101
+ def load_dependencies(*args)
102
+ args.map do |arg|
103
+ case arg
104
+ when String then load_dependency(arg)
105
+ when Hash then arg.map { |r,v| load_dependency(r, v) }
106
+ when Array then arg.map { |r| load_dependency(r) }
107
+ end
108
+ end
109
+ end
110
+
111
+ # Does a basic require, and prints a message if an error occurs.
112
+ #
113
+ # @param library<to_s> The library to attempt to include.
114
+ # @param message<String> The error to add to the log upon failure. Defaults to nil.
115
+ def rescue_require(library, message = nil)
116
+ require library
117
+ rescue LoadError, RuntimeError
118
+ Merb.logger.error!(message) if message
119
+ end
120
+
121
+ # Used in Merb.root/config/init.rb to tell Merb which ORM (Object Relational
122
+ # Mapper) you wish to use. Currently Merb has plugins to support
123
+ # ActiveRecord, DataMapper, and Sequel.
124
+ #
125
+ # @param orm<Symbol> The ORM to use.
126
+ #
127
+ # @example
128
+ # use_orm :datamapper
129
+ #
130
+ # # This will use the DataMapper generator for your ORM
131
+ # $ merb-gen model ActivityEvent
132
+ #
133
+ # @note
134
+ # If for some reason this is called more than once, latter
135
+ # call takes over other.
136
+ # @api public
137
+ def use_orm(orm)
138
+ begin
139
+ Merb.orm = orm
140
+ orm_plugin = "merb_#{orm}"
141
+ Kernel.dependency(orm_plugin)
142
+ rescue LoadError => e
143
+ Merb.logger.warn!("The #{orm_plugin} gem was not found. You may need to install it.")
144
+ raise e
145
+ end
146
+ end
147
+
148
+ # Used in Merb.root/config/init.rb to tell Merb which testing framework to
149
+ # use. Currently Merb has plugins to support RSpec and Test::Unit.
150
+ #
151
+ # @param test_framework<Symbol>
152
+ # The test framework to use. Currently only supports :rspec and :test_unit.
153
+ #
154
+ # @example
155
+ # use_test :rspec
156
+ #
157
+ # # This will now use the RSpec generator for tests
158
+ # $ merb-gen model ActivityEvent
159
+ # @api public
160
+ def use_test(test_framework, *test_dependencies)
161
+ Merb.test_framework = test_framework
162
+
163
+ Kernel.dependencies test_dependencies if Merb.env == "test" || Merb.env.nil?
164
+ end
165
+
166
+ # Used in Merb.root/config/init.rb to tell Merb which template engine to
167
+ # prefer.
168
+ #
169
+ # @param template_engine<Symbol>
170
+ # The template engine to use.
171
+ #
172
+ # @example
173
+ # use_template_engine :haml
174
+ #
175
+ # # This will now use haml templates in generators where available.
176
+ # $ merb-gen resource_controller Project
177
+ # @api public
178
+ def use_template_engine(template_engine)
179
+ Merb.template_engine = template_engine
180
+
181
+ if template_engine != :erb
182
+ if template_engine.in?(:haml, :builder)
183
+ template_engine_plugin = "merb-#{template_engine}"
184
+ else
185
+ template_engine_plugin = "merb_#{template_engine}"
186
+ end
187
+ Kernel.dependency(template_engine_plugin)
188
+ end
189
+ rescue LoadError => e
190
+ Merb.logger.warn!("The #{template_engine_plugin} gem was not found. You may need to install it.")
191
+ raise e
192
+ end
193
+
194
+ # @param i<Fixnum> The caller number. Defaults to 1.
195
+ #
196
+ # @return <Array[Array]> The file, line and method of the caller.
197
+ #
198
+ # @example
199
+ # __caller_info__(1)
200
+ # # => ['/usr/lib/ruby/1.8/irb/workspace.rb', '52', 'irb_binding']
201
+ def __caller_info__(i = 1)
202
+ file, line, meth = caller[i].scan(/(.*?):(\d+):in `(.*?)'/).first
203
+ end
204
+
205
+ # @param file<String> The file to read.
206
+ # @param line<Fixnum> The line number to look for.
207
+ # @param size<Fixnum>
208
+ # Number of lines to include above and below the the line to look for.
209
+ # Defaults to 4.
210
+ #
211
+ # @return <Array[Array]>
212
+ # Triplets containing the line number, the line and whether this was the
213
+ # searched line.
214
+ #
215
+ # @example
216
+ # __caller_lines__('/usr/lib/ruby/1.8/debug.rb', 122, 2) # =>
217
+ # [
218
+ # [ 120, " def check_suspend", false ],
219
+ # [ 121, " return if Thread.critical", false ],
220
+ # [ 122, " while (Thread.critical = true; @suspend_next)", true ],
221
+ # [ 123, " DEBUGGER__.waiting.push Thread.current", false ],
222
+ # [ 124, " @suspend_next = false", false ]
223
+ # ]
224
+ def __caller_lines__(file, line, size = 4)
225
+ line = line.to_i
226
+ if file =~ /\(erubis\)/
227
+ yield :error, "Template Error! Problem while rendering", false
228
+ elsif !File.file?(file) || !File.readable?(file)
229
+ yield :error, "File `#{file}' not available", false
230
+ else
231
+ lines = File.read(file).split("\n")
232
+ first_line = (f = line - size - 1) < 0 ? 0 : f
233
+ lines = lines[first_line, size * 2 + 1]
234
+
235
+ lines.each_with_index do |str, index|
236
+ yield index + line - size, str.chomp
237
+ end
238
+ end
239
+ #
240
+ # lines = File.readlines(file)
241
+ # current = line.to_i - 1
242
+ #
243
+ # first = current - size
244
+ # first = first < 0 ? 0 : first
245
+ #
246
+ # last = current + size
247
+ # last = last > lines.size ? lines.size : last
248
+ #
249
+ # log = lines[first..last]
250
+ #
251
+ # area = []
252
+ #
253
+ # log.each_with_index do |line, index|
254
+ # index = index + first + 1
255
+ # area << [index, line.chomp, index == current + 1]
256
+ # end
257
+ #
258
+ # area
259
+ end
260
+
261
+ # Takes a block, profiles the results of running the block
262
+ # specified number of times and generates HTML report.
263
+ #
264
+ # @param name<#to_s>
265
+ # The file name. The result will be written out to
266
+ # Merb.root/"log/#{name}.html".
267
+ # @param min<Fixnum>
268
+ # Minimum percentage of the total time a method must take for it to be
269
+ # included in the result. Defaults to 1.
270
+ #
271
+ # @return <String>
272
+ # The result of the profiling.
273
+ #
274
+ # @note
275
+ # Requires ruby-prof (<tt>sudo gem install ruby-prof</tt>)
276
+ #
277
+ # @example
278
+ # __profile__("MyProfile", 5, 30) do
279
+ # rand(10)**rand(10)
280
+ # puts "Profile run"
281
+ # end
282
+ #
283
+ # Assuming that the total time taken for #puts calls was less than 5% of the
284
+ # total time to run, #puts won't appear in the profile report.
285
+ # The code block will be run 30 times in the example above.
286
+ def __profile__(name, min=1, iter=100)
287
+ require 'ruby-prof' unless defined?(RubyProf)
288
+ return_result = ''
289
+ result = RubyProf.profile do
290
+ iter.times{return_result = yield}
291
+ end
292
+ printer = RubyProf::GraphHtmlPrinter.new(result)
293
+ path = File.join(Merb.root, 'log', "#{name}.html")
294
+ File.open(path, 'w') do |file|
295
+ printer.print(file, {:min_percent => min,
296
+ :print_file => true})
297
+ end
298
+ return_result
299
+ end
300
+
301
+ # Extracts an options hash if it is the last item in the args array. Used
302
+ # internally in methods that take *args.
303
+ #
304
+ # @param args<Array> The arguments to extract the hash from.
305
+ #
306
+ # @example
307
+ # def render(*args,&blk)
308
+ # opts = extract_options_from_args!(args) || {}
309
+ # # [...]
310
+ # end
311
+ def extract_options_from_args!(args)
312
+ args.pop if Hash === args.last
313
+ end
314
+
315
+ # Checks that the given objects quack like the given conditions.
316
+ #
317
+ # @param opts<Hash>
318
+ # Conditions to enforce. Each key will receive a quacks_like? call with the
319
+ # value (see Object#quacks_like? for details).
320
+ #
321
+ # @raise <ArgumentError>
322
+ # An object failed to quack like a condition.
323
+ def enforce!(opts = {})
324
+ opts.each do |k,v|
325
+ raise ArgumentError, "#{k.inspect} doesn't quack like #{v.inspect}" unless k.quacks_like?(v)
326
+ end
327
+ end
328
+
329
+ unless Kernel.respond_to?(:debugger)
330
+
331
+ # Define debugger method so that code even works if debugger was not
332
+ # requested. Drops a note to the logs that Debugger was not available.
333
+ def debugger
334
+ Merb.logger.info! "\n***** Debugger requested, but was not " +
335
+ "available: Start server with --debugger " +
336
+ "to enable *****\n"
337
+ end
338
+ end
339
+
340
+ end