fixturama 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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