specifier 0.1.0 → 0.2.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: 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