shog 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e02b8810b0926489472a9c3ae7172681c903d247
4
- data.tar.gz: 82e7c9c7d3fbc7ea093b79f3afd0c638c9bab046
3
+ metadata.gz: d11bb673ce2d41a4d247e564223a567f17a3b327
4
+ data.tar.gz: c2f4489f6612357260fdfd8d5279ebaa53605c94
5
5
  SHA512:
6
- metadata.gz: 3a851ea89f9357838dd1d6b2f31108757b1b846cdcfec47004887a08cf825c4afd086217251c49e403f2589f1697b15d5d1094043e715ba821c2e70258bf25c1
7
- data.tar.gz: 515b080f51d3f66fa3d49f48c1a9e6fbc26916c46e27261195c38eb3aa244a384981b229ef3584e8f45314a198be31842cd0a4907b55f35c2f291fae4fe384e6
6
+ metadata.gz: 1cdb0e922b0536b88ff3ea00f8e4fce9fe5dd95ba54400e00cf103ef5cc506cead951f7f816be42724f967b3053dd6c874c32233e95dda8fbf7bedcba8c705b1
7
+ data.tar.gz: e18c58381f770385352b1ad7123c96fccf466b9e5122f07a9d2b755ba4ca67a9684d4e82abe815082f0e1657d30ada3d59adc73a72424caeb2aa6e010dee4119
data/README.md CHANGED
@@ -2,32 +2,53 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/shog.svg)](http://badge.fury.io/rb/shog)
4
4
  [![Code Climate](https://codeclimate.com/github/phallguy/shog.png)](https://codeclimate.com/github/phallguy/shog)
5
- [![Dependency Status](https://gemnasium.com/phallguy/shog.svg)](https://gemnasium.com/phallguy/shog)
6
5
 
7
6
 
8
7
  Make rails 4.0 log details more colorful.
9
8
 
10
9
  There are plenty of logging frameworks for making tags (like timestamp, log
11
10
  level, etc.) more colorful - but what about the details in the line of text?
12
- What about the HTTP method used to make the request? What about the times.
11
+ What about the HTTP method used to make the request? What about the render
12
+ times?
13
13
 
14
- ## Installation
14
+ Shog adds colors to highlight context and make it easier to pick out the
15
+ important parts of the message. Unformatted logs like this
15
16
 
16
- Add this line to your application's Gemfile:
17
+ ![Plain Logs](docs/images/plain.png)
17
18
 
18
- gem 'shog'
19
+ become easy to ready and process logs like this
19
20
 
20
- And then execute:
21
+ ![Shogged Logs](docs/images/shogged.png)
21
22
 
22
- $ bundle
23
+ ## Using Shog
23
24
 
24
- Or install it yourself as:
25
+ Just add the following to your Gemfile to enable shiny output logs.
25
26
 
26
- $ gem install shog
27
+ ```
28
+ gem 'shog'
29
+ ```
27
30
 
28
- ## Usage
31
+ Shog comes built in with some sane defaults for rails apps. But you can
32
+ customize the output to match additional log message by your app, or to use
33
+ different options in production.
29
34
 
30
- TODO: Write usage instructions here
35
+ To change the defaults, add `shog.rb` to your `config/initializers` folder
36
+
37
+ ```ruby
38
+ Shog.configure do
39
+ if ::Rails.env.production?
40
+ reset_config!
41
+ timestamp
42
+ end
43
+
44
+ match /execution expired/ do |msg,matches|
45
+ # Highlight timeout errors
46
+ msg.red
47
+ end
48
+ end
49
+ ```
50
+
51
+ See [Shog::Formatter#configure](lib/shog/formatter.rb) for more configuration options.
31
52
 
32
53
  ## Contributing
33
54
 
Binary file
Binary file
@@ -2,31 +2,43 @@ require 'active_support/tagged_logging'
2
2
  require 'colored'
3
3
 
4
4
  module Shog
5
+
6
+ # A rails logger formatter that spices up the log message adding color to
7
+ # and context to log messages.
8
+ #
9
+ # Shog automatically overrides the default formatter in your rails app. Use
10
+ # {Shog.configure} to configure the default logger.
5
11
  class Formatter < ::ActiveSupport::Logger::SimpleFormatter
6
12
 
7
13
  include ActiveSupport::TaggedLogging::Formatter
8
14
 
15
+ def initialize
16
+ reset_config!
17
+ end
18
+
19
+ # Called by the logger to prepare a message for output.
20
+ # @return [String]
9
21
  def call( severity, time, progname, msg )
10
- return if msg.blank?
11
- tagged formatted_severity_tag( severity ) do
12
- msg = formatted_message( severity, msg )
13
- msg = timestamped_message( time, msg )
14
- msg = prognamed_message( progname, msg )
22
+ return if msg.blank? || _silence?( msg )
15
23
 
16
- super severity, time, progname, msg
17
- end
18
- end
24
+ msg = [
25
+ _tagged( time, :timestamp ),
26
+ _tagged( progname, :progname ),
27
+ formatted_severity_tag( severity ),
28
+ formatted_message( severity, msg )
29
+ ].compact.join(" ")
19
30
 
20
- def initialize
21
- reset_config!
31
+ super severity, time, progname, msg
22
32
  end
23
33
 
24
- # Formats the message according to the configured settings.
34
+ # Formats the message according to the configured {#match} blocks.
35
+ #
25
36
  # @param [String] msg to format.
37
+ # @return [String] the formatted message.
26
38
  def formatted_message( severity, msg )
27
39
  msg = String === msg ? msg : msg.inspect
28
40
 
29
- if args = matched( msg )
41
+ if args = _matched( msg )
30
42
  args.first.call msg, args.last
31
43
  elsif proc = configuration[:severities][severity]
32
44
  proc.call msg
@@ -37,6 +49,7 @@ module Shog
37
49
 
38
50
  # Formats the severity indicator prefixed before each line when writing to
39
51
  # the log.
52
+ #
40
53
  # @param [String] the severity of the message (ex DEBUG, WARN, etc.)
41
54
  # @return [String] formatted version of the severity
42
55
  def formatted_severity_tag( severity )
@@ -44,16 +57,27 @@ module Shog
44
57
  configuration[:severity_tags].reduce(0){ |l,(k,_)| [k.length,l].max }
45
58
  end
46
59
 
60
+ return if length == 0
61
+
47
62
  padded_severity = severity.ljust length
48
63
 
49
- if proc = configuration[:severity_tags][severity]
50
- proc.call padded_severity
51
- else
52
- padded_severity
53
- end
64
+ formatted = if proc = configuration[:severity_tags][severity]
65
+ proc.call padded_severity
66
+ else
67
+ padded_severity
68
+ end
69
+ _tagged formatted, :severity_tags
54
70
  end
55
71
 
56
- # Formats a ms time value.
72
+ # Formats a time value expressed in ms, adding color to highlight times
73
+ # outside the expected range.
74
+ #
75
+ # If `time` is more thatn `expected` it's highligted yellow. If it's more
76
+ # than double it's highlighted red.
77
+ #
78
+ # @param [String] time in ms.
79
+ # @param [Float] expected maximum amount of time it should have taken.
80
+ # @return [String] the formatted time.
57
81
  def format_time( time, expected = 30 )
58
82
  timef = time.to_f
59
83
  case
@@ -67,57 +91,181 @@ module Shog
67
91
  # ==========================================================================
68
92
  # @!group Configuration
69
93
 
70
- # Configure messages formatting for this formatter.
94
+ # Set up log message formatting for this formatter.
95
+ #
96
+ # @yield and executes the block where self is this formatter.
97
+ # @return [Formatter] self.
98
+ #
99
+ # @example
100
+ # Formatter.new.configure do
101
+ # with :defaults
102
+ # timestamp
103
+ # severity(:error){ |msg| msg.red }
104
+ # severity(:fatal){ |msg| "\b#{msg}".red }
105
+ # end
71
106
  def configure( &block )
72
107
  instance_eval( &block )
73
108
  self
74
109
  end
75
110
 
76
- # Format the severity tagged before each line.
77
- def severity_tag( level, &block )
78
- configuration[:severity_tags][ level.to_s.upcase ] = block
111
+ # Format the severity indicator tagged before each line. To format the
112
+ # actual message itself use {#severity}.
113
+ #
114
+ # @overload severity_tag( level, proc )
115
+ # @param [String,Symbol] level to format.
116
+ # @param [#call(level)] proc that receives the log level and returns the
117
+ # reformatted level.
118
+ #
119
+ # @overload severity_tag( level )
120
+ # @param [String,Symbol] level to format.
121
+ # @yieldparam level [String] the log level to reformat.
122
+ # @yieldreturn [String] the reformatted level.
123
+ #
124
+ # @return [Formatter] self.
125
+ #
126
+ # @example
127
+ # configure do
128
+ # severity_tag(:warn){|level| level.yellow }
129
+ # severity_tag(:error){|level| level.red }
130
+ # end
131
+ def severity_tag( level, proc = nil, &block )
132
+ proc ||= block
133
+ configuration[:severity_tags][ level.to_s.upcase ] = proc
134
+ self
79
135
  end
80
136
 
81
- # Provide customized formatting for messages of the given severity when they
82
- # a custom matcher cannot be found.
83
- # @param [String,Symbol] level to format.
84
- def severity( level, &block )
85
- configuration[:severities][ level.to_s.upcase ] = block
137
+ # Provide default formatting for messages of the given severity when
138
+ # a {#match} is not found.
139
+ #
140
+ # @overload severity( level, proc )
141
+ # @param [String,Symbol] level to format.
142
+ # @param [#call(msg)] proc that receives the message and returns the
143
+ # reformatted message.
144
+ # @overload severity( level )
145
+ # @param [String,Symbol] level to format.
146
+ # @yieldparam msg [String] the message to reformat.
147
+ # @yieldreturn [String] the reformatted message.
148
+ #
149
+ # @return [Formatter] self.
150
+ #
151
+ # @example
152
+ # configure do
153
+ # severity(:fatal){ |msg| msg.white_on_red }
154
+ # end
155
+ def severity( level, proc = nil, &block )
156
+ proc ||= block
157
+ configuration[:severities][ level.to_s.upcase ] = proc
158
+ self
86
159
  end
87
160
 
88
161
  # Resets any previously configured formatting settings.
162
+ # @return [Formatter] self.
89
163
  def reset_config!
90
164
  @configuration ||= {
91
165
  severity_tags: {},
92
166
  severities: {},
93
- matchers: {}
167
+ matchers: {},
168
+ silencers: []
94
169
  }
95
170
  self
96
171
  end
97
172
 
98
- # When a log message matches the given pattern, provide a custom format
99
- # for it.
100
- def match( pattern, &block )
101
- configuration[:matchers][pattern] = block
173
+ # Re-format any log messages that match the given `pattern`.
174
+ #
175
+ # @overload match( pattern, proc)
176
+ # @param [Regexp] pattern to match against the log message.
177
+ # @param [#call(message,last_match)] proc a callable object that receives
178
+ # the message and the last match and re-formats the message.
179
+ #
180
+ # @overload match( pattern )
181
+ # @param [Regexp] pattern to match against the log message.
182
+ # @yieldparam message [String] the matched log message.
183
+ # @yieldparam last_match [MatchData] the regex matches.
184
+ # @yieldreturn [String] the re-formatted message.
185
+ #
186
+ # @example
187
+ # configure do
188
+ # match /GET (?<address>.*)/ do |message,last_match|
189
+ # "GETTING -> #{last_match['address'].green}"
190
+ # end
191
+ # end
192
+ # @return [Formatter] self.
193
+ def match( pattern, proc = nil, &block )
194
+ proc ||= block
195
+ configuration[:matchers][pattern] = proc
196
+ self
197
+ end
198
+
199
+ # When a log message matches the given `pattern` don't log it.
200
+ #
201
+ # @param [Regexp] pattern to match.
202
+ #
203
+ # @return [Formatter] self.
204
+ #
205
+ # @example
206
+ # configure do
207
+ # silence /assets\/bootstrap/
208
+ # end
209
+ def silence( pattern )
210
+ configuration[:silencers] << pattern
211
+ self
102
212
  end
103
213
 
104
- # Adds the named matchers to the log
105
- def formatter( mod )
214
+ # Use configuration defined in the given module.
215
+ #
216
+ # @param [Symobl,#configure] mod the name of the shog module to use or an
217
+ # object that responds to `#configure`.
218
+ #
219
+ # @return [Formatter] self.
220
+ #
221
+ # When `mod` is a symobl, it loads one of the modules from
222
+ # {Shog::Formatters} and uses any configuration options sepcified in that
223
+ # module.
224
+ #
225
+ # Otherwise `mod` must respond to `#configure` taking a single argument -
226
+ # this formatter.
227
+ #
228
+ # @example Built-in Formatters
229
+ # configure do
230
+ # with :defaults
231
+ # with :requests
232
+ # end
233
+ #
234
+ # @example Custom Shared Formatters
235
+ # module MyFormatters
236
+ # def self.configure( formatter )
237
+ # formatter.configure do
238
+ # timestamp
239
+ # end
240
+ # end
241
+ # end
242
+ #
243
+ # configure do
244
+ # with MyFormatters
245
+ # end
246
+ def with( mod )
106
247
  unless mod.is_a? Module
107
248
  mod = "Shog::Formatters::#{mod.to_s.camelize}".constantize
108
249
  end
109
250
 
110
251
  mod.configure self
252
+ self
111
253
  end
112
254
 
113
255
  # Include timestamp in logged messages.
114
- def timestamp( enabled = true )
115
- configuration[:timestamp] = enabled
256
+ # @param [Boolean] enable or disable timestamping of log messages.
257
+ # @return [Formatter] self.
258
+ def timestamp( enable = true )
259
+ configuration[:timestamp] = enable
260
+ self
116
261
  end
117
262
 
118
263
  # Include the progname in logged messages.
119
- def progname( enabled = true )
120
- configuration[:progname] = enabled
264
+ # @param [Boolean] enable or disable tagging with the prog name of log messages.
265
+ # @return [Formatter] self.
266
+ def progname( enable = true )
267
+ configuration[:progname] = enable
268
+ self
121
269
  end
122
270
 
123
271
  # @!endgroup
@@ -126,7 +274,7 @@ module Shog
126
274
 
127
275
  attr_accessor :configuration
128
276
 
129
- def matched( msg )
277
+ def _matched( msg )
130
278
  if matched = configuration[:matchers].find do |pattern,_|
131
279
  pattern === msg
132
280
  end
@@ -134,16 +282,13 @@ module Shog
134
282
  end
135
283
  end
136
284
 
137
- def timestamped_message( time, msg )
138
- return msg unless configuration[:timestamp]
139
-
140
- "[#{time}] #{msg}"
285
+ def _tagged( val, config_key )
286
+ return unless configuration[config_key]
287
+ "[#{val}]"
141
288
  end
142
289
 
143
- def prognamed_message( progname, msg )
144
- return msg unless configuration[:progname]
145
-
146
- "[#{progname}] #{msg}"
290
+ def _silence?( msg )
291
+ configuration[:silencers].any?{|p| p === msg }
147
292
  end
148
293
  end
149
294
  end
@@ -1,9 +1,12 @@
1
1
  module Shog
2
2
  module Formatters
3
- # Default formatting options
3
+
4
+ # Provide common default log formatting options.
4
5
  module Defaults
5
6
  module_function
6
7
 
8
+ # @see Shog::Formatter#configure
9
+ # @see Shog::Formatter#with
7
10
  def configure( formatter )
8
11
  formatter.configure do
9
12
  severity_tag( :debug ) { |msg| msg.black.bold }
@@ -1,21 +1,28 @@
1
1
  module Shog
2
2
  module Formatters
3
- # Format log messages from the request processing such as controller endpoints and views.
3
+ # Provide common log formatting options for rails request logs such as
4
+ # controller names views, and render times.
4
5
  module Requests
5
6
  module_function
6
7
 
8
+ # @see Shog::Formatter#configure
9
+ # @see Shog::Formatter#with
7
10
  def configure( formatter )
8
11
  formatter.configure do
12
+
13
+ # Highlight HTTP request methods
9
14
  match /Started\s+(?<method>PUT|PATCH|GET|POST|DELETE)\s+(?<path>"[^"]*")[^\d\.]+(?<ip>[\d\.]+)(?<time>.*)/ do |msg,match|
10
15
  # http://refiddle.com/ge6
11
16
  "#{match["method"].ljust 6} ".green.bold + " #{match["path"]} ".white.bold + " for " + "#{match["ip"]}".yellow + " #{match["time"]}".black.bold
12
17
  end
13
18
 
19
+ # Dim detailed info about rendering views
14
20
  match /\s*Rendered\s+(?<view>[^\s]+)\swithin\s(?<layout>[^\s]+)\s\((?<time>.*)\)/ do |msg,match|
15
21
  # http://refiddle.com/18qr
16
- " Rendered " + match["view"].white.bold + " within " + match["layout"].white + " " + format_time( match['time'] )
22
+ " Rendered #{ match["view"] } within ".black + match["layout"].black.bold + " " + format_time( match['time'].black )
17
23
  end
18
24
 
25
+ # Highlight the final rendered response
19
26
  match /\s*Completed\s(?<code>\d+)\s(?<friendly>.*)\sin\s(?<time>\d+[^\s]*)\s(?<details>.*)/ do |msg,match|
20
27
  # http://refiddle.com/18qq
21
28
  parts = [ "Completed" ]
@@ -32,6 +39,7 @@ module Shog
32
39
  parts.join(" ")
33
40
  end
34
41
 
42
+ # Highlight the controller and action responding to the request
35
43
  match /Processing by (?<controller>[^\s]*) as (?<format>.*)/ do |msg,match|
36
44
  # http://refiddle.com/18qs
37
45
  "Processing by #{match['controller'].magenta.bold} as #{match['format'].yellow}"
data/lib/shog/rails.rb CHANGED
@@ -2,13 +2,16 @@ require 'rails'
2
2
 
3
3
  module Shog
4
4
  module Rails
5
+
6
+ # Automatically integrate Shog with the rails logger.
5
7
  class Railtie < ::Rails::Railtie
6
8
  config.before_initialize do
7
9
  ::Rails.logger.formatter = Shog::Formatter.new.configure do
8
- formatter :defaults
9
- formatter :requests
10
+ with :defaults
11
+ with :requests
10
12
  end
11
13
  end
12
14
  end
15
+
13
16
  end
14
17
  end
data/lib/shog/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Shog
2
- VERSION_NUMBER = "0.0.2"
2
+ VERSION_NUMBER = "0.1.0"
3
3
  VERSION_SUFFIX = ""
4
4
  VERSION = "#{VERSION_NUMBER}#{VERSION_SUFFIX}"
5
5
  end
data/lib/shog.rb CHANGED
@@ -5,6 +5,9 @@ require 'shog/rails'
5
5
  require 'pry'
6
6
 
7
7
  module Shog
8
+
9
+ # Set up formatting options for the default rails logger.
10
+ # @see Shog::Formatter#configure
8
11
  def self.configure(&block)
9
12
  ::Rails.logger.formatter.configure &block
10
13
  end
@@ -12,16 +12,70 @@ end
12
12
  describe Shog::Formatter do
13
13
  let(:formatter){ Shog::Formatter.new }
14
14
 
15
+ describe "#call" do
16
+ it "without configuration doesn't modify the request" do
17
+ output = formatter.call "TEST", Time.now, nil, "Unformatted"
18
+ expect( output ).to eq "Unformatted\n"
19
+ end
20
+
21
+ it "adds severity when at least one is configured" do
22
+ formatter.severity_tag :debug, ->(msg){ msg }
23
+ output = formatter.call "DEBUG", Time.now, nil, "Tagged"
24
+
25
+ expect( output ).to eq "[DEBUG] Tagged\n"
26
+ end
27
+
28
+ it "adds timestamp when configured" do
29
+ formatter.timestamp
30
+ output = formatter.call "WHEN", "just now", nil, "we missed them"
31
+
32
+ expect( output ).to eq "[just now] we missed them\n"
33
+ end
34
+
35
+ it "adds progname when configured" do
36
+ formatter.progname
37
+ output = formatter.call "NAME", Time.now, "proggy", "magic"
38
+
39
+ expect( output ).to eq "[proggy] magic\n"
40
+ end
41
+
42
+ it "adds them all" do
43
+ formatter.configure do
44
+ timestamp
45
+ progname
46
+ severity_tag :debug, ->(msg){ msg }
47
+ end
48
+
49
+ output = formatter.call "DEBUG", "NOW", "proggy", "gets them all"
50
+ expect( output ).to eq "[NOW] [proggy] [DEBUG] gets them all\n"
51
+ end
52
+ end
53
+
15
54
  describe "#formatted_severity_tag" do
16
55
  it "buffers to the same size" do
17
56
  formatter.severity_tag( :warn ){ |msg| msg }
18
57
  formatter.severity_tag( :longer ){ |msg| msg }
19
58
 
20
- expect( formatter.formatted_severity_tag( "WARN" ) ).to eq "WARN "
21
- expect( formatter.formatted_severity_tag( "LONGER" ) ).to eq "LONGER"
59
+ expect( formatter.formatted_severity_tag( "WARN" ) ).to eq "[WARN ]"
60
+ expect( formatter.formatted_severity_tag( "LONGER" ) ).to eq "[LONGER]"
22
61
  end
23
62
  end
24
63
 
64
+ describe "#format_time" do
65
+ it "doesn't change when within expected" do
66
+ expect( formatter.format_time( "10ms".cyan, 30 ) ).to eq "10ms".cyan
67
+ end
68
+
69
+ it "is yellow when above expected" do
70
+ expect( formatter.format_time( "50ms", 30 ) ).to eq "50ms".yellow
71
+ end
72
+
73
+ it "is red when way above expected" do
74
+ expect( formatter.format_time( "150ms", 30 ) ).to eq "150ms".red
75
+ end
76
+
77
+ end
78
+
25
79
  describe "#match" do
26
80
  it "formats a matched line" do
27
81
  formatter.match /GET/ do |msg,match|
@@ -29,7 +83,7 @@ describe Shog::Formatter do
29
83
  end
30
84
 
31
85
  result = formatter.call "INFO", Time.now, nil, "Started GET \"/Home\""
32
- expect( result ).to eq "[INFO] R'DONE\n"
86
+ expect( result ).to eq "R'DONE\n"
33
87
  end
34
88
 
35
89
  it "includes the match" do
@@ -42,26 +96,44 @@ describe Shog::Formatter do
42
96
 
43
97
  it "doesn't match" do
44
98
  result = formatter.call "INFO", Time.now, nil, "Started GET \"/Home\""
45
- expect( result ).to eq "[INFO] Started GET \"/Home\"\n"
99
+ expect( result ).to eq "Started GET \"/Home\"\n"
46
100
  end
47
101
 
48
102
  it "uses default severity when no matcher is found" do
49
103
  formatter.severity( :info ){ |msg| "DEFAULT" }
50
104
 
51
105
  result = formatter.call "INFO", Time.now, nil, "Started GET \"/Home\""
52
- expect( result ).to eq "[INFO] DEFAULT\n"
106
+ expect( result ).to eq "DEFAULT\n"
53
107
  end
54
108
  end
55
109
 
56
- describe "#formatters" do
110
+ describe "#silence" do
111
+ it "silences matching logs" do
112
+ formatter.silence /loud/
113
+ output = formatter.call "DEBUG", Time.now, nil, "I'm a really loud message"
114
+ expect(output).to be_nil
115
+ end
116
+ end
117
+
118
+ describe "#formatter" do
57
119
  it "loads from a symbol" do
58
- formatter.should_receive(:severity).with(:test, anything())
120
+ expect( Shog::Formatters::Spec).to receive :configure
121
+
122
+ formatter.configure do
123
+ with :spec
124
+ end
125
+ end
126
+
127
+ it "lods from a module" do
128
+ expect( Shog::Formatters::Spec).to receive :configure
59
129
 
60
130
  formatter.configure do
61
- formatter :spec
131
+ with Shog::Formatters::Spec
62
132
  end
63
133
  end
64
134
  end
65
135
 
66
136
 
137
+
138
+
67
139
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Alexander
@@ -100,6 +100,8 @@ files:
100
100
  - Procfile
101
101
  - README.md
102
102
  - Rakefile
103
+ - docs/images/plain.png
104
+ - docs/images/shogged.png
103
105
  - lib/shog.rb
104
106
  - lib/shog/formatter.rb
105
107
  - lib/shog/formatters.rb