joe-merb-core 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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 +456 -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 +648 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +889 -0
  13. data/lib/merb-core/config.rb +380 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +620 -0
  16. data/lib/merb-core/controller/exceptions.rb +302 -0
  17. data/lib/merb-core/controller/merb_controller.rb +283 -0
  18. data/lib/merb-core/controller/mime.rb +111 -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 +316 -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 +345 -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 +200 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +77 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +98 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +172 -0
  34. data/lib/merb-core/dispatch/request.rb +718 -0
  35. data/lib/merb-core/dispatch/router.rb +228 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +610 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +220 -0
  39. data/lib/merb-core/dispatch/router/route.rb +560 -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 +215 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +27 -0
  51. data/lib/merb-core/rack/adapter.rb +47 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +24 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +13 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +119 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +33 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +14 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +40 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +17 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +72 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +96 -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 +321 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +252 -0
  75. data/lib/merb-core/tasks/merb.rb +2 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +51 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +17 -0
  79. data/lib/merb-core/test/helpers.rb +10 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +176 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +61 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +47 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +10 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +108 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +393 -0
  89. data/lib/merb-core/test/run_specs.rb +141 -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,345 @@
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
+ # If the last argument is a Hash, extract the :immediate option,
38
+ # forcing a dependency to load immediately.
39
+ #
40
+ # @return <Gem::Dependency> The dependency information.
41
+ def dependency(name, *ver)
42
+ immediate = ver.last.is_a?(Hash) && ver.pop[:immediate]
43
+ if immediate || Merb::BootLoader.finished?(Merb::BootLoader::Dependencies)
44
+ load_dependency(name, *ver)
45
+ else
46
+ track_dependency(name, *ver)
47
+ end
48
+ end
49
+
50
+ # Loads the given string as a gem.
51
+ #
52
+ # This new version tries to load the file via ROOT/gems first before moving
53
+ # off to the system gems (so if you have a lower version of a gem in
54
+ # ROOT/gems, it'll still get loaded).
55
+ #
56
+ # @param name<String,Gem::Dependency>
57
+ # The name or dependency object of the gem to load.
58
+ # @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
59
+ # Version requirements to be passed to Gem.activate.
60
+ #
61
+ # @note
62
+ # If the gem cannot be found, the method will attempt to require the string
63
+ # as a library.
64
+ #
65
+ # @return <Gem::Dependency> The dependency information.
66
+ def load_dependency(name, *ver)
67
+ dep = name.is_a?(Gem::Dependency) ? name : track_dependency(name, *ver)
68
+ gem(dep)
69
+ rescue Gem::LoadError
70
+ ensure
71
+ require dep.name
72
+ Merb.logger.info!("loading gem '#{dep.name}' ...")
73
+ return dep # ensure needs explicit return
74
+ end
75
+
76
+ # Loads both gem and library dependencies that are passed in as arguments.
77
+ # Execution is deferred to the Merb::BootLoader::Dependencies.run during bootup.
78
+ #
79
+ # @param *args<String, Hash, Array> The dependencies to load.
80
+ def dependencies(*args)
81
+ args.map do |arg|
82
+ case arg
83
+ when String then dependency(arg)
84
+ when Hash then arg.map { |r,v| dependency(r, v) }
85
+ when Array then arg.map { |r| dependency(r) }
86
+ end
87
+ end
88
+ end
89
+
90
+ # Loads both gem and library dependencies that are passed in as arguments.
91
+ #
92
+ # @param *args<String, Hash, Array> The dependencies to load.
93
+ #
94
+ # @note
95
+ # Each argument can be:
96
+ # String:: Single dependency.
97
+ # Hash::
98
+ # Multiple dependencies where the keys are names and the values versions.
99
+ # Array:: Multiple string dependencies.
100
+ #
101
+ # @example dependencies "RedCloth" # Loads the the RedCloth gem
102
+ # @example dependencies "RedCloth", "merb_helpers" # Loads RedCloth and merb_helpers
103
+ # @example dependencies "RedCloth" => "3.0" # Loads RedCloth 3.0
104
+ def load_dependencies(*args)
105
+ args.map do |arg|
106
+ case arg
107
+ when String then load_dependency(arg)
108
+ when Hash then arg.map { |r,v| load_dependency(r, v) }
109
+ when Array then arg.map { |r| load_dependency(r) }
110
+ end
111
+ end
112
+ end
113
+
114
+ # Does a basic require, and prints a message if an error occurs.
115
+ #
116
+ # @param library<to_s> The library to attempt to include.
117
+ # @param message<String> The error to add to the log upon failure. Defaults to nil.
118
+ def rescue_require(library, message = nil)
119
+ require library
120
+ rescue LoadError, RuntimeError
121
+ Merb.logger.error!(message) if message
122
+ end
123
+
124
+ # Used in Merb.root/config/init.rb to tell Merb which ORM (Object Relational
125
+ # Mapper) you wish to use. Currently Merb has plugins to support
126
+ # ActiveRecord, DataMapper, and Sequel.
127
+ #
128
+ # @param orm<Symbol> The ORM to use.
129
+ #
130
+ # @example
131
+ # use_orm :datamapper
132
+ #
133
+ # # This will use the DataMapper generator for your ORM
134
+ # $ merb-gen model ActivityEvent
135
+ #
136
+ # @note
137
+ # If for some reason this is called more than once, latter
138
+ # call takes over other.
139
+ # @api public
140
+ def use_orm(orm)
141
+ begin
142
+ Merb.orm = orm
143
+ orm_plugin = "merb_#{orm}"
144
+ Kernel.dependency(orm_plugin)
145
+ rescue LoadError => e
146
+ Merb.logger.warn!("The #{orm_plugin} gem was not found. You may need to install it.")
147
+ raise e
148
+ end
149
+ end
150
+
151
+ # Used in Merb.root/config/init.rb to tell Merb which testing framework to
152
+ # use. Currently Merb has plugins to support RSpec and Test::Unit.
153
+ #
154
+ # @param test_framework<Symbol>
155
+ # The test framework to use. Currently only supports :rspec and :test_unit.
156
+ #
157
+ # @example
158
+ # use_test :rspec
159
+ #
160
+ # # This will now use the RSpec generator for tests
161
+ # $ merb-gen model ActivityEvent
162
+ # @api public
163
+ def use_test(test_framework, *test_dependencies)
164
+ Merb.test_framework = test_framework
165
+
166
+ Kernel.dependencies test_dependencies if Merb.env == "test" || Merb.env.nil?
167
+ end
168
+
169
+ # Used in Merb.root/config/init.rb to tell Merb which template engine to
170
+ # prefer.
171
+ #
172
+ # @param template_engine<Symbol>
173
+ # The template engine to use.
174
+ #
175
+ # @example
176
+ # use_template_engine :haml
177
+ #
178
+ # # This will now use haml templates in generators where available.
179
+ # $ merb-gen resource_controller Project
180
+ # @api public
181
+ def use_template_engine(template_engine)
182
+ Merb.template_engine = template_engine
183
+
184
+ if template_engine != :erb
185
+ if template_engine.in?(:haml, :builder)
186
+ template_engine_plugin = "merb-#{template_engine}"
187
+ else
188
+ template_engine_plugin = "merb_#{template_engine}"
189
+ end
190
+ Kernel.dependency(template_engine_plugin)
191
+ end
192
+ rescue LoadError => e
193
+ Merb.logger.warn!("The #{template_engine_plugin} gem was not found. You may need to install it.")
194
+ raise e
195
+ end
196
+
197
+ # @param i<Fixnum> The caller number. Defaults to 1.
198
+ #
199
+ # @return <Array[Array]> The file, line and method of the caller.
200
+ #
201
+ # @example
202
+ # __caller_info__(1)
203
+ # # => ['/usr/lib/ruby/1.8/irb/workspace.rb', '52', 'irb_binding']
204
+ def __caller_info__(i = 1)
205
+ file, line, meth = caller[i].scan(/(.*?):(\d+):in `(.*?)'/).first
206
+ end
207
+
208
+ # @param file<String> The file to read.
209
+ # @param line<Fixnum> The line number to look for.
210
+ # @param size<Fixnum>
211
+ # Number of lines to include above and below the the line to look for.
212
+ # Defaults to 4.
213
+ #
214
+ # @return <Array[Array]>
215
+ # Triplets containing the line number, the line and whether this was the
216
+ # searched line.
217
+ #
218
+ # @example
219
+ # __caller_lines__('/usr/lib/ruby/1.8/debug.rb', 122, 2) # =>
220
+ # [
221
+ # [ 120, " def check_suspend", false ],
222
+ # [ 121, " return if Thread.critical", false ],
223
+ # [ 122, " while (Thread.critical = true; @suspend_next)", true ],
224
+ # [ 123, " DEBUGGER__.waiting.push Thread.current", false ],
225
+ # [ 124, " @suspend_next = false", false ]
226
+ # ]
227
+ def __caller_lines__(file, line, size = 4)
228
+ line = line.to_i
229
+ if file =~ /\(erubis\)/
230
+ yield :error, "Template Error! Problem while rendering", false
231
+ elsif !File.file?(file) || !File.readable?(file)
232
+ yield :error, "File `#{file}' not available", false
233
+ else
234
+ lines = File.read(file).split("\n")
235
+ first_line = (f = line - size - 1) < 0 ? 0 : f
236
+
237
+ old_lines = lines
238
+ lines = lines[first_line, size * 2 + 1]
239
+
240
+ lines && lines.each_with_index do |str, index|
241
+ yield index + line - size, str.chomp
242
+ end
243
+ end
244
+ #
245
+ # lines = File.readlines(file)
246
+ # current = line.to_i - 1
247
+ #
248
+ # first = current - size
249
+ # first = first < 0 ? 0 : first
250
+ #
251
+ # last = current + size
252
+ # last = last > lines.size ? lines.size : last
253
+ #
254
+ # log = lines[first..last]
255
+ #
256
+ # area = []
257
+ #
258
+ # log.each_with_index do |line, index|
259
+ # index = index + first + 1
260
+ # area << [index, line.chomp, index == current + 1]
261
+ # end
262
+ #
263
+ # area
264
+ end
265
+
266
+ # Takes a block, profiles the results of running the block
267
+ # specified number of times and generates HTML report.
268
+ #
269
+ # @param name<#to_s>
270
+ # The file name. The result will be written out to
271
+ # Merb.root/"log/#{name}.html".
272
+ # @param min<Fixnum>
273
+ # Minimum percentage of the total time a method must take for it to be
274
+ # included in the result. Defaults to 1.
275
+ #
276
+ # @return <String>
277
+ # The result of the profiling.
278
+ #
279
+ # @note
280
+ # Requires ruby-prof (<tt>sudo gem install ruby-prof</tt>)
281
+ #
282
+ # @example
283
+ # __profile__("MyProfile", 5, 30) do
284
+ # rand(10)**rand(10)
285
+ # puts "Profile run"
286
+ # end
287
+ #
288
+ # Assuming that the total time taken for #puts calls was less than 5% of the
289
+ # total time to run, #puts won't appear in the profile report.
290
+ # The code block will be run 30 times in the example above.
291
+ def __profile__(name, min=1, iter=100)
292
+ require 'ruby-prof' unless defined?(RubyProf)
293
+ return_result = ''
294
+ result = RubyProf.profile do
295
+ iter.times{return_result = yield}
296
+ end
297
+ printer = RubyProf::GraphHtmlPrinter.new(result)
298
+ path = File.join(Merb.root, 'log', "#{name}.html")
299
+ File.open(path, 'w') do |file|
300
+ printer.print(file, {:min_percent => min,
301
+ :print_file => true})
302
+ end
303
+ return_result
304
+ end
305
+
306
+ # Extracts an options hash if it is the last item in the args array. Used
307
+ # internally in methods that take *args.
308
+ #
309
+ # @param args<Array> The arguments to extract the hash from.
310
+ #
311
+ # @example
312
+ # def render(*args,&blk)
313
+ # opts = extract_options_from_args!(args) || {}
314
+ # # [...]
315
+ # end
316
+ def extract_options_from_args!(args)
317
+ args.pop if Hash === args.last
318
+ end
319
+
320
+ # Checks that the given objects quack like the given conditions.
321
+ #
322
+ # @param opts<Hash>
323
+ # Conditions to enforce. Each key will receive a quacks_like? call with the
324
+ # value (see Object#quacks_like? for details).
325
+ #
326
+ # @raise <ArgumentError>
327
+ # An object failed to quack like a condition.
328
+ def enforce!(opts = {})
329
+ opts.each do |k,v|
330
+ raise ArgumentError, "#{k.inspect} doesn't quack like #{v.inspect}" unless k.quacks_like?(v)
331
+ end
332
+ end
333
+
334
+ unless Kernel.respond_to?(:debugger)
335
+
336
+ # Define debugger method so that code even works if debugger was not
337
+ # requested. Drops a note to the logs that Debugger was not available.
338
+ def debugger
339
+ Merb.logger.info! "\n***** Debugger requested, but was not " +
340
+ "available: Start server with --debugger " +
341
+ "to enable *****\n"
342
+ end
343
+ end
344
+
345
+ end