specifier 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a4098b51fe3a0a29ab860be282db1a5a8d2007b
4
- data.tar.gz: 46e4a79e654b9c3fa4ea7dd16dd0f0e8156e11ac
3
+ metadata.gz: fcc4798a1f25e09eca4bafa1fcd12d394252cf41
4
+ data.tar.gz: 760454e29ac6570d2656d668373d526b3db51bfa
5
5
  SHA512:
6
- metadata.gz: a481e8da92542e1274dd295e327945fe0286852a66eae950e3ea99ec6796ea4c60ff6bb207b698a0decf7886dbb6daeaa19cc59cab1026310c59020d101e6ddc
7
- data.tar.gz: 075ca1cda8870fd238b161705077f82073d254170a12b36d99fa03a73e16354ee99ef9ccd2581dfcdd22c3cbc787068eb463bf25dd47d8586dedfc255906d3a2
6
+ metadata.gz: 944625dab26f7964579a42490722e1a1c02729172211feb3f5fbe6388925e99084faff054046b0a89848eb5b91f0e13fd5eee7f80713c871b9112b8e7b2e617e
7
+ data.tar.gz: 4d0810069845ecc67a544461c6518f06cff5d33ef86db94fa4359ee3c0d703d75374a5af33ac9b263c83fda2aca4f91659e376c24835392ba149603c13418a47
data/README.md CHANGED
@@ -1,10 +1,16 @@
1
1
  # Specifier
2
2
 
3
+ ## Installation
4
+
5
+ ```bash
6
+ gem install specifier
7
+ ```
8
+
3
9
  ## Example
4
10
 
5
11
  ```ruby
6
12
  class Echo
7
- def self.say(message)
13
+ def say(message)
8
14
  message
9
15
  end
10
16
  end
@@ -12,13 +18,11 @@ end
12
18
 
13
19
  ```ruby
14
20
  Specifier.specify Echo do
15
- describe '.say' do
16
- it 'says "Hello" if you say "Hello"' do
17
- expect(Echo.say('Hello')).to equal('Hello')
18
- end
21
+ let(:echo) { Echo.new }
19
22
 
20
- it 'says "Goodbye" if you say "Goodbye"' do
21
- expect(Echo.say('Goodbye')).to equal('Goodbye')
23
+ describe '#say' do
24
+ it 'says "Hello" if you say "Hello"' do
25
+ expect(echo.say('Hello')).to equal('Hello')
22
26
  end
23
27
  end
24
28
  end
@@ -29,3 +33,14 @@ end
29
33
  ```bash
30
34
  bundle exec specifier ./specs
31
35
  ```
36
+
37
+ ## Status
38
+
39
+ [![travis](https://img.shields.io/travis/ksylvest/specifier.svg)](https://travis-ci.org/ksylvest/specifier)
40
+ [![gemnasium](https://img.shields.io/gemnasium/ksylvest/specifier.svg)](https://gemnasium.com/ksylvest/specifier)
41
+ [![coveralls](https://img.shields.io/coveralls/ksylvest/specifier.svg)](https://coveralls.io/r/ksylvest/specifier)
42
+ [![codeclimate](https://img.shields.io/codeclimate/github/ksylvest/specifier.svg)](https://codeclimate.com/github/ksylvest/specifier)
43
+
44
+ ## Copyright
45
+
46
+ Copyright (c) 2016 - 2017 Kevin Sylvestre. See LICENSE for details.
@@ -3,21 +3,29 @@ require 'slop'
3
3
 
4
4
  require 'specifier/version'
5
5
  require 'specifier/cli'
6
+ require 'specifier/colorizer'
6
7
  require 'specifier/config'
7
8
  require 'specifier/logger'
8
9
  require 'specifier/runner'
9
10
  require 'specifier/context'
11
+ require 'specifier/definition'
10
12
  require 'specifier/example'
11
13
  require 'specifier/expectation'
12
- require 'specifier/memoizer'
13
14
  require 'specifier/matcher'
15
+ require 'specifier/memoizer'
14
16
  require 'specifier/formatter'
15
17
 
16
18
  module Specifier
19
+ def self.contexts
20
+ @contexts ||= []
21
+ end
17
22
 
18
23
  def self.specify(scope, &block)
19
- context = Context.new(scope, &block)
20
- context.run
24
+ contexts << Context.setup(scope, &block)
25
+ end
26
+
27
+ def self.run
28
+ contexts.each(&:run)
21
29
  end
22
30
 
23
31
  def self.config
@@ -29,7 +37,7 @@ module Specifier
29
37
  end
30
38
 
31
39
  def self.formatter
32
- @formatter ||= Formatter::Progress.new(logger)
40
+ @formatter ||= Formatter.formatters[config.formatter || Specifier::Formatter::DEFAULT].new(logger)
33
41
  end
34
42
 
35
43
  end
@@ -1,4 +1,12 @@
1
1
  module Specifier
2
+
3
+ # Used when interacting with the suite from the command line interface (CLI).
4
+ #
5
+ # Usage:
6
+ #
7
+ # cli = Specifier::CLI.new
8
+ # cli.parse
9
+ #
2
10
  class CLI
3
11
  BANNER = 'usage: specifier [options] [./specs]'.freeze
4
12
 
@@ -13,6 +21,8 @@ module Specifier
13
21
  options.on '-v', '--version', 'version' do
14
22
  return version
15
23
  end
24
+
25
+ options.string '-f', '--formatter', 'formatter', default: Specifier::Formatter::DEFAULT
16
26
  end
17
27
 
18
28
  run(config)
@@ -29,6 +39,8 @@ module Specifier
29
39
  end
30
40
 
31
41
  def run(options)
42
+ Specifier.config.formatter = options[:formatter]
43
+
32
44
  paths = Set.new
33
45
  options.arguments.each do |argument|
34
46
  Find.find(argument) do |path|
@@ -0,0 +1,26 @@
1
+ module Specifier
2
+
3
+ # Used to colorize messages for console output.
4
+ #
5
+ # Usage:
6
+ #
7
+ # Specifier::Colorizer.muted("...")
8
+ # Specifier::Colorizer.passed("...")
9
+ # Specifier::Colorizer.failed("...")
10
+ #
11
+ module Colorizer
12
+
13
+ def self.muted(message)
14
+ "\e[37m#{message}\e[0m"
15
+ end
16
+
17
+ def self.failed(message)
18
+ "\e[31m#{message}\e[0m"
19
+ end
20
+
21
+ def self.passed(message)
22
+ "\e[32m#{message}\e[0m"
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module Specifier
2
+
3
+ # Configuration options used internally such as the formatter. Can be accessed via the global.
4
+ #
5
+ # Usage:
6
+ #
7
+ # Specifier.config
8
+ #
9
+ class Config
10
+ attr_accessor :formatter
11
+ end
12
+
13
+ end
@@ -1,29 +1,72 @@
1
- require 'byebug'
2
-
3
1
  module Specifier
2
+
3
+ # Defines a context that can be deeply nested (evaluates from oldest to newest in ancestry).
4
+ #
5
+ # Usage:
6
+ #
7
+ # let "..." do
8
+ # # ...
9
+ # end
10
+ #
11
+ # it "..." do
12
+ # # ...
13
+ # end
14
+ #
15
+ # describe "..." do
16
+ # end
17
+ #
4
18
  class Context
5
19
  attr_accessor :parent
20
+ attr_accessor :children
21
+ attr_accessor :description
6
22
 
7
- def initialize(scope, &block)
8
- @_scope = scope
9
- @_block = block
10
- @_examples = Set.new
23
+ def self.setup(description, parent = nil, &block)
24
+ context = Context.new(description, &block)
25
+ context.parent = parent
26
+ parent.children << context if parent
27
+ context.instance_eval(&block)
28
+ context
11
29
  end
12
30
 
13
- def describe(scope, &block)
14
- context = Context.new(scope, &block)
15
- context.parent = self
16
- context.run
31
+ def initialize(description, &block)
32
+ @description = description
33
+ @block = block
34
+ @children = []
35
+ @examples = []
36
+ @definitions = []
17
37
  end
18
38
 
19
- def it(descriptor, &block)
20
- @_examples << Example.new(descriptor, &block)
39
+ def describe(description, &block)
40
+ self.class.setup(description, self, &block)
41
+ end
42
+
43
+ def let(name, &block)
44
+ definition = Definition.new(name, &block)
45
+ @definitions << definition
46
+ definition
47
+ end
48
+
49
+ def it(description, &block)
50
+ example = Example.new(description, &block)
51
+ @examples << example
52
+ example
21
53
  end
22
54
 
23
55
  def run
24
- instance_eval(&@_block)
25
- @_examples.each do |example|
26
- Specifier.formatter.record(example, example.run)
56
+ Specifier.formatter.context(self) do
57
+ @examples.each do |example|
58
+ setup(example)
59
+ result = example.run
60
+ Specifier.formatter.record(example, result)
61
+ end
62
+ @children.each(&:run)
63
+ end
64
+ end
65
+
66
+ def setup(example)
67
+ parent&.setup(example)
68
+ @definitions.each do |definition|
69
+ definition.define(example)
27
70
  end
28
71
  end
29
72
 
@@ -0,0 +1,28 @@
1
+ module Specifier
2
+
3
+ # Configures a definition (used for let statements).
4
+ #
5
+ # Usage:
6
+ #
7
+ # definition = Specifier::Definition.new("...") do
8
+ # # ...
9
+ # end
10
+ #
11
+ # definition.define(object)
12
+ #
13
+ class Definition
14
+
15
+ def initialize(name, &block)
16
+ @name = name
17
+ @memoizer = Memoizer.new(&block)
18
+ end
19
+
20
+ def define(object)
21
+ memoizer = @memoizer
22
+ object.define_singleton_method(@name) do
23
+ memoizer.evaluate
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -1,17 +1,24 @@
1
1
  module Specifier
2
+
3
+ # Configures an example (used for it statements).
4
+ #
5
+ # Usage:
6
+ #
7
+ # example = Specifier::Example.new("...") do
8
+ # expect(value).to equal(value)
9
+ # end
10
+ #
11
+ # example.run
12
+ #
2
13
  class Example
3
14
  Result = Struct.new(:status, :message)
4
15
 
5
- def initialize(descriptor, &block)
6
- @_descriptor = descriptor
7
- @_block = block
8
- end
16
+ attr_accessor :description
9
17
 
10
- # def let(name, &block)
11
- # define_method(name) do
12
- # @_memoizer.resolve(name, block)
13
- # end
14
- # end
18
+ def initialize(description, &block)
19
+ @description = description
20
+ @block = block
21
+ end
15
22
 
16
23
  def expect(value)
17
24
  Expectation.new(value)
@@ -22,7 +29,7 @@ module Specifier
22
29
  end
23
30
 
24
31
  def run
25
- instance_eval(&@_block)
32
+ instance_eval(&@block)
26
33
  return Result.new(:pass)
27
34
  rescue Specifier::Expectation::Miss => miss
28
35
  return Result.new(:fail, miss.message)
@@ -1,4 +1,12 @@
1
1
  module Specifier
2
+
3
+ # Configures an expectation (used for expect statements).
4
+ #
5
+ # Usage:
6
+ #
7
+ # expectation = Specifier::Expectation.new("today")
8
+ # expectation.to(matcher) # 'raises 'Miss'
9
+ #
2
10
  class Expectation
3
11
  class Miss < StandardError
4
12
  def initialize(message)
@@ -1,2 +1,17 @@
1
+ module Specifier
2
+ module Formatter
3
+ def self.formatters
4
+ @formatters ||= {}
5
+ end
6
+ end
7
+ end
8
+
1
9
  require 'specifier/formatter/base'
10
+ require 'specifier/formatter/documentation'
2
11
  require 'specifier/formatter/progress'
12
+
13
+ module Specifier
14
+ module Formatter
15
+ DEFAULT = Progress::NAME
16
+ end
17
+ end
@@ -1,5 +1,14 @@
1
1
  module Specifier
2
2
  module Formatter
3
+
4
+ # A base defintion for formatting the specifier results.
5
+ #
6
+ # Usage:
7
+ #
8
+ # formatter = Specifier::Formatter::Base.new
9
+ # formatter.record(example, result)
10
+ # formatter.summarize
11
+ #
3
12
  class Base
4
13
  Recording = Struct.new(:example, :result) do
5
14
  def status
@@ -18,12 +27,25 @@ module Specifier
18
27
  def initialize(logger)
19
28
  @logger = logger
20
29
  @recordings = []
30
+ @contexts = []
21
31
  end
22
32
 
23
33
  def record(example, result)
24
34
  @recordings << Recording.new(example, result)
25
35
  end
26
36
 
37
+ def context(context)
38
+ @contexts << context
39
+ yield
40
+ end
41
+
42
+ def summarize
43
+ @logger.log
44
+ @logger.log(summary)
45
+ end
46
+
47
+ protected
48
+
27
49
  def passed
28
50
  @recordings.select(&:pass?)
29
51
  end
@@ -32,18 +54,14 @@ module Specifier
32
54
  @recordings.select(&:fail?)
33
55
  end
34
56
 
35
- def summarize
36
- @logger.log
37
- @logger.log(summary)
38
- end
39
-
40
57
  def summary
41
58
  <<~SUMMARY
42
- Total: #{@recordings.count}
43
- Passed: #{passed.count}
44
- Failed: #{failed.count}
59
+ Total: #{Colorizer.muted(@recordings.count)}
60
+ Passed: #{Colorizer.passed(passed.count)}
61
+ Failed: #{Colorizer.failed(failed.count)}
45
62
  SUMMARY
46
63
  end
47
64
  end
65
+
48
66
  end
49
67
  end
@@ -0,0 +1,54 @@
1
+ module Specifier
2
+ module Formatter
3
+
4
+ # A custom defintion for formatting the specifier results.
5
+ #
6
+ # Usage:
7
+ #
8
+ # formatter = Specifier::Formatter::Documentation.new
9
+ # formatter.context(context) do
10
+ # formatter.record(example, result)
11
+ # end
12
+ # formatter.summarize
13
+ #
14
+ class Documentation < Base
15
+ INDENTATION = ' '.freeze
16
+ NAME = 'documentation'.freeze
17
+
18
+ Formatter.formatters[NAME] = self
19
+
20
+ def initialize(logger)
21
+ super
22
+ @indentation = 0
23
+ end
24
+
25
+ def record(example, result)
26
+ super
27
+
28
+ message =
29
+ case result.status
30
+ when :pass then Colorizer.passed(indent(example.description))
31
+ when :fail then Colorizer.failed(indent(example.description))
32
+ end
33
+
34
+ @logger.log(message)
35
+ end
36
+
37
+ def context(context)
38
+ @logger.log(indent(context.description))
39
+
40
+ @indentation = @indentation.next
41
+ super
42
+ @indentation = @indentation.pred
43
+ end
44
+
45
+ private
46
+
47
+ def indent(message)
48
+ INDENTATION * @indentation + String(message)
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -1,20 +1,36 @@
1
1
  module Specifier
2
2
  module Formatter
3
+
4
+ # A custom defintion for formatting the specifier results.
5
+ #
6
+ # Usage:
7
+ #
8
+ # formatter = Specifier::Formatter::Progress.new
9
+ # formatter.context(context) do
10
+ # formatter.record(example, result)
11
+ # end
12
+ # formatter.summarize
13
+ #
3
14
  class Progress < Base
15
+ NAME = 'progress'.freeze
16
+ PASS = '+'.freeze
17
+ FAIL = '-'.freeze
18
+
19
+ Formatter.formatters[NAME] = self
20
+
4
21
  def record(example, result)
5
22
  super
6
- @logger << symbol(result)
7
- end
8
23
 
9
- private
24
+ message =
25
+ case result.status
26
+ when :pass then Specifier::Colorizer.passed(PASS)
27
+ when :fail then Specifier::Colorizer.failed(FAIL)
28
+ end
10
29
 
11
- def symbol(result)
12
- case result.status
13
- when :pass then '+'
14
- when :fail then '-'
15
- end
30
+ @logger << message
16
31
  end
17
32
 
18
33
  end
34
+
19
35
  end
20
36
  end
@@ -7,6 +7,18 @@ module Specifier
7
7
  # Specifier.logger.log("...")
8
8
  #
9
9
  class Logger
10
+ module Color
11
+ BLACK = 0
12
+ RED = 1
13
+ GREEN = 2
14
+ YELLOW = 3
15
+ end
16
+
17
+ module Formatting
18
+ DEFAULT = 0
19
+ BOLD = 1
20
+ ITALIC = 3
21
+ end
10
22
 
11
23
  def initialize(stream = STDOUT)
12
24
  @stream = stream
@@ -1,11 +1,24 @@
1
1
  module Specifier
2
+
3
+ # It remembers things (used for within let statements).
4
+ #
5
+ # Usage:
6
+ #
7
+ # memoizer = Specifier::Memoizer.new do
8
+ # # ...
9
+ # end
10
+ # memoizer.evaluate
11
+ #
2
12
  class Memoizer
3
- def initialize
4
- @resolutions = {}
13
+
14
+ def initialize(&block)
15
+ @block = block
5
16
  end
6
17
 
7
- def resolve(name)
8
- @resolutions[name] ||= yield
18
+ def evaluate
19
+ return @result if defined?(@result)
20
+ @result = @block.call
9
21
  end
22
+
10
23
  end
11
24
  end
@@ -1,4 +1,12 @@
1
1
  module Specifier
2
+
3
+ # Handles load and execution of specs.
4
+ #
5
+ # Usage:
6
+ #
7
+ # runner = Specifier::Runner.new(paths: "./spec")
8
+ # runner.run
9
+ #
2
10
  class Runner
3
11
  def initialize(paths:)
4
12
  @paths = paths
@@ -8,6 +16,8 @@ module Specifier
8
16
  @paths.each do |path|
9
17
  load path
10
18
  end
19
+
20
+ Specifier.run
11
21
  Specifier.formatter.summarize
12
22
  end
13
23
  end
@@ -1,3 +1,3 @@
1
1
  module Specifier
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-29 00:00:00.000000000 Z
11
+ date: 2017-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slop
@@ -108,12 +108,15 @@ files:
108
108
  - bin/specifier
109
109
  - lib/specifier.rb
110
110
  - lib/specifier/cli.rb
111
+ - lib/specifier/colorizer.rb
111
112
  - lib/specifier/config.rb
112
113
  - lib/specifier/context.rb
114
+ - lib/specifier/definition.rb
113
115
  - lib/specifier/example.rb
114
116
  - lib/specifier/expectation.rb
115
117
  - lib/specifier/formatter.rb
116
118
  - lib/specifier/formatter/base.rb
119
+ - lib/specifier/formatter/documentation.rb
117
120
  - lib/specifier/formatter/progress.rb
118
121
  - lib/specifier/logger.rb
119
122
  - lib/specifier/matcher.rb