fixturama 0.1.0 → 0.5.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -0
  3. data/README.md +76 -4
  4. data/lib/fixturama.rb +48 -20
  5. data/lib/fixturama/changes.rb +72 -0
  6. data/lib/fixturama/changes/base.rb +28 -0
  7. data/lib/fixturama/changes/chain.rb +64 -0
  8. data/lib/fixturama/changes/chain/actions.rb +31 -0
  9. data/lib/fixturama/changes/chain/arguments.rb +59 -0
  10. data/lib/fixturama/changes/chain/raise_action.rb +43 -0
  11. data/lib/fixturama/changes/chain/return_action.rb +33 -0
  12. data/lib/fixturama/changes/const.rb +30 -0
  13. data/lib/fixturama/changes/env.rb +35 -0
  14. data/lib/fixturama/changes/request.rb +91 -0
  15. data/lib/fixturama/changes/request/response.rb +62 -0
  16. data/lib/fixturama/changes/request/responses.rb +22 -0
  17. data/lib/fixturama/changes/seed.rb +39 -0
  18. data/lib/fixturama/config.rb +5 -0
  19. data/lib/fixturama/fixture_error.rb +31 -0
  20. data/lib/fixturama/loader.rb +6 -2
  21. data/lib/fixturama/loader/context.rb +14 -5
  22. data/lib/fixturama/loader/value.rb +1 -0
  23. data/lib/fixturama/version.rb +4 -0
  24. metadata +85 -73
  25. data/.gitignore +0 -12
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -22
  28. data/.travis.yml +0 -17
  29. data/Gemfile +0 -9
  30. data/LICENSE.txt +0 -21
  31. data/Rakefile +0 -6
  32. data/fixturama.gemspec +0 -24
  33. data/lib/fixturama/seed.rb +0 -15
  34. data/lib/fixturama/stubs.rb +0 -79
  35. data/lib/fixturama/stubs/chain.rb +0 -90
  36. data/lib/fixturama/stubs/chain/actions.rb +0 -39
  37. data/lib/fixturama/stubs/chain/actions/raise.rb +0 -21
  38. data/lib/fixturama/stubs/chain/actions/return.rb +0 -23
  39. data/lib/fixturama/stubs/chain/arguments.rb +0 -86
  40. data/lib/fixturama/stubs/const.rb +0 -43
  41. data/lib/fixturama/stubs/request.rb +0 -98
  42. data/lib/fixturama/stubs/request/response.rb +0 -43
  43. data/lib/fixturama/stubs/request/responses.rb +0 -20
  44. data/lib/fixturama/utils.rb +0 -39
  45. data/spec/fixturama/load_fixture/_spec.rb +0 -53
  46. data/spec/fixturama/load_fixture/data.json +0 -5
  47. data/spec/fixturama/load_fixture/data.yaml +0 -3
  48. data/spec/fixturama/load_fixture/data.yml +0 -3
  49. data/spec/fixturama/seed_fixture/_spec.rb +0 -36
  50. data/spec/fixturama/seed_fixture/seed.yml +0 -16
  51. data/spec/fixturama/stub_fixture/_spec.rb +0 -120
  52. data/spec/fixturama/stub_fixture/stub.yml +0 -73
  53. data/spec/spec_helper.rb +0 -26
@@ -0,0 +1,64 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # Stub a chain of messages
5
+ #
6
+ class Chain < Base
7
+ require_relative "chain/raise_action"
8
+ require_relative "chain/return_action"
9
+ require_relative "chain/actions"
10
+ require_relative "chain/arguments"
11
+
12
+ def key
13
+ @key ||= ["chain", @receiver.name, *@messages].join(".")
14
+ end
15
+
16
+ def merge(other)
17
+ return self unless other.instance_of?(self.class) && other.key == key
18
+
19
+ tap { @arguments = (other.arguments | arguments).sort_by(&:order) }
20
+ end
21
+
22
+ def call(example)
23
+ call_action = example.send(:receive_message_chain, *@messages) do |*real|
24
+ action = arguments.find { |expected| expected.match?(*real) }
25
+ action ? action.call : raise("Unexpected arguments: #{real}")
26
+ end
27
+
28
+ example.send(:allow, @receiver).to call_action
29
+ end
30
+
31
+ protected
32
+
33
+ attr_reader :arguments
34
+
35
+ private
36
+
37
+ def initialize(**options)
38
+ @receiver = receiver_from(options)
39
+ @messages = messages_from(options)
40
+ @arguments = [Arguments.new(options)]
41
+ end
42
+
43
+ def receiver_from(options)
44
+ case options.slice(:class, :object).keys
45
+ when %i[class] then Kernel.const_get(options[:class])
46
+ when %i[object] then Object.send(:eval, options[:object])
47
+ else raise
48
+ end
49
+ rescue StandardError => err
50
+ raise Fixturama::FixtureError.new("a stabbed object", options, err)
51
+ end
52
+
53
+ def messages_from(options)
54
+ case value = options[:chain]
55
+ when Array then value.map(&:to_sym)
56
+ when String then [value.to_sym]
57
+ when Symbol then value
58
+ else raise
59
+ end
60
+ rescue StandardError => err
61
+ raise Fixturama::FixtureError.new("a messages chain", options, err)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ class Fixturama::Changes::Chain
2
+ #
3
+ # @private
4
+ # Keep arguments of a message chain along with the corresponding actions
5
+ #
6
+ class Actions
7
+ def next
8
+ @list.count > 1 ? @list.pop : @list.first
9
+ end
10
+
11
+ private
12
+
13
+ def initialize(*list)
14
+ list = [{ return: nil }] if list.empty?
15
+
16
+ @list = list.flatten.reverse.flat_map do |item|
17
+ action = build(item)
18
+ [action] * action.repeat
19
+ end
20
+ end
21
+
22
+ def build(item)
23
+ item = Hash(item).transform_keys(&:to_sym)
24
+ case item.slice(:return, :raise).keys
25
+ when %i[return] then ReturnAction.new(item)
26
+ when %i[raise] then RaiseAction.new(item)
27
+ else raise Fixturama::FixtureError.new("an action", item)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ class Fixturama::Changes::Chain
2
+ #
3
+ # @private
4
+ # Keep a set of arguments along with the corresponding actions to be done
5
+ #
6
+ class Arguments
7
+ # @return [Array<Object>] the collection of arguments
8
+ attr_reader :arguments
9
+
10
+ # Order of comparing this set of arguments with the actual ones
11
+ # @return [Integer]
12
+ def order
13
+ -arguments.count
14
+ end
15
+
16
+ # Compare definitions by sets of arguments
17
+ # @param [Fixturama::Changes::Chain::Arguments] other
18
+ # @return [Boolean]
19
+ def ==(other)
20
+ other.arguments == arguments
21
+ end
22
+
23
+ # If actual arguments are covered by the current ones
24
+ def match?(*args, **opts)
25
+ return false if arguments.count > args.count + 1
26
+
27
+ arguments.first(args.count).zip(args).each do |(expected, actual)|
28
+ return false unless actual == expected
29
+ end
30
+
31
+ if arguments.count > args.count
32
+ Hash(arguments.last).transform_keys(&:to_sym).each do |k, v|
33
+ return false unless opts[k] == v
34
+ end
35
+ end
36
+
37
+ true
38
+ end
39
+
40
+ # Call the corresponding action if actual arguments are matched
41
+ def call
42
+ @actions.next.call
43
+ end
44
+
45
+ private
46
+
47
+ def initialize(options)
48
+ @arguments = extract(options, :arguments)
49
+ @actions = Actions.new(*extract(options, :actions))
50
+ end
51
+
52
+ def extract(options, key)
53
+ return [] unless options.key?(key)
54
+
55
+ source = options[key]
56
+ source.is_a?(Array) ? source : [source]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,43 @@
1
+ class Fixturama::Changes::Chain
2
+ #
3
+ # @private
4
+ # Raise a specified exception as a result of stubbing
5
+ #
6
+ class RaiseAction
7
+ attr_reader :repeat
8
+
9
+ def call
10
+ raise @error
11
+ end
12
+
13
+ private
14
+
15
+ def initialize(**options)
16
+ @error = error_from options
17
+ @repeat = repeat_from options
18
+ rescue StandardError => err
19
+ raise Fixturama::FixtureError.new("an exception class", options, err)
20
+ end
21
+
22
+ def error_from(options)
23
+ klass = klass_from(options)
24
+ params = options[:arguments]
25
+ params.is_a?(Array) ? klass.new(*params) : klass
26
+ end
27
+
28
+ def klass_from(options)
29
+ klass = case value = options[:raise]
30
+ when NilClass, TrueClass, "true" then StandardError
31
+ when Class then value
32
+ else Kernel.const_get(value)
33
+ end
34
+
35
+ klass < Exception ? klass : raise("#{klass} is not an exception")
36
+ end
37
+
38
+ def repeat_from(options)
39
+ options.fetch(:repeat, 1).to_i.tap { |val| return val if val.positive? }
40
+ raise Fixturama::FixtureError.new("a number of repeats", options)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ class Fixturama::Changes::Chain
2
+ #
3
+ # @private
4
+ # Return a specified value as a result of stubbing
5
+ #
6
+ class ReturnAction
7
+ attr_reader :repeat
8
+
9
+ def call
10
+ @value
11
+ end
12
+
13
+ private
14
+
15
+ def initialize(**options)
16
+ @value = value_from(options)
17
+ @repeat = repeat_from(options)
18
+ end
19
+
20
+ def value_from(options)
21
+ value = options[:return]
22
+ value.respond_to?(:dup) ? value.dup : value
23
+ rescue TypeError
24
+ # in the Ruby 2.3.0 Fixnum#dup is defined, but raises TypeError
25
+ value
26
+ end
27
+
28
+ def repeat_from(options)
29
+ options.fetch(:repeat, 1).to_i.tap { |val| return val if val.positive? }
30
+ raise Fixturama::FixtureError.new("a number of repeats", options)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # Stub a constant
5
+ #
6
+ class Const < Base
7
+ def key
8
+ name
9
+ end
10
+
11
+ def call(example)
12
+ example.send(:stub_const, name, value)
13
+ self
14
+ end
15
+
16
+ private
17
+
18
+ def initialize(**options)
19
+ @options = options
20
+ end
21
+
22
+ def name
23
+ @options[:const]
24
+ end
25
+
26
+ def value
27
+ @options[:value]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # Stub an environment variable
5
+ #
6
+ class Env < Base
7
+ # All changes has the same +key+
8
+ # They will be merged before stubbing (see +call+)
9
+ def key
10
+ "ENV"
11
+ end
12
+
13
+ # When we merge 2 env-s, we just merge their options
14
+ def merge(other)
15
+ return self unless other.is_a?(self.class)
16
+ dup.tap { |env| env.options.update(other.options) }
17
+ end
18
+
19
+ def call(example)
20
+ original = Hash ENV
21
+ example.send(:stub_const, "ENV", original.merge(options))
22
+ self
23
+ end
24
+
25
+ protected
26
+
27
+ attr_reader :options
28
+
29
+ private
30
+
31
+ def initialize(**options)
32
+ @options = { options[:env].to_s => options[:value].to_s }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,91 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # Stub an HTTP(S) request using +Webmock+
5
+ #
6
+ class Request < Base
7
+ require_relative "request/response"
8
+ require_relative "request/responses"
9
+
10
+ def call(example)
11
+ stub = example.stub_request(http_method, uri)
12
+ stub = stub.with(request) if request.any?
13
+ stub.to_return { |_| responses.next }
14
+ self
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :options
20
+
21
+ def initialize(options)
22
+ @options = options
23
+ with_error { @options = Hash(@options).transform_keys(&:to_sym) }
24
+ end
25
+
26
+ HTTP_METHODS = %i[get post put patch delete head options any].freeze
27
+
28
+ def http_method
29
+ with_error("http method") do
30
+ value = with_error("method") { options[:method]&.to_sym&.downcase }
31
+ value ||= :any
32
+ raise("Invalid HTTP method") unless HTTP_METHODS.include?(value)
33
+ value
34
+ end
35
+ end
36
+
37
+ def uri
38
+ with_error("uri") { maybe_regexp(options[:uri] || options[:url]) }
39
+ end
40
+
41
+ def headers
42
+ with_error("headers") do
43
+ Hash(options[:headers]).transform_keys(&:to_s) if options.key? :headers
44
+ end
45
+ end
46
+
47
+ def query
48
+ with_error("query") do
49
+ Hash(options[:query]).transform_keys(&:to_s) if options.key?(:query)
50
+ end
51
+ end
52
+
53
+ def body
54
+ with_error("body") { maybe_regexp options[:body] }
55
+ end
56
+
57
+ def basic_auth
58
+ with_error("basic auth") do
59
+ value = options[:auth] || options[:basic_auth]
60
+ Hash(value).transform_keys(&:to_s).values_at("user", "pass") if value
61
+ end
62
+ end
63
+
64
+ def request
65
+ @request ||= {
66
+ headers: headers,
67
+ body: body,
68
+ query: query,
69
+ basic_auth: basic_auth
70
+ }.select { |_, val| val }
71
+ end
72
+
73
+ def responses
74
+ @responses ||= Responses.new(options[:response] || options[:responses])
75
+ end
76
+
77
+ def with_error(part = nil)
78
+ yield
79
+ rescue StandardError => err
80
+ part = ["a valid request", part].compact.join(" ")
81
+ raise Fixturama::FixtureError.new(part, options, err)
82
+ end
83
+
84
+ def maybe_regexp(str)
85
+ return unless str
86
+
87
+ str = str.to_s
88
+ str[%r{\A/.*/\z}] ? Regexp.new(str[1..-2]) : str
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,62 @@
1
+ class Fixturama::Changes::Request
2
+ #
3
+ # @private
4
+ # Store data for a response to the corresponding request
5
+ #
6
+ class Response
7
+ # @!attribute [r] repeat A number of times the response to be repeated
8
+ # @return [Integer]
9
+ attr_reader :repeat
10
+
11
+ # @!attribute [r] to_h A hash for the +to_respond(...)+ part of the stub
12
+ # @return [Hash<Symbol, Object>]
13
+ attr_reader :to_h
14
+
15
+ private
16
+
17
+ def initialize(options)
18
+ @options = options
19
+ @options = with_error { Hash(options).transform_keys(&:to_sym) }
20
+ @to_h = build_hash
21
+ @repeat = build_repeat
22
+ end
23
+
24
+ def build_repeat
25
+ with_error("number of repeats") do
26
+ value = @options.fetch(:repeat, 1).to_i
27
+ value.positive? ? value : raise("Wrong value")
28
+ end
29
+ end
30
+
31
+ def build_hash
32
+ { status: status, body: body, headers: headers }.select { |_, v| v }
33
+ end
34
+
35
+ def status
36
+ with_error("status") { @options[:status]&.to_i } || 200
37
+ end
38
+
39
+ def body
40
+ with_error("body") do
41
+ case @options[:body]
42
+ when NilClass then nil
43
+ when Hash then JSON.dump(@options[:body])
44
+ else @options[:body].to_s
45
+ end
46
+ end
47
+ end
48
+
49
+ def headers
50
+ with_error("headers") do
51
+ Hash(@options[:headers]).map { |k, v| [k.to_s, v.to_s] }.to_h
52
+ end
53
+ end
54
+
55
+ def with_error(part = nil)
56
+ yield
57
+ rescue StandardError => err
58
+ object = ["a response", part].compact.join(" ")
59
+ raise Fixturama::FixtureError.new(object, options, err)
60
+ end
61
+ end
62
+ end