From fe99ec427423b83215cf9efc1f3f413d21f7e29d Mon Sep 17 00:00:00 2001 From: Alexandr Smirnov Date: Sat, 24 Jun 2017 16:08:40 +0300 Subject: [PATCH] Added documentation (#10) * prepared version dump to next release 1.0.0a would not be launched immediately, but version should be bumped 1.0.0a would be affected by big back-incompatible changes in #9, so it would be a good time to increment majorest version. Hovewer, i'll increment it now to simplify Changelog usage. * Added Changelog.md * added doc:coverage task into rake default to check yard coverage * done * fixed bugs and updated json dependency for ruby 2.4 compatibility * update travis build matrix to actual supported versions * updated rubocop to 0.49 --- .rubocop.yml | 1 + .travis.yml | 3 ++- Changelog.md | 47 ++++++++++++++++++++++++++++++++++ Gemfile | 1 + Rakefile | 42 +++++++++++++++++++++++++++++- lamian.gemspec | 6 ++++- lib/lamian.rb | 24 +++++++++++++++++ lib/lamian/config.rb | 4 +++ lib/lamian/engine.rb | 5 ++++ lib/lamian/logger.rb | 43 ++++++++++++++++++++++++++++--- lib/lamian/logger_extension.rb | 3 +++ lib/lamian/middleware.rb | 7 +++++ lib/lamian/version.rb | 15 ++++++++++- spec/lamian/logger_spec.rb | 1 + spec/lamian/middleware_spec.rb | 1 + spec/lamian_spec.rb | 1 + spec/spec_helper.rb | 1 + 17 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 Changelog.md diff --git a/.rubocop.yml b/.rubocop.yml index 14624ec..8d54dc2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,5 +2,6 @@ AllCops: Exclude: - bin/**/* TargetRubyVersion: 2.3 + Style/Documentation: Enabled: false diff --git a/.travis.yml b/.travis.yml index 03ee64f..c621a0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ sudo: false language: ruby rvm: - - 2.3.1 + - 2.3.4 + - 2.4.1 before_install: gem install bundler -v 1.12.5 diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..13b6506 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,47 @@ +# Lamian version changes (since 0.1.0) + +Update this on a pull request, under Lamian::VERSION +(also known as next version). If this constant would be changed without release, +i'll update it here too + +## 1.0.0alpha + +Add your changes here + + +## 0.3.3 + +* 8136689 fixed crashes when dump used outside lamian context + + +## 0.3.2 + +* e57e6cec Changed rails dependency from ~> 4.2 to >= 4.2 + + +## 0.3.1 + +* 34ca83b5 Fixed formatting + +Stabilized formatting api, which removes control sequences from loggers data. +E.g. "[23mNice, lol[0m\n" becomes "Nice, lol\n" + + +## 0.3.0 + +* d24f895b API update + +Updated API, so lamian is now forced to be used with block. +It also simplified usage outside a middleware + + +## 0.2.0 +* 3166517e Added integrtation with rails + +Injected middleware before ExceptionNotification, so ExceptionNotification +can use current log without any configuration. +Also added some views + + +## 0.1.0 +* 62eb8685 Made test version to check it's integration with rails application diff --git a/Gemfile b/Gemfile index c466a65..39eb344 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ # frozen_string_literal: true + source 'https://rubygems.org' # Specify your gem's dependencies in lamian.gemspec diff --git a/Rakefile b/Rakefile index 823219a..e37856d 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,49 @@ # frozen_string_literal: true + require 'bundler/gem_tasks' require 'rspec/core/rake_task' require 'rubocop/rake_task' +require 'yard' +require 'pathname' +require 'launchy' +require 'uri' + +ROOT = Pathname.new(__FILE__).join('..') RSpec::Core::RakeTask.new(:spec) RuboCop::RakeTask.new(:lint) -task default: %i(lint spec) +YARD::Rake::YardocTask.new(:doc) do |t| + t.files = Dir[ROOT.join('lib/**/*.rb')] + t.options = %w[--private] +end + +def open_in_browser(path) + Launchy.open(URI.join('file:///', path.to_s)) +end + +namespace :doc do + desc 'open doc' + task open: :doc do + open_in_browser ROOT.join('doc/frames.html') + end + + desc 'checks doc coverage' + task coverage: :doc do + # ideally you've already generated the database to .load it + # if not, have this task depend on the docs task. + YARD::Registry.load + objs = YARD::Registry.select do |o| + puts "pending #{o}" if o.docstring =~ /TODO|FIXME|@pending/ + o.docstring.blank? + end + + next if objs.empty? + puts 'No documentation found for:' + objs.each { |x| puts "\t#{x}" } + + raise '100% document coverage required' + end +end + +task default: %i[lint doc:coverage spec] diff --git a/lamian.gemspec b/lamian.gemspec index 27d6d80..fea0477 100644 --- a/lamian.gemspec +++ b/lamian.gemspec @@ -1,5 +1,6 @@ # coding: utf-8 # frozen_string_literal: true + lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'lamian/version' @@ -24,7 +25,10 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.12' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rspec', '~> 3.0' - spec.add_development_dependency 'rubocop', '~> 0.45' + spec.add_development_dependency 'rubocop', '~> 0.49' spec.add_development_dependency 'pry', '~> 0.10' spec.add_development_dependency 'coveralls', '~> 0.8' + spec.add_development_dependency 'yard', '~> 0.9' + spec.add_development_dependency 'launchy', '~> 2.4.3' + spec.add_development_dependency 'json', '>= 2.1.0' end diff --git a/lib/lamian.rb b/lib/lamian.rb index 60b4cbf..406701d 100644 --- a/lib/lamian.rb +++ b/lib/lamian.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true +# Lamian is an in-memory logger, which content could be released +# for error messages. +# It is designed to work in pair with `exception_notification` gem inside +# rails aplications module Lamian autoload :VERSION, 'lamian/version' autoload :Config, 'lamian/config' @@ -10,6 +14,14 @@ module Lamian require 'lamian/engine' class << self + # Yields curent configuration if block given + # @example + # Lamian.configure do |config| + # config.formatter = MyFormatter.new + # end + # @yield [config] current configuration + # @yieldparam config [Lamian::Config] + # @return [Lamian::Config] current configuration def configure @config ||= Config.new yield(@config) if block_given? @@ -17,18 +29,30 @@ def configure end alias config configure + # Extends logger instance to tee it's output to Lamian logger + # @param other_logger [Logger] logger to extend def extend_logger(other_logger) other_logger.extend(Lamian::LoggerExtension) end + # @api private + # Gives access to current logger + # @return [Lamian::Logger] current logger def logger Lamian::Logger.current end + # Collects logs sent inside block def run logger.run { yield } end + # Dumps log collected in this run + # @option format [Symbol] + # requested format of log. At this point, returns raw log if falsey + # or log without controll sequences (such as '[23m') if truthy + # value given (for now) + # @return formatted log (String by default) def dump(format: nil) logger.dump(format: format) end diff --git a/lib/lamian/config.rb b/lib/lamian/config.rb index 6a8d123..ba03874 100644 --- a/lib/lamian/config.rb +++ b/lib/lamian/config.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true + require 'logger' module Lamian + # General lamian configuration class + # @attr formatter [Logger::Foramtter] + # formatter to use in lamian, global Config = Struct.new(:formatter) do def initialize self.formatter = ::Logger::Formatter.new diff --git a/lib/lamian/engine.rb b/lib/lamian/engine.rb index 60611fd..ad7a8f0 100644 --- a/lib/lamian/engine.rb +++ b/lib/lamian/engine.rb @@ -1,9 +1,14 @@ # frozen_string_literal: true + require 'rails' require 'exception_notification' require 'exception_notification/rails' module Lamian + # Rails engine, which injects middleware and appends + # lamian views to rails library. + # Lamian views are used in exception_notifier to provide + # request_log section class Engine < ::Rails::Engine config.app_middleware.insert_before( ExceptionNotification::Rack, diff --git a/lib/lamian/logger.rb b/lib/lamian/logger.rb index f2908cd..c8aab6e 100644 --- a/lib/lamian/logger.rb +++ b/lib/lamian/logger.rb @@ -3,7 +3,19 @@ require 'logger' module Lamian + # @api private + # Provides thread-local loggers to catch teed messages from + # regular loggers. + # Uses :__lamian_logger thread variable + # @attr level [Int] + # current log level, implicitly set to zero + # @attr logdevs [Array(StringIO)] + # stack of log devices used to store logs + # @attr formatter [Logger::Formatter] + # formatter, inherited from Lamian.config class Logger < ::Logger + # Provides access to logger bound to curent thread + # @return [Lamian::Logger] current logger def self.current Thread.current[:__lamian_logger] ||= new end @@ -14,6 +26,8 @@ def initialize self.formatter = Lamian.config.formatter end + # @see Lamian.run + # Collects logs sent inside block def run push_logdev(StringIO.new) yield @@ -21,10 +35,19 @@ def run pop_logdev end + # Part of Logger api, entry point for all logs + # extened to run on each log device in stack def add(*args, &block) each_logdev { super(*args, &block) } end + # @see Lamian.dump + # Dumps log collected in this run + # @option format [Symbol] + # requested format of log. At this point, returns raw log if falsey + # or log without controll sequences (such as '[23m') if truthy + # value given (for now) + # @return formatted log (String by default) def dump(format: nil) result = logdevs[-1]&.string&.dup apply_format!(format, result) @@ -35,20 +58,34 @@ def dump(format: nil) attr_accessor :level, :logdevs, :formatter - def apply_format!(format, result) + # Formats string using given format + # @todo create `formatters` interface to allow real format selection + # @note + # `format` is now checked only for thruthyness. Please, use + # `:text` format to keep it same after `formatters` interface integration + # @param format [Symbol] requested format, e.g. `:text` + # @param string [String] string to be changed + # @return avoid return value usage + def apply_format!(format, string) return unless format - return unless result - result.gsub!(/\[\d{1,2}m/, '') + return unless string + string.gsub!(/\[\d{1,2}m/, '') end + # Pushes new logdev in the start of #run call + # @param logdev [StringIO] new StringIO def push_logdev(logdev) logdevs << logdev end + # Pops logdev in the end of #run call + # @return [StringIO] logdev def pop_logdev logdevs.pop end + # Runs specific block with all logdevs in stack to + # populate them all def each_logdev logdevs.each do |logdev| @logdev = logdev diff --git a/lib/lamian/logger_extension.rb b/lib/lamian/logger_extension.rb index 40ecda2..845b6b2 100644 --- a/lib/lamian/logger_extension.rb +++ b/lib/lamian/logger_extension.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true + module Lamian + # Provides extension to loggers, which should be teed to lamian module LoggerExtension + # Extension to Logger#add method, which tees info into lamian def add(*args, &block) Logger.current.add(*args, &block) super diff --git a/lib/lamian/middleware.rb b/lib/lamian/middleware.rb index 253d6cc..8cb8fa2 100644 --- a/lib/lamian/middleware.rb +++ b/lib/lamian/middleware.rb @@ -1,10 +1,17 @@ # frozen_string_literal: true + module Lamian + # Provides rack middleware, which allows to colelct request logs + # @attr app [Proc] stored application class Middleware + # @param app [Proc] stored application def initialize(app) self.app = app end + # wraps application inside lamian context and calls it + # @param env [Hash] left intact + # @return result of app.call def call(env) Lamian.run { app.call(env) } end diff --git a/lib/lamian/version.rb b/lib/lamian/version.rb index 5195219..61f1cba 100644 --- a/lib/lamian/version.rb +++ b/lib/lamian/version.rb @@ -1,4 +1,17 @@ # frozen_string_literal: true + module Lamian - VERSION = '0.3.3' + # Current lamian vewrsion + # + # format: 'a.b.c' with possible suffixes such as alpha + # * a is for major version, it is guaranteed to be changed + # if back-compatibility of public API is broken + # * b is for minor version, it is guaranteed to be changed + # on public API changes and also if private API + # back-compatibility is broken + # * c is for incremental version, it is updated in other cases + # According to this, it is enought to specify '~> a.b' + # if private API was not used and to specify '~> a.b.c' if it was + + VERSION = '1.0.0alpha' end diff --git a/spec/lamian/logger_spec.rb b/spec/lamian/logger_spec.rb index b162613..88b3280 100644 --- a/spec/lamian/logger_spec.rb +++ b/spec/lamian/logger_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + describe Lamian::Logger do describe '#dump', :cool_loggers do specify 'with #run' do diff --git a/spec/lamian/middleware_spec.rb b/spec/lamian/middleware_spec.rb index f380f75..1e36225 100644 --- a/spec/lamian/middleware_spec.rb +++ b/spec/lamian/middleware_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + describe Lamian::Middleware do let(:app) { double(:app) } let(:env) { double(:env) } diff --git a/spec/lamian_spec.rb b/spec/lamian_spec.rb index 4a508f1..3682918 100644 --- a/spec/lamian_spec.rb +++ b/spec/lamian_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + describe Lamian do it 'has a version number' do expect(Lamian::VERSION).not_to be nil diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b8bdfa7..fc8b83d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'bundler/setup' require 'logger' require 'pry'