blood_contracts 0.2.1 → 1.0.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.
@@ -1,59 +0,0 @@
1
- require 'nanoid'
2
-
3
- module BloodContracts
4
- module Storages
5
- class BaseBackend
6
- extend Dry::Initializer
7
- extend Forwardable
8
-
9
- param :storage
10
- param :example_name
11
- option :name, default: -> do
12
- BloodContracts.run_name || ::Nanoid.generate(size: 10)
13
- end
14
- def_delegators :@storage, :input_writer, :output_writer,
15
- :input_serializer, :output_serializer, :meta_serializer
16
-
17
-
18
- def sample_exists?(sample_name)
19
- raise NotImplementedError
20
- end
21
-
22
- def find_all_samples(run, tag, sample)
23
- raise NotImplementedError
24
- end
25
-
26
- def load_sample(_sample_name)
27
- %i(input output meta).map do |type|
28
- load_sample_chunk(type, _sample_name)
29
- end
30
- end
31
-
32
- def load_sample_chunk(_dump_type, _sample_name)
33
- raise NotImplementedError
34
- end
35
-
36
- def describe_sample(_tag, _options, _context)
37
- raise NotImplementedError
38
- end
39
-
40
- def serialize_sample(tag, options, context)
41
- %i(input output meta).each do |type|
42
- serialize_sample_chunk(type, tag, options, context)
43
- end
44
- end
45
-
46
- def serialize_sample_chunk(_type, _tag, _option, _context)
47
- raise NotImplementedError
48
- end
49
-
50
- def suggestion
51
- raise NotImplementedError
52
- end
53
-
54
- def unexpected_suggestion
55
- raise NotImplementedError
56
- end
57
- end
58
- end
59
- end
@@ -1,108 +0,0 @@
1
- module BloodContracts
2
- module Storages
3
- class FileBackend < BaseBackend
4
- option :root, default: -> { Rails.root.join(path) }
5
-
6
- def suggestion
7
- "#{path}/*/*"
8
- end
9
-
10
- def unexpected_suggestion
11
- "#{path}/#{Storage::UNDEFINED_RULE}/*"
12
- end
13
-
14
- def default_path
15
- "./tmp/contract_tests/"
16
- end
17
-
18
- def timestamp
19
- @timestamp ||= Time.current.to_s(:usec)[8..-3]
20
- end
21
-
22
- def reset_timestamp!
23
- @timestamp = nil
24
- end
25
-
26
- def parse_sample_name(sample_name)
27
- path_items = sample_name.split("/")
28
- sample = path_items.pop
29
- tag = path_items.pop
30
- path_str = path_items.join("/")
31
- run_n_example_str = path_str.sub(default_path, '')
32
- if run_n_example_str.end_with?('*')
33
- [
34
- run_n_example_str.chomp("*"),
35
- tag,
36
- sample
37
- ]
38
- elsif run_n_example_str.end_with?(example_name)
39
- [
40
- run_n_example_str.chomp(example_name),
41
- tag,
42
- sample
43
- ]
44
- else
45
- %w(__no_match__ __no_match__ __no_match__)
46
- end
47
- end
48
-
49
- def find_all_samples(sample_name)
50
- run, tag, sample = parse_sample_name(sample_name)
51
- run_path = path(run_name: run)
52
- files = Dir.glob("#{run_path}/#{tag.to_s}/#{sample}*")
53
- files.select { |f| f.end_with?(".output") }
54
- .map { |f| f.chomp(".output") }
55
- end
56
-
57
- def path(run_name: name)
58
- File.join(default_path, run_name, example_name.to_s)
59
- end
60
-
61
- def sample_name(tag, run_path: root, sample: timestamp)
62
- File.join(run_path, tag.to_s, sample)
63
- end
64
-
65
- def sample_exists?(sample_name)
66
- run, tag, sample = parse_sample_name(sample_name)
67
- name = sample_name(tag, run_path: path(run_name: run), sample: sample)
68
- File.exist?("#{name}.input")
69
- end
70
-
71
- def load_sample_chunk(dump_type, sample_name)
72
- run, tag, sample = parse_sample_name(sample_name)
73
- name = sample_name(tag, run_path: path(run_name: run), sample: sample)
74
- send("#{dump_type}_serializer")[:load].call(
75
- File.read("#{name}.#{dump_type}.dump")
76
- )
77
- end
78
-
79
- def write(writer, cntxt, options)
80
- writer = cntxt.method(writer) if cntxt && writer.respond_to?(:to_sym)
81
- writer.call(options).encode(
82
- "UTF-8", invalid: :replace, undef: :replace, replace: "?",
83
- )
84
- end
85
-
86
- def describe_sample(tag, options, context)
87
- FileUtils.mkdir_p File.join(root, tag.to_s)
88
-
89
- reset_timestamp!
90
- name = sample_name(tag)
91
- File.open("#{name}.input", "w+") do |f|
92
- f << write(input_writer, context, options)
93
- end
94
- File.open("#{name}.output", "w+") do |f|
95
- f << write(output_writer, context, options)
96
- end
97
- end
98
-
99
- def serialize_sample_chunk(type, tag, options, context)
100
- return unless (dump_proc = send("#{type}_serializer")[:dump])
101
- name, data = sample_name(tag), options.send(type)
102
- File.open("#{name}.#{type}.dump", "w+") do |f|
103
- f << write(dump_proc, context, data)
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,38 +0,0 @@
1
- module BloodContracts
2
- module Storages
3
- class Serializer
4
- extend Dry::Initializer
5
-
6
- param :serializer
7
-
8
- def self.call(*args)
9
- new(*args).call
10
- end
11
-
12
- def call
13
- return object_serializer_to_hash if object_serializer?
14
- return serializer.to_hash if hash_serializer?
15
-
16
- raise "Both #dump and #load methods should be defined for serialization"
17
- end
18
-
19
- private
20
-
21
- def object_serializer?
22
- serializer.respond_to?(:dump) && serializer.respond_to?(:load)
23
- end
24
-
25
- def object_serializer_to_hash
26
- {
27
- load: serializer.method(:load),
28
- dump: serializer.method(:dump),
29
- }
30
- end
31
-
32
- def hash_serializer?
33
- return unless serializer.respond_to?(:to_hash)
34
- (%i[dump load] - serializer.to_hash.keys).empty?
35
- end
36
- end
37
- end
38
- end
@@ -1,41 +0,0 @@
1
- module BloodContracts
2
- class Suite
3
- extend Dry::Initializer
4
-
5
- option :contract, ->(v) { Hashie::Mash.new(v) }
6
- option :data_generator, optional: true
7
-
8
- option :input_writer, optional: true
9
- option :output_writer, optional: true
10
-
11
- option :input_serializer, optional: true
12
- option :output_serializer, optional: true
13
- option :meta_serializer, optional: true
14
-
15
- option :storage_backend, optional: true
16
- option :storage, default: -> { default_storage }
17
-
18
- def data_generator=(generator)
19
- raise ArgumentError unless generator.respond_to?(:call)
20
- @data_generator = generator
21
- end
22
-
23
- def input_writer=(writer)
24
- storage.input_writer = writer
25
- end
26
-
27
- def output_writer=(writer)
28
- storage.output_writer = writer
29
- end
30
-
31
- def default_storage
32
- Storage.new(
33
- input_writer: input_writer,
34
- output_writer: output_writer,
35
- input_serializer: input_serializer,
36
- output_serializer: output_serializer,
37
- meta_serializer: meta_serializer,
38
- )
39
- end
40
- end
41
- end
@@ -1,3 +0,0 @@
1
- module BloodContracts
2
- VERSION = "0.2.1".freeze
3
- end
@@ -1,31 +0,0 @@
1
- class String
2
- # See gem Facets String#pathize
3
-
4
- # Transforms a namespace, i.e. a class or module name, into a viable
5
- # file path.
6
- #
7
- # "ExamplePathize".pathize #=> "example_pathize"
8
- # "ExamplePathize::Example".pathize #=> "example_pathize/example"
9
- #
10
- # Compare this method to {String#modulize) and {String#methodize).
11
- #
12
- def pathize
13
- gsub(/([A-Z]+)([A-Z])/,'\1_\2').
14
- gsub(/([a-z])([A-Z])/,'\1_\2').
15
- gsub('__','/').
16
- gsub('::','/').
17
- gsub(/\s+/, ''). # spaces are bad form
18
- gsub(/[?%*:|"<>.]+/, ''). # reserved characters
19
- downcase
20
- end
21
-
22
- # Compare to Rails definition:
23
- #
24
- # gsub(/__/, '/').
25
- # gsub(/::/, '/').
26
- # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
27
- # gsub(/([a-z\d])([A-Z])/,'\1_\2').
28
- # tr("-", "_").
29
- # downcase
30
- #
31
- end
@@ -1,106 +0,0 @@
1
- module RSpec
2
- module MeetContractMatcher
3
- extend RSpec::Matchers::DSL
4
- Runner = ::BloodContracts::Runner
5
- Debugger = ::BloodContracts::Debugger
6
-
7
- matcher :meet_contract_rules do |args|
8
- match do |subject|
9
- runner = ENV["debug"] ? Debugger : Runner
10
-
11
- @_contract_runner = runner.new(
12
- subject,
13
- context: self,
14
- suite: build_suite(args || subject),
15
- iterations: @_iterations,
16
- time_to_run: @_time_to_run,
17
- stop_on_unexpected: @_halt_on_unexpected,
18
- )
19
- @_contract_runner.call
20
- end
21
-
22
- def build_suite(args)
23
- suite = nil
24
- if args.respond_to?(:to_contract_suite)
25
- suite = args.to_contract_suite(name: _example_name_to_path)
26
- elsif args.respond_to?(:to_h) && args.to_h.fetch(:contract) { false }
27
- ::BloodContracts::Suite.new(
28
- storage: new_storage,
29
- contract: args[:contract]
30
- )
31
- else
32
- raise "Matcher arguments is not a Blood Contract"
33
- end
34
- suite.data_generator = @_generator if @_generator
35
- suite
36
- end
37
-
38
- def new_storage
39
- storage = Storage.new(contract_name: _example_name_to_path)
40
- storage.input_writer = _input_writer if _input_writer
41
- storage.output_writer = _output_writer if _output_writer
42
- if @_input_serializer
43
- storage.input_serializer = @_input_serializer
44
- end
45
- if @_output_serializer
46
- storage.output_serializer = @_output_serializer
47
- end
48
- storage
49
- end
50
-
51
- def _example_name_to_path
52
- method_missing(:class)
53
- .name
54
- .sub("RSpec::ExampleGroups::", "")
55
- .pathize
56
- end
57
-
58
- def _input_writer
59
- input_writer = @_writers.to_h[:input]
60
- input_writer ||= :input_writer if defined? self.input_writer
61
- input_writer
62
- end
63
-
64
- def _output_writer
65
- output_writer = @_writers.to_h[:output]
66
- output_writer ||= :output_writer if defined? self.output_writer
67
- output_writer
68
- end
69
-
70
- supports_block_expectations
71
-
72
- failure_message { @_contract_runner.failure_message }
73
-
74
- description { @_contract_runner.description }
75
-
76
- chain :using_generator do |generator|
77
- if generator.respond_to?(:to_sym)
78
- @_generator = method(generator.to_sym)
79
- else
80
- raise ArgumentError unless generator.respond_to?(:call)
81
- @_generator = generator
82
- end
83
- end
84
-
85
- chain :during_n_iterations_run do |iterations|
86
- @_iterations = Integer(iterations)
87
- end
88
-
89
- chain :during_n_seconds_run do |time_to_run|
90
- @_time_to_run = Float(time_to_run)
91
- end
92
-
93
- chain :halt_on_unexpected do
94
- @_halt_on_unexpected = true
95
- end
96
-
97
- chain :serialize_input do |serializer|
98
- @_input_serializer = serializer
99
- end
100
-
101
- chain :serialize_output do |serializer|
102
- @_output_serializer = serializer
103
- end
104
- end
105
- end
106
- end