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,68 @@
1
+ module Merb
2
+
3
+ # Sessions stored in memcached.
4
+ #
5
+ # Requires setup in your +init.rb+.
6
+ #
7
+ # This for the 'memcache-client' gem:
8
+ #
9
+ # Merb::BootLoader.after_app_loads do
10
+ # require 'memcache'
11
+ # Merb::MemcacheSession.store =
12
+ # MemCache.new('127.0.0.1:11211', :namespace => 'my_app')
13
+ # end
14
+ #
15
+ # Or this for the 'memcached' gem:
16
+ #
17
+ # Merb::BootLoader.after_app_loads do
18
+ # require 'memcache'
19
+ # Merb::MemcacheSession.store =
20
+ # Memcached.new('127.0.0.1:11211', :namespace => 'my_app')
21
+ # end
22
+
23
+ class MemcacheSession < SessionStoreContainer
24
+
25
+ # The session store type
26
+ self.session_store_type = :memcache
27
+
28
+ end
29
+
30
+ module MemcacheStore
31
+
32
+ # Make the Memcached gem conform to the SessionStoreContainer interface
33
+
34
+ # ==== Parameters
35
+ # session_id<String>:: ID of the session to retrieve.
36
+ #
37
+ # ==== Returns
38
+ # ContainerSession:: The session corresponding to the ID.
39
+ def retrieve_session(session_id)
40
+ get("session:#{session_id}")
41
+ end
42
+
43
+ # ==== Parameters
44
+ # session_id<String>:: ID of the session to set.
45
+ # data<ContainerSession>:: The session to set.
46
+ def store_session(session_id, data)
47
+ set("session:#{session_id}", data)
48
+ end
49
+
50
+ # ==== Parameters
51
+ # session_id<String>:: ID of the session to delete.
52
+ def delete_session(session_id)
53
+ delete("session:#{session_id}")
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ # For the memcached gem.
61
+ class Memcached
62
+ include Merb::MemcacheStore
63
+ end
64
+
65
+ # For the memcache-client gem.
66
+ class MemCache
67
+ include Merb::MemcacheStore
68
+ end
@@ -0,0 +1,99 @@
1
+ module Merb
2
+
3
+ # Sessions stored in memory.
4
+ #
5
+ # Set it up by adding the following to your init file:
6
+ #
7
+ # Merb::Config.use do |c|
8
+ # c[:session_store] = :memory
9
+ # c[:memory_session_ttl] = 3600 # in seconds, one hour
10
+ # end
11
+ #
12
+ # Sessions will remain in memory until the server is stopped or the time
13
+ # as set in :memory_session_ttl expires. Expired sessions are cleaned up in the
14
+ # background by a separate thread. Every time reaper
15
+ # cleans up expired sessions, garbage collection is scheduled start.
16
+ #
17
+ # Memory session is accessed in a thread safe manner.
18
+ class MemorySession < SessionStoreContainer
19
+
20
+ # The session store type
21
+ self.session_store_type = :memory
22
+
23
+ # Bypass normal implicit class attribute reader - see below.
24
+ def store
25
+ self.class.store
26
+ end
27
+
28
+ # Lazy load/setup of MemorySessionStore.
29
+ def self.store
30
+ @_store ||= MemorySessionStore.new(Merb::Config[:memory_session_ttl])
31
+ end
32
+
33
+ end
34
+
35
+ # Used for handling multiple sessions stored in memory.
36
+ class MemorySessionStore
37
+
38
+ # ==== Parameters
39
+ # ttl<Fixnum>:: Session validity time in seconds. Defaults to 1 hour.
40
+ def initialize(ttl=nil)
41
+ @sessions = Hash.new
42
+ @timestamps = Hash.new
43
+ @mutex = Mutex.new
44
+ @session_ttl = ttl || Merb::Const::HOUR # defaults 1 hour
45
+ start_timer
46
+ end
47
+
48
+ # ==== Parameters
49
+ # session_id<String>:: ID of the session to retrieve.
50
+ #
51
+ # ==== Returns
52
+ # ContainerSession:: The session corresponding to the ID.
53
+ def retrieve_session(session_id)
54
+ @mutex.synchronize {
55
+ @timestamps[session_id] = Time.now
56
+ @sessions[session_id]
57
+ }
58
+ end
59
+
60
+ # ==== Parameters
61
+ # session_id<String>:: ID of the session to set.
62
+ # data<ContainerSession>:: The session to set.
63
+ def store_session(session_id, data)
64
+ @mutex.synchronize {
65
+ @timestamps[session_id] = Time.now
66
+ @sessions[session_id] = data
67
+ }
68
+ end
69
+
70
+ # ==== Parameters
71
+ # session_id<String>:: ID of the session to delete.
72
+ def delete_session(session_id)
73
+ @mutex.synchronize {
74
+ @timestamps.delete(session_id)
75
+ @sessions.delete(session_id)
76
+ }
77
+ end
78
+
79
+ # Deletes any sessions that have reached their maximum validity.
80
+ def reap_expired_sessions
81
+ @timestamps.each do |session_id,stamp|
82
+ delete_session(session_id) if (stamp + @session_ttl) < Time.now
83
+ end
84
+ GC.start
85
+ end
86
+
87
+ # Starts the timer that will eventually reap outdated sessions.
88
+ def start_timer
89
+ Thread.new do
90
+ loop {
91
+ sleep @session_ttl
92
+ reap_expired_sessions
93
+ }
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ end
@@ -0,0 +1,150 @@
1
+ module Merb
2
+
3
+ class SessionStoreContainer < SessionContainer
4
+
5
+ class_inheritable_accessor :store
6
+ attr_accessor :_fingerprint
7
+
8
+ # The class attribute :store holds a reference to an object that implements
9
+ # the following interface:
10
+ #
11
+ # - retrieve_session(session_id) # returns a Hash
12
+ # - store_session(session_id, data) # expects data to be Hash
13
+ # - delete_session(session_id)
14
+ #
15
+ # You can use session store classes directly by assigning to :store in your
16
+ # config/init.rb after_app_loads step, for example:
17
+ #
18
+ # Merb::BootLoader.after_app_loads do
19
+ # SessionStoreContainer.store = MemorySession.new
20
+ # end
21
+ #
22
+ # Or you can inherit from SessionStoreContainer to create a SessionContainer
23
+ # that delegates to aggregated store.
24
+ #
25
+ # class MemorySession < SessionStoreContainer
26
+ # self.session_store_type = :memory
27
+ # end
28
+ #
29
+ # class MemoryContainer
30
+ #
31
+ # def self.retrieve_session(session_id)
32
+ # ...
33
+ # end
34
+ #
35
+ # def self.store_session(session_id, data)
36
+ # ...
37
+ # end
38
+ #
39
+ # def self.delete_session(session_id)
40
+ # ...
41
+ # end
42
+ #
43
+ # end
44
+ # When used directly, report as :store store
45
+ self.session_store_type = :store
46
+
47
+ class << self
48
+
49
+ # Generates a new session ID and creates a new session.
50
+ #
51
+ # ==== Returns
52
+ # SessionStoreContainer:: The new session.
53
+ def generate
54
+ session = new(Merb::SessionMixin.rand_uuid)
55
+ session.needs_new_cookie = true
56
+ session
57
+ end
58
+
59
+ # Setup a new session.
60
+ #
61
+ # ==== Parameters
62
+ # request<Merb::Request>:: The Merb::Request that came in from Rack.
63
+ #
64
+ # ==== Returns
65
+ # SessionContainer:: a SessionContainer. If no sessions were found,
66
+ # a new SessionContainer will be generated.
67
+ def setup(request)
68
+ session = retrieve(request.session_id)
69
+ request.session = session
70
+ # TODO Marshal.dump is slow - needs optimization
71
+ session._fingerprint = Marshal.dump(request.session.to_hash).hash
72
+ session
73
+ end
74
+
75
+ private
76
+
77
+ # ==== Parameters
78
+ # session_id<String:: The ID of the session to retrieve.
79
+ #
80
+ # ==== Returns
81
+ # SessionStoreContainer:: SessionStoreContainer instance with the session data. If no
82
+ # sessions matched session_id, a new SessionStoreContainer will be generated.
83
+ #
84
+ # ==== Notes
85
+ # If there are persisted exceptions callbacks to execute, they all get executed
86
+ # when Memcache library raises an exception.
87
+ def retrieve(session_id)
88
+ unless session_id.blank?
89
+ begin
90
+ session_data = store.retrieve_session(session_id)
91
+ rescue => err
92
+ Merb.logger.warn!("Could not retrieve session from #{self.name}: #{err.message}")
93
+ end
94
+ # Not in container, but assume that cookie exists
95
+ session_data = new(session_id) if session_data.nil?
96
+ else
97
+ # No cookie...make a new session_id
98
+ session_data = generate
99
+ end
100
+ if session_data.is_a?(self)
101
+ session_data
102
+ else
103
+ # Recreate using the existing session as the data, when switching
104
+ # from another session type for example, eg. cookie to memcached
105
+ # or when the data is just a hash
106
+ new(session_id).update(session_data)
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ # Teardown and/or persist the current session.
113
+ #
114
+ # If @_destroy is true, clear out the session completely, including
115
+ # removal of the session cookie itself.
116
+ #
117
+ # ==== Parameters
118
+ # request<Merb::Request>:: The Merb::Request that came in from Rack.
119
+ #
120
+ # ==== Notes
121
+ # The data (self) is converted to a Hash first, since a container might
122
+ # choose to do a full Marshal on the data, which would make it persist
123
+ # attributes like 'needs_new_cookie', which it shouldn't.
124
+ def finalize(request)
125
+ if @_destroy
126
+ store.delete_session(self.session_id)
127
+ request.destroy_session_cookie
128
+ else
129
+ if _fingerprint != Marshal.dump(data = self.to_hash).hash
130
+ begin
131
+ store.store_session(request.session(self.class.session_store_type).session_id, data)
132
+ rescue => err
133
+ Merb.logger.warn!("Could not persist session to #{self.class.name}: #{err.message}")
134
+ end
135
+ end
136
+ if needs_new_cookie || Merb::SessionMixin.needs_new_cookie?
137
+ request.set_session_id_cookie(self.session_id)
138
+ end
139
+ end
140
+ end
141
+
142
+ # Regenerate the session ID.
143
+ def regenerate
144
+ store.delete_session(self.session_id)
145
+ self.session_id = Merb::SessionMixin.rand_uuid
146
+ store.store_session(self.session_id, self)
147
+ end
148
+
149
+ end
150
+ end
@@ -0,0 +1,28 @@
1
+ module Merb
2
+ class Worker
3
+
4
+ attr_accessor :thread
5
+
6
+ def initialize
7
+ @thread = Thread.new { loop { process_queue } }
8
+ end
9
+
10
+ def process_queue
11
+ begin
12
+ while blk = Merb::Dispatcher.work_queue.pop
13
+ # we've been blocking on the queue waiting for an item sleeping.
14
+ # when someone pushes an item it wakes up this thread so we
15
+ # immediately pass execution to the scheduler so we don't
16
+ # accidentally run this block before the action finishes
17
+ # it's own processing
18
+ Thread.pass
19
+ blk.call
20
+ end
21
+ rescue Exception => e
22
+ Merb.logger.warn! %Q!Worker Thread Crashed with Exception:\n#{Merb.exception(e)}\nRestarting Worker Thread!
23
+ retry
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,77 @@
1
+ require 'erubis'
2
+ module Erubis
3
+ module Basic::Converter
4
+ def convert_input(src, input)
5
+ pat = @pattern
6
+ regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
7
+ pos = 0
8
+ is_bol = true # is beginning of line
9
+ input.scan(regexp) do |indicator, code, tailch, rspace|
10
+ match = Regexp.last_match()
11
+ len = match.begin(0) - pos
12
+ text = input[pos, len]
13
+ pos = match.end(0)
14
+ ch = indicator ? indicator[0] : nil
15
+ lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
16
+ is_bol = rspace ? true : false
17
+ add_text(src, text) if text && !text.empty?
18
+ ## * when '<%= %>', do nothing
19
+ ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
20
+ if ch == ?= # <%= %>
21
+ rspace = nil if tailch && !tailch.empty?
22
+ add_text(src, lspace) if lspace
23
+ add_expr(src, code, indicator)
24
+ add_text(src, rspace) if rspace
25
+ elsif ch == ?\# # <%# %>
26
+ n = code.count("\n") + (rspace ? 1 : 0)
27
+ if @trim && lspace && rspace
28
+ add_stmt(src, "\n" * n)
29
+ else
30
+ add_text(src, lspace) if lspace
31
+ add_stmt(src, "\n" * n)
32
+ add_text(src, rspace) if rspace
33
+ end
34
+ elsif ch == ?% # <%% %>
35
+ s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
36
+ add_text(src, s)
37
+ else # <% %>
38
+ if @trim && lspace && rspace
39
+ if respond_to?(:add_stmt2)
40
+ add_stmt2(src, "#{lspace}#{code}#{rspace}", tailch)
41
+ else
42
+ add_stmt(src, "#{lspace}#{code}#{rspace}")
43
+ end
44
+ else
45
+ add_text(src, lspace) if lspace
46
+ if respond_to?(:add_stmt2)
47
+ add_stmt2(src, code, tailch)
48
+ else
49
+ add_stmt(src, code)
50
+ end
51
+ add_text(src, rspace) if rspace
52
+ end
53
+ end
54
+ end
55
+ #rest = $' || input # ruby1.8
56
+ rest = pos == 0 ? input : input[pos..-1] # ruby1.9
57
+ add_text(src, rest)
58
+ end
59
+
60
+ end
61
+
62
+ class MEruby < Erubis::Eruby
63
+ include PercentLineEnhancer
64
+ include StringBufferEnhancer
65
+ end
66
+
67
+ # Loads a file, runs it through Erubis and parses it as YAML.
68
+ #
69
+ # ===== Parameters
70
+ # file<String>:: The name of the file to load.
71
+ # binding<Binding>::
72
+ # The binding to use when evaluating the ERB tags. Defaults to the current
73
+ # binding.
74
+ def self.load_yaml_file(file, binding = binding)
75
+ YAML::load(Erubis::MEruby.new(IO.read(File.expand_path(file))).result(binding))
76
+ end
77
+ end
@@ -0,0 +1,203 @@
1
+ Merb::Logger = Extlib::Logger
2
+ # require "time" # httpdate
3
+ # # ==== Public Merb Logger API
4
+ # #
5
+ # # To replace an existing logger with a new one:
6
+ # # Merb::Logger.set_log(log{String, IO},level{Symbol, String})
7
+ # #
8
+ # # Available logging levels are
9
+ # # Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
10
+ # #
11
+ # # Logging via:
12
+ # # Merb.logger.fatal(message<String>,&block)
13
+ # # Merb.logger.error(message<String>,&block)
14
+ # # Merb.logger.warn(message<String>,&block)
15
+ # # Merb.logger.info(message<String>,&block)
16
+ # # Merb.logger.debug(message<String>,&block)
17
+ # #
18
+ # # Logging with autoflush:
19
+ # # Merb.logger.fatal!(message<String>,&block)
20
+ # # Merb.logger.error!(message<String>,&block)
21
+ # # Merb.logger.warn!(message<String>,&block)
22
+ # # Merb.logger.info!(message<String>,&block)
23
+ # # Merb.logger.debug!(message<String>,&block)
24
+ # #
25
+ # # Flush the buffer to
26
+ # # Merb.logger.flush
27
+ # #
28
+ # # Remove the current log object
29
+ # # Merb.logger.close
30
+ # #
31
+ # # ==== Private Merb Logger API
32
+ # #
33
+ # # To initialize the logger you create a new object, proxies to set_log.
34
+ # # Merb::Logger.new(log{String, IO},level{Symbol, String})
35
+ # module Merb
36
+ #
37
+ # class << self
38
+ # attr_accessor :logger
39
+ # end
40
+ #
41
+ # class Logger
42
+ #
43
+ # attr_accessor :level
44
+ # attr_accessor :delimiter
45
+ # attr_accessor :auto_flush
46
+ # attr_reader :buffer
47
+ # attr_reader :log
48
+ # attr_reader :init_args
49
+ #
50
+ # # ==== Notes
51
+ # # Ruby (standard) logger levels:
52
+ # # :fatal:: An unhandleable error that results in a program crash
53
+ # # :error:: A handleable error condition
54
+ # # :warn:: A warning
55
+ # # :info:: generic (useful) information about system operation
56
+ # # :debug:: low-level information for developers
57
+ # Levels =
58
+ # {
59
+ # :fatal => 7,
60
+ # :error => 6,
61
+ # :warn => 4,
62
+ # :info => 3,
63
+ # :debug => 0
64
+ # } unless const_defined?(:Levels)
65
+ #
66
+ # private
67
+ #
68
+ # # Readies a log for writing.
69
+ # #
70
+ # # ==== Parameters
71
+ # # log<IO, String>:: Either an IO object or a name of a logfile.
72
+ # def initialize_log(log)
73
+ # close if @log # be sure that we don't leave open files laying around.
74
+ #
75
+ # if log.respond_to?(:write)
76
+ # @log = log
77
+ # elsif File.exist?(log)
78
+ # @log = open(log, (File::WRONLY | File::APPEND))
79
+ # @log.sync = true
80
+ # else
81
+ # FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
82
+ # @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
83
+ # @log.sync = true
84
+ # @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
85
+ # end
86
+ # end
87
+ #
88
+ # public
89
+ #
90
+ # # To initialize the logger you create a new object, proxies to set_log.
91
+ # #
92
+ # # ==== Parameters
93
+ # # *args:: Arguments to create the log from. See set_logs for specifics.
94
+ # def initialize(*args)
95
+ # @init_args = args
96
+ # set_log(*args)
97
+ # end
98
+ #
99
+ # # Replaces an existing logger with a new one.
100
+ # #
101
+ # # ==== Parameters
102
+ # # log<IO, String>:: Either an IO object or a name of a logfile.
103
+ # # log_level<~to_sym>::
104
+ # # The log level from, e.g. :fatal or :info. Defaults to :error in the
105
+ # # production environment and :debug otherwise.
106
+ # # delimiter<String>::
107
+ # # Delimiter to use between message sections. Defaults to " ~ ".
108
+ # # auto_flush<Boolean>::
109
+ # # Whether the log should automatically flush after new messages are
110
+ # # added. Defaults to false.
111
+ # def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
112
+ # if log_level && Levels[log_level.to_sym]
113
+ # @level = Levels[log_level.to_sym]
114
+ # elsif Merb.environment == "production"
115
+ # @level = Levels[:warn]
116
+ # else
117
+ # @level = Levels[:debug]
118
+ # end
119
+ # @buffer = []
120
+ # @delimiter = delimiter
121
+ # @auto_flush = auto_flush
122
+ #
123
+ # initialize_log(log)
124
+ #
125
+ # Merb.logger = self
126
+ # end
127
+ #
128
+ # # Flush the entire buffer to the log object.
129
+ # def flush
130
+ # return unless @buffer.size > 0
131
+ # @log.write(@buffer.slice!(0..-1).to_s)
132
+ # end
133
+ #
134
+ # # Close and remove the current log object.
135
+ # def close
136
+ # flush
137
+ # @log.close if @log.respond_to?(:close) && !@log.tty?
138
+ # @log = nil
139
+ # end
140
+ #
141
+ # # Appends a message to the log. The methods yield to an optional block and
142
+ # # the output of this block will be appended to the message.
143
+ # #
144
+ # # ==== Parameters
145
+ # # string<String>:: The message to be logged. Defaults to nil.
146
+ # #
147
+ # # ==== Returns
148
+ # # String:: The resulting message added to the log file.
149
+ # def <<(string = nil)
150
+ # message = ""
151
+ # message << delimiter
152
+ # message << string if string
153
+ # message << "\n" unless message[-1] == ?\n
154
+ # @buffer << message
155
+ # flush if @auto_flush
156
+ #
157
+ # message
158
+ # end
159
+ # alias :push :<<
160
+ #
161
+ # # Generate the logging methods for Merb.logger for each log level.
162
+ # Levels.each_pair do |name, number|
163
+ # class_eval <<-LEVELMETHODS, __FILE__, __LINE__
164
+ #
165
+ # # Appends a message to the log if the log level is at least as high as
166
+ # # the log level of the logger.
167
+ # #
168
+ # # ==== Parameters
169
+ # # string<String>:: The message to be logged. Defaults to nil.
170
+ # #
171
+ # # ==== Returns
172
+ # # self:: The logger object for chaining.
173
+ # def #{name}(message = nil)
174
+ # self << message if #{number} >= level
175
+ # self
176
+ # end
177
+ #
178
+ # # Appends a message to the log if the log level is at least as high as
179
+ # # the log level of the logger. The bang! version of the method also auto
180
+ # # flushes the log buffer to disk.
181
+ # #
182
+ # # ==== Parameters
183
+ # # string<String>:: The message to be logged. Defaults to nil.
184
+ # #
185
+ # # ==== Returns
186
+ # # self:: The logger object for chaining.
187
+ # def #{name}!(message = nil)
188
+ # self << message if #{number} >= level
189
+ # flush if #{number} >= level
190
+ # self
191
+ # end
192
+ #
193
+ # # ==== Returns
194
+ # # Boolean:: True if this level will be logged by this logger.
195
+ # def #{name}?
196
+ # #{number} >= level
197
+ # end
198
+ # LEVELMETHODS
199
+ # end
200
+ #
201
+ # end
202
+ #
203
+ # end