logg 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ # A sample Guardfile
2
+ # More info at http://github.com/guard/guard#readme
3
+
4
+ guard 'cucumber' do
5
+ watch('^features/(.*).feature')
6
+ watch('^features/support') { 'features' }
7
+ watch('^features/step_definitions') { 'features' }
8
+ end
9
+
10
+ guard 'rspec', :version => 2 do
11
+ watch('^spec/(.*)_spec.rb')
12
+ watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
13
+ watch('^spec/spec_helper.rb') { "spec" }
14
+
15
+ # Rails example
16
+ watch('^app/(.*)\.rb') { |m| "spec/#{m[1]}_spec.rb" }
17
+ # watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
18
+ watch('^config/routes.rb') { "spec/routing" }
19
+ watch('^app/controllers/application_controller.rb') { "spec/controllers" }
20
+ watch('^spec/factories.rb') { "spec/models" }
21
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2010 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Logg
2
+
3
+ A simple message dispatcher (aka. logger) for your Ruby applications.
4
+
5
+ ## Install
6
+
7
+ ``` bash
8
+ $ gem install logg
9
+ ```
10
+
11
+ ## Synopsis
12
+
13
+ Logg is a library providing generic logging features. At the core of Logg is a module, `Logg::Machine`, which you may include (mixin) in a class, or extend within another module. This will inject the Logg helpers, so one can write something like this:
14
+
15
+ ``` ruby
16
+ class Foo
17
+ include Logg::Machine
18
+ end
19
+
20
+ Foo.log.debug "test!" # => Fri Dec 31 16:00:09 +0100 2010 | [debug] test!
21
+ Foo.new.log.debug "test…" # => Fri Dec 31 16:00:09 +0100 2010 | [debug] test…
22
+ Foo.new.log.error "failed" # => Fri Dec 31 16:00:09 +0100 2010 | [error] failed
23
+ ```
24
+
25
+ This illustrates the basic use case, with the default message format being: `time | [namespace] message` where namespace is the method called on the logger.
26
+
27
+ Many other use cases are available under the `examples/` directory, based on the Cucumber `features/`. This README explains some of them.
28
+
29
+ ## Custom loggers
30
+
31
+ Usually, logging engines provide you with a bunch of "log levels", such as FATAL, ERROR, WARNING, NOTICE. Logg does not enforce such a convention and rather let you define your own, if required, but does not enforce you to do so. More generally, one may create custom loggers using `Logg::Dispatcher#as`:
32
+
33
+ ``` ruby
34
+ class Foo
35
+ include Logg::Machine
36
+
37
+ # let's define a custom logger
38
+ log.as(:failure) do |data|
39
+ # play with data and render/do something, somewhere: output a String,
40
+ # send an email, anything you want.
41
+ end
42
+
43
+ # then use it!
44
+ log.failure my_data
45
+ end
46
+ ```
47
+
48
+ `as` expects a mandatory block, which may take any number of arguments, of any kind. Within the block, it is expected you will "log" somehow, but actually you are free to perform anything. You may output a simple string on $stdout, call an external API through HTTP, send an email, or even render a template (see below): that's just legacy ruby code in here! All in all, Logg is just a mega-method-definition-machine, aimed at logging—but feel free to use it the way you like (dispatching events, for instance).
49
+
50
+ Soon to come: the possibility to change `#log` for any other valid method name.
51
+
52
+ Note: if you would like to define a custom logger under the name `#as`, the helper method is also available under the `#_as` alias.
53
+
54
+ ## Message formatting, templates
55
+
56
+ Logging is all about building meaningful messages. You may also want to log to the tty, a file and send an email on top of that, and each one of those output channels would benefit from using a different data representation. One should thus be provided with efficient tools to define how a message is rendered in particular context. Logg makes use of Tilt to help you format your data. Tilt is a wrapper around several template engines (you may know about ERB or haml, but there are many others). Just tell Logg which format you want to use and go ahead! The dispatching logic is of your responsability.
57
+
58
+ For more details, see `examples/` and read/run the Cucumber `features/` (command: `cucumber features`).
59
+
60
+ ``` ruby
61
+ class Foo
62
+ include Logg::Machine
63
+
64
+ # let's use vanilla ruby code for the first example: output the
65
+ # message to $stdout using #puts
66
+ log.as(:foo) do
67
+ puts "something really important happened"
68
+ end
69
+
70
+ # you may also define a template within the block, render it and
71
+ # use the result.
72
+ # (Keep in mind that this kind of template with heavy code is considered bad practice ;))
73
+ log.as(:foo) do |data|
74
+ tpl = "Data: <%= self.map { |k,v| puts k.to_s + v.to_s } %>"
75
+ puts render_inline(tpl, :as => :erb, :data => data)
76
+ end
77
+ log.foo({:foo => :bar}) # => "Data: foobar"
78
+
79
+ # now we want to render an external HAML template, providing its path with
80
+ # or withouth the .haml extension (if not provided, the :as option is mandatory)
81
+ log.as(:http_response) do |resp|
82
+ output = render('tpl/foo', :as => :haml, :data => resp)
83
+ # do something with output
84
+ end
85
+ end
86
+ ```
87
+
88
+ If you want to render to several logging endpoints, and send a mail on top of that, just do it within the block!
89
+
90
+ ## Dispatching helpers
91
+
92
+ TODO: provide helpers for message dispatching/logging, levels managment and the like.
93
+
94
+ ## About the logger implementation
95
+
96
+ When a class mixins the `Logg::Machine` module, a `Logg::Dispatcher` instance is created and associated (if possible, see below) to the receiving class, through method injection. The custom loggers blocks are runned in the context of a `Logg::Dispatcher::Render` class, so be aware you must inject in the closure any data you would require. This is by design so as to keep the logger's logic separated from the application burden, enforcing explicit control over the data payloads. If this is just too much a burden for you, you may avoid mixin `Logg::Machine` and just make use of the `Render` core implementation.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Logg'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
data/lib/logg.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'tilt'
2
+ require 'pathname'
3
+ require 'better/tempfile'
4
+ require 'logg/core'
data/lib/logg/core.rb ADDED
@@ -0,0 +1,220 @@
1
+ module Logg
2
+ # Set to true to puts output when using logger#debug default's method.
3
+ ALWAYS_PUTS = true
4
+
5
+ # A Dispatcher is a logger core implementation, providing logger's definition
6
+ # and output methods. It's not intented to be used directly but through the
7
+ # Er mixin, within a class.
8
+ #
9
+ class Dispatcher
10
+ class Render
11
+ # Render a template. Just a mere proxy for Tilt::Template#render method,
12
+ # the first argument being the filepath or file, and the latter,
13
+ # the usual arguments for Tilt's #render.
14
+ #
15
+ # @param [String, #path, #realpath] path filepath or an object behaving
16
+ # like a legacy File
17
+ # @param [Object] obj context object the template will be rendered within
18
+ # @param [Hash] args rendering context
19
+ # @option [Symbol] :as syntax engine
20
+ # @option [Object] :data template's rendering contextual object
21
+ # @option [Hash] :locals template's locals
22
+ # @return [String] the interpolated template
23
+ #
24
+ def render(path, *args)
25
+ args = args.first
26
+ path = detect_path(path)
27
+ tpl = fetch_template(args, path)
28
+ tpl.render(args[:data], args[:locals])
29
+ end
30
+
31
+ def render_inline(content, *args)
32
+ args = args.first
33
+ syntax = detect_syntax(args)
34
+ res = Object
35
+
36
+ Better::Tempfile.open(['dummylogg', ".#{syntax}"]) do |f|
37
+ f.write(content)
38
+ f.rewind
39
+ res = Tilt.new(f.path).render(args[:data], args[:locals])
40
+ end
41
+ res
42
+ end
43
+
44
+ def detect_path(path)
45
+ if path.respond_to?(:path)
46
+ path.path
47
+ elsif path.respond_to?(:realpath)
48
+ path.to_s
49
+ elsif path.respond_to?(:to_s)
50
+ path.to_s
51
+ else
52
+ raise ArgumentError, 'Missing file or a filepath.'
53
+ end
54
+ end
55
+
56
+ def fetch_template(args, path)
57
+ if args[:as]
58
+ begin
59
+ test_path = Pathname.new(path)
60
+ raise ArgumentError, "Invalid filepath #{path}" unless test_path.file?
61
+ rescue
62
+ test_path = Pathname.new(path + ".#{args[:as].to_s.downcase}")
63
+ raise ArgumentError, "Invalid filepath #{path}" unless test_path.file?
64
+ path = test_path.to_s
65
+ end
66
+ Tilt.const_get("#{args[:as].to_s.downcase.capitalize}Template").new(path)
67
+ else
68
+ Tilt.new(path)
69
+ end
70
+ end
71
+
72
+ def detect_syntax(options)
73
+ unless options.has_key?(:as)
74
+ raise ArgumentError, 'Missing template syntax specified as the :as option.'
75
+ end
76
+ options[:as].to_s
77
+ end
78
+ end
79
+
80
+ attr_reader :message, :namespace
81
+
82
+ # The Dispatcher default behavior relies on #method_missing. It sets both the
83
+ # message and a namespace, then auto-sends the order to output.
84
+ #
85
+ def method_missing(meth, *args, &block)
86
+ @namespace = meth.to_s
87
+ @message = (args.first.to_s == 'debug') ? nil : args.first.to_s
88
+ self.send :output!
89
+ end
90
+
91
+ def eigenclass
92
+ class << self; self; end
93
+ end
94
+
95
+ # Define a custom logger, using a template. The template may be defined
96
+ # within the block as a (multi-line) string, or one may reference a
97
+ # file.
98
+ # # do whatever you want with data or anything else, for instance,
99
+ # send mails, tweet, then…
100
+ #
101
+ # Inline templates (defined within the block) make use of #render_inline
102
+ # (indentation broken for the sake of example readibility):
103
+ #
104
+ # logger.as(:custom) do |response|
105
+ # tpl = <<-TPL
106
+ # %h2 Query log report
107
+ # %span
108
+ # Statu:
109
+ # = data.status
110
+ # %span
111
+ # Response:
112
+ # = data.body
113
+ # %br/
114
+ # TPL
115
+ # puts render_inline(tpl, :as => :haml, :data => response)
116
+ # end
117
+ #
118
+ # With an external template, one should use the #render helper to, well,
119
+ # render the template file. The extension will be used to infer the proper
120
+ # rendering engine. If not provided or when a custom extension is used, one
121
+ # may declare the template syntax.
122
+ #
123
+ # logger.as(:custom) do |data|
124
+ # # do whatever you want with data or anything else, then…
125
+ # out = render('my/template.erb', :data => data)
126
+ # # one may then use out to send mails, log to file, tweet…
127
+ # end
128
+ #
129
+ # logger.as(:custom) do |data|
130
+ # render('my/template', :as => :erb, :data => data)
131
+ # end
132
+ #
133
+ # See #render and #render_inline for more details.
134
+ #
135
+ # TODO: memoize the Render instance somehow? Or find another trick to
136
+ # execute the block.
137
+ #
138
+ def as(method, &block)
139
+ raise ArgumentError, 'Missing mandatory block' unless block_given?
140
+
141
+ method = method.to_sym
142
+
143
+ # Define the guard at class-level, if not already defined.
144
+ if !eigenclass.respond_to?(method)
145
+ eigenclass.send(:define_method, method) do |*args|
146
+ Render.new.instance_exec(*args, &block)
147
+ end
148
+ end
149
+
150
+ # Define the guard at instance-level by overriding #initialize, if not
151
+ # already defined.
152
+ eigenclass.send(:define_method, :new) do
153
+ o = super
154
+ if !o.respond_to?(method)
155
+ o.send(:define_method, method) do |*args|
156
+ Render.new.instance_exec(*args, &block)
157
+ end
158
+ end
159
+ o
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ # Default logging behavior. Outputs to $stdout using #puts and return
166
+ # the message.
167
+ #
168
+ def output!
169
+ output = "#{Time.now} | "
170
+ output += "[#{@namespace.gsub('_', ' ')}] " unless @namespace.nil?
171
+ output += @message
172
+ puts output if defined?(Logg::ALWAYS_PUTS) && Logg::ALWAYS_PUTS
173
+ return output
174
+ end
175
+ end
176
+
177
+ # The Er module, when mixed-in a class, instantiates a Dispatcher and performs some
178
+ # simple meta-programming on this receiver to add support for the logger. It thus
179
+ # enable the receiver to use the logger's default implementation and/or define
180
+ # custom loggers.
181
+ #
182
+ module Machine
183
+ LOGGER = Logg::Dispatcher.new
184
+ #NAME = (defined?(::Logg::LOG_METHOD) && ::Logg::LOG_METHOD) || :log
185
+ NAME = :log
186
+
187
+ def self.included(base)
188
+ if !base.respond_to?(NAME)
189
+ base.instance_eval do
190
+ # Memoized logger for the receiver's class.
191
+ #
192
+ # TODO: add support for defining the logger under a different name than #log,
193
+ # this means either defining a constant before mixin, or delaying the metaprog.
194
+ class << self; self; end.instance_eval do
195
+ define_method(NAME) do
196
+ @@_logg_er ||= LOGGER
197
+ end
198
+ end
199
+ end
200
+ else
201
+ raise RuntimeError, "Cannot mixin Logg::Er as #{base}#logger, method's already defined."
202
+ end
203
+
204
+ # Memoized logger for the receiver.
205
+ base.extend RedefInit
206
+ end
207
+
208
+ module RedefInit
209
+ def new *args, &block
210
+ o = super
211
+ if !o.respond_to?(NAME)
212
+ class << o; self; end.send(:define_method, NAME) do
213
+ @_logg_er ||= LOGGER
214
+ end
215
+ end
216
+ o
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,2 @@
1
+ module Logg
2
+ end
@@ -0,0 +1,6 @@
1
+ module Logg
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ PATCH = 4
5
+ VERSION = [MAJOR, MINOR, PATCH].join('.')
6
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logg
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.4
6
+ platform: ruby
7
+ authors:
8
+ - Jean-Denis Vauguet <jdvauguet@af83.com>
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-02 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: A simple logger for your ruby applications.
18
+ email: jdvauguet@af83.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - lib/logg/render.rb
27
+ - lib/logg/version.rb
28
+ - lib/logg/core.rb
29
+ - lib/logg.rb
30
+ - MIT-LICENSE
31
+ - Rakefile
32
+ - Guardfile
33
+ - README.md
34
+ has_rdoc: true
35
+ homepage: http://www.github.com/af83/logg
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.6.1
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: A simple logger.
62
+ test_files: []
63
+