faraday-hot_mock 0.3.0 → 0.4.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
  SHA256:
3
- metadata.gz: ca41f6d974866b895fa2ce0da58d30c61120d63c342dcb39d353346156692abe
4
- data.tar.gz: f45729e717e06848776bca596533d8e4af59cf22eebd83ee5a0e4405ff003ec1
3
+ metadata.gz: 56228dde369003b5c2b7a68b7af56e2533c13d07ffc9dc997c8fe3a937c48f55
4
+ data.tar.gz: bc60f461818b035f6e3d14a28d6b6f9943d47bac85ce398013a91569fba6268f
5
5
  SHA512:
6
- metadata.gz: af9f9770c7e9893202512044fc30502871766c4056541eb3f723fc4975948fa72d6d8567e4a751085dbbfd2f0fbbb0b32bcd4dae06d93862575763e9b1045a57
7
- data.tar.gz: f99a15ab120d185c605ba7f9982c1e9925ca9fe8065645152236c3094fc6bcfe5db1878f6a8fcac5f1ae321cad235b97330ba58fe03e735f953882ac15a22f56
6
+ metadata.gz: 6398844d597bda4844ef3967f104dd6300ff9d9226128b34df2ef2ae096e740f0037b9708f53f70acf5151405a581d507a15ff4e3e9db3fc0d590bfabcdd8694
7
+ data.tar.gz: 3ee86b689589bb1cf24d012a47feddefe1e2d8074497953acf7ee85d1936a02387ec3124b07b2cd366345005a5e5dc73a67507e7b63452f209e05ed226905afa
data/README.md CHANGED
@@ -6,6 +6,17 @@ This adapter attempts to make that simpler by parsing YAML files at runtime. If
6
6
 
7
7
  _**This adapter is meant for Faraday usage in Rails, not for Faraday that's used in other frameworks or situations.**_
8
8
 
9
+ ## Why Use Faraday::HotMock instead of VCR?
10
+
11
+ VCR focuses on keeping HTTP requests out of tests - Faraday::HotMock focuses on simulating API responses during development.
12
+
13
+ To use VCR in development would require wrapping code in VCR blocks, which must then be undone before deployment. Simple, but tedious and error-prone.
14
+
15
+ Faraday::HotMock requires no code changes to the application - it doesn't mock anything in production, even if you try. So while the HotMock adapter is "used", it just passes requests to the default or adapter or specified fallback.
16
+
17
+ VCR works with any HTTP library, Faraday::HotMock only works with Faraday. This is a critical limitation unless you use only or primarily Faraday.
18
+
19
+ You could, ostensibly, replace VCR with Faraday::HotMock in tests. VCR is battle-tested, well-written and widely used, so it's likely a better choice for testing. But the goal is to make Faraday::HotMock just as useful in all non-production environments.
9
20
 
10
21
  ## How It Works
11
22
 
@@ -19,13 +30,12 @@ This means that if you have a Staging environment, or a UAT environment along wi
19
30
 
20
31
  You can organize your per-environment mocks as you see fit - all in one file, or split between descriptive directories and file names.
21
32
 
22
-
23
33
  ## Installation
24
34
 
25
35
  Add this line to your application's Gemfile:
26
36
 
27
37
  ```ruby
28
- gem "faraday-hot_mock", git: "https://github.com/seanhogge/faraday-hot_mock"
38
+ gem "faraday-hot_mock"
29
39
  ```
30
40
 
31
41
  And then execute:
@@ -33,7 +43,6 @@ And then execute:
33
43
  $ bundle
34
44
  ```
35
45
 
36
-
37
46
  ## Usage
38
47
 
39
48
  Add this adapter to your middleware pipeline, making sure that it's last:
@@ -52,11 +61,11 @@ Optionally, specify a fallback adapter (`Faraday.default_adapter` is the default
52
61
  @conn = Faraday.new(url: "https://dog.ceo/api/") do |faraday|
53
62
  faraday.request :json
54
63
  faraday.response :json
55
- faraday.adapter :hot_mock, fallback: :cool_community_adapter
64
+ faraday.adapter :hot_mock, fallback: :cool_community_adapter # only useful if the default adapter isn't already :cool_community_adapter!
56
65
  end
57
66
  ```
58
67
 
59
- Then add the switch: `tmp/mocking-#{Rails.env}.txt`. Just like Rails' own `tmp/caching-dev.txt` file, this will toggle HotMock on when present, and off when not present.
68
+ Then add the switch: `tmp/mocking-#{Rails.env}.txt` (or use the [convenience method](#convenience-methods)). Just like Rails' own `tmp/caching-dev.txt` file, this will toggle HotMock on when present, and off when not present.
60
69
 
61
70
  > ⚠️ REMEMBER: For caching, it's `tmp/caching-dev.txt`, but for mocking it's `tmp/mocking-development.txt`
62
71
 
@@ -64,7 +73,21 @@ Now, create the directory `lib/faraday/mocks/` and a subdirectory for each envir
64
73
 
65
74
  > ⚠️ REMEMBER: it's `lib/faraday/mocks`, not `app/lib/faraday/mocks`
66
75
 
67
- Consider adding these directories to .gitignore unless you want mocks to be shared.
76
+ ### Git Ignore?
77
+
78
+ It can be useful to share mocks with others, or to ensure you don't ever lose them.
79
+
80
+ It can also be a terrible idea if your mocks are designed for your specific needs, and can cause noise in PRs and commits.
81
+
82
+ It's up to you and your team what makes sense.
83
+
84
+ In most cases, it makes sense to check in the mocks for the test environment, and ignore all the rest so in most cases you should add to your `.gitignore`:
85
+
86
+ ```
87
+ # Ignore Faraday HotMocks except in test env
88
+ lib/faraday/mocks/**
89
+ !lib/faraday/mocks/test
90
+ ```
68
91
 
69
92
  ### Convenience Methods
70
93
 
@@ -76,19 +99,27 @@ Faraday::HotMock.disable! # deletes tmp/mocking-#{Rails.env}.txt
76
99
  Faraday::HotMock.toggle! # creates tmp/mocking-#{Rails.env}.txt if missing, deletes if present
77
100
  ```
78
101
 
79
- In addition, you can check if mocking is enabled with:
102
+ You can check if mocking is enabled with:
80
103
 
81
104
  ```ruby
82
105
  Faraday::HotMock.enabled? # returns true/false;
83
106
  Faraday::HotMock.disabled? # returns true/false;
84
107
  ```
85
108
 
86
- These methods have limited use, but can be helpful in scripting scenarios.
109
+ You can check if a given url and method will match against a mock with:
110
+
111
+ ```ruby
112
+ Faraday::HotMock.mocked?(method: 'GET', url: 'https://vendorname.com/api/v1/endpoint') # returns matching mock or false
113
+ ```
114
+
115
+ These methods have limited use, but can be helpful in scripting scenarios, or to skip tedious file creation and deletion.
87
116
 
88
117
  ### Defining Mocks
89
118
 
119
+ You can simply create a YAML file in `lib/faraday/mocks/#{Rails.env}/` with one or more mock definitions by hand:
120
+
90
121
  ```yaml
91
- # lib/faraday/mocks/development/vendor_name_mocks.yml
122
+ # lib/faraday/mocks/development/any_name_you_like.yml
92
123
  - url_pattern: vendorname.com.*/endpoint
93
124
  method: POST
94
125
  status: 418
@@ -98,12 +129,14 @@ These methods have limited use, but can be helpful in scripting scenarios.
98
129
  error: I'm a teapot
99
130
  ```
100
131
 
132
+ Or you can do the same in the default mock file for the current environment: `lib/faraday/mocks/#{Rails.env}/hot_mocks.yml`.
133
+
101
134
  Now, any POST request made to `vendorname.com/api/v1/endpoint` will return a mock 418 response with a JSON body. A GET to the same endpoint will make the actual call.
102
135
 
103
136
  If you edit the file to be:
104
137
 
105
138
  ```yaml
106
- # lib/faraday/mocks/development/vendor_name_mocks.yml
139
+ # lib/faraday/mocks/development/any_name_you_like.yml
107
140
  - url_pattern: vendorname.com.*/endpoint
108
141
  method: POST
109
142
  status: 503
@@ -115,7 +148,27 @@ If you edit the file to be:
115
148
 
116
149
  then the next request made to `vendorname.com/api/v1/endpoint` will return a mock 503 response with a JSON body. No need to reload anything.
117
150
 
118
- This lets you quickly simulate any type of response you need.
151
+ If you want to add a mock from the Rails console, you can use `Faraday::HotMock.mock!(method: [method], url_pattern: [url_pattern], status: [status], headers: [headers], body: [body])`. This will add the mock to the default mock file for the current environment.
152
+
153
+ A mock can be recorded by calling `Faraday::HotMock.record(method: [method], url: [url])`. This will make the actual call and then store the response in the default mock file for the current environment: `lib/faraday/mocks/#{Rails.env}/hot_mocks.yml`.
154
+
155
+ If a mock already exists for that method and URL, no request will be made and `false` will be returned.
156
+
157
+ Use `Faraday::HotMock.record!` (with a bang) to force recording even if a mock already exists. This will remove the old matching mock and put the new one in its place.
158
+
159
+ > ⚠️ WARNING: Recording does not remove any mocks from custom files, only from the default `hot_mocks.yml` file. If there are duplicates between custom files and the default file, there is no guarantee which one will be used.
160
+
161
+ Once recorded, you can easily edit the mock file to change status codes, headers, or body content as needed.
162
+
163
+ If you need to know where the mock file is located, you can call `Faraday::HotMock.hot_mock_file`, which will return the full path to the default mock file for the current environment.
164
+
165
+ ### REGEX and You(r Mocks)
166
+
167
+ When recording a mock, you must pass a full URL. However, when defining mocks in YAML files, you can use regular expressions for more flexible matching.
168
+
169
+ Remember that once recorded, the `url_pattern` can be adjusted.
170
+
171
+ ### Disabling Mocks
119
172
 
120
173
  If you want to disable mocks, you can:
121
174
 
@@ -127,14 +180,10 @@ If you want to disable mocks, you can:
127
180
  - Delete the directory
128
181
  - Use the [convenience methods](#convenience-methods)
129
182
 
130
- If you'd rather keep the file(s) around, just delete `tmp/mocking-development.txt`. That will globally disable any mocked responses.
131
-
132
-
133
183
  ## Contributing
134
184
 
135
185
  Fork, work, PR while following the [Code of Conduct](https://github.com/seanhogge/faraday-hot_mock/CODE_OF_CONDUCT.md)
136
186
 
137
-
138
187
  ## License
139
188
 
140
189
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -9,60 +9,25 @@ module Faraday
9
9
  class Adapter < Faraday::Adapter
10
10
  def initialize(app, options = {})
11
11
  super(app)
12
- @mock_dir = options[:mock_dir] || default_mock_dir
13
12
  @enabled_file = options[:enabled_file] || "tmp/mocking-#{Rails.env}.txt"
14
13
  fallback = options[:fallback] || Faraday.default_adapter
15
14
  @fallback_adapter = Faraday::Adapter.lookup_middleware(fallback)
16
- @mocks = load_mocks
15
+ @mocks = Faraday::HotMock.mocks
17
16
  end
18
17
 
19
18
  def call(env)
20
19
  super
21
- if mocking_enabled? && @mocks && (mock = find_mock(env.method, env.url))
22
- save_response(env, mock["status"] || 200, mock["body"] || "", mock["headers"] || {})
23
- else
24
- @fallback_adapter.new(@app, @options).call(env)
25
- end
26
- end
27
-
28
- private
29
20
 
30
- def load_mocks
31
- return [] unless Dir.exist?(Rails.root.join @mock_dir)
32
-
33
- mocks = []
34
-
35
- yaml_files.each do |file|
36
- file_mocks = YAML.load_file(file)
37
- mocks.concat(file_mocks) if file_mocks.is_a?(Array)
21
+ if Rails.env.production?
22
+ return @fallback_adapter.new(@app, @options).call(env)
38
23
  end
39
24
 
40
- mocks
41
- end
42
-
43
- def mocking_enabled?
44
- File.exist?(Rails.root.join @enabled_file)
45
- end
46
-
47
- def default_mock_dir
48
- rails_env = defined?(Rails) ? Rails.env : ENV["RAILS_ENV"] || "development"
49
- "lib/faraday/mocks/#{rails_env}"
50
- end
51
-
52
- def find_mock(method, url)
53
- return nil unless @mocks.any?
54
-
55
- @mocks.find do |mock|
56
- url_matches = Regexp.new(mock["url_pattern"]).match?(url.to_s)
57
- method_matches = mock["method"].nil? || mock["method"].to_s.upcase == method.to_s.upcase
58
-
59
- url_matches && method_matches
25
+ if Faraday::HotMock.enabled? && (mock = Faraday::HotMock.mocked?(method: env.method, url: env.url))
26
+ save_response(env, mock["status"] || 200, mock["body"] || "", mock["headers"] || {})
27
+ else
28
+ @fallback_adapter.new(@app, @options).call(env)
60
29
  end
61
30
  end
62
-
63
- def yaml_files
64
- Dir.glob(File.join(Rails.root.join(@mock_dir), "**", "*.{yml,yaml}"))
65
- end
66
31
  end
67
32
  end
68
33
  end
@@ -1,5 +1,5 @@
1
1
  module Faraday
2
2
  module HotMock
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -5,31 +5,140 @@ require "faraday"
5
5
 
6
6
  module Faraday
7
7
  module HotMock
8
- module_function
8
+ extend self
9
9
 
10
10
  def disable!
11
- FileUtils.rm_f(Rails.root.join("tmp/mocking-#{Rails.env}.txt"))
11
+ FileUtils.rm_f(hot_mocking_file)
12
12
  end
13
13
 
14
14
  def disabled?
15
- !File.exist?(Rails.root.join("tmp/mocking-#{Rails.env}.txt"))
15
+ !File.exist?(hot_mocking_file)
16
16
  end
17
17
 
18
18
  def enable!
19
- FileUtils.touch(Rails.root.join("tmp/mocking-#{Rails.env}.txt"))
19
+ FileUtils.touch(hot_mocking_file)
20
20
  end
21
21
 
22
22
  def enabled?
23
- File.exist?(Rails.root.join("tmp/mocking-#{Rails.env}.txt"))
23
+ File.exist?(hot_mocking_file)
24
24
  end
25
25
 
26
26
  def toggle!
27
- if File.exist?(Rails.root.join("tmp/mocking-#{Rails.env}.txt"))
27
+ if File.exist?(hot_mocking_file)
28
28
  disable!
29
29
  else
30
30
  enable!
31
31
  end
32
32
  end
33
+
34
+ def delete_mock(method:, url:)
35
+ return unless File.exist?(hot_mock_file)
36
+
37
+ mocks = YAML.load_file(hot_mock_file) || []
38
+
39
+ mocks.reject! { |entry| entry["url_pattern"] == url && entry["method"].to_s.upcase == method.to_s.upcase }
40
+
41
+ File.write(hot_mock_file, mocks.to_yaml)
42
+ end
43
+
44
+ def mock!(method:, url:, status:, headers: {}, body: nil)
45
+ FileUtils.touch(hot_mock_file)
46
+
47
+ mocks = YAML.load_file(hot_mock_file) || []
48
+
49
+ mocks.reject! { |entry| entry["url_pattern"] == url && entry["method"].to_s.upcase == method.to_s.upcase }
50
+
51
+ mocks << {
52
+ "method" => method.to_s.upcase,
53
+ "url_pattern" => url,
54
+ "status" => status,
55
+ "headers" => headers,
56
+ "body" => body
57
+ }
58
+
59
+ File.write(hot_mock_file, mocks.to_yaml)
60
+ end
61
+
62
+ def mocked?(method:, url:)
63
+ mocks.find { |entry| entry["method"].to_s.upcase == method.to_s.upcase && Regexp.new(entry["url_pattern"]).match?(url.to_s) } || false
64
+ end
65
+
66
+ def record(method:, url:)
67
+ return false if mocked?(method:, url:)
68
+
69
+ faraday = Faraday.new
70
+
71
+ response = faraday.send(method.downcase.to_sym, url)
72
+
73
+ FileUtils.touch(hot_mock_file)
74
+
75
+ hot_mocks = YAML.load_file(hot_mock_file) || []
76
+
77
+ hot_mocks << {
78
+ "method" => method.to_s.upcase,
79
+ "url_pattern" => url,
80
+ "status" => response.status,
81
+ "headers" => response.headers.to_h.merge("x-hotmock-recorded-at" => Time.now.utc.iso8601),
82
+ "body" => response.body
83
+ }
84
+
85
+ File.write(hot_mock_file, hot_mocks.to_yaml)
86
+ rescue Faraday::Error => e
87
+ puts "Error recording mock for #{method.upcase} #{url}: #{e.message}"
88
+ end
89
+
90
+ def record!(method:, url:)
91
+ faraday = Faraday.new
92
+
93
+ response = faraday.send(method.downcase.to_sym, url)
94
+
95
+ FileUtils.touch(hot_mock_file)
96
+
97
+ hot_mocks = YAML.load_file(hot_mock_file) || []
98
+
99
+ hot_mocks.reject! { |entry| entry["url_pattern"] == url && entry["method"].to_s.upcase == method.to_s.upcase }
100
+
101
+ hot_mocks << {
102
+ "method" => method.to_s.upcase,
103
+ "url_pattern" => url,
104
+ "status" => response.status,
105
+ "headers" => response.headers.to_h.merge("x-hotmock-recorded-at" => Time.now.utc.iso8601),
106
+ "body" => response.body
107
+ }
108
+
109
+ File.write(hot_mock_file, hot_mocks.to_yaml)
110
+ rescue Faraday::Error => e
111
+ puts "Error recording mock for #{method.upcase} #{url}: #{e.message}"
112
+ end
113
+
114
+ def hot_mock_dir
115
+ Rails.root.join "lib/faraday/mocks/#{Rails.env}"
116
+ end
117
+
118
+ def hot_mocking_file
119
+ Rails.root.join "tmp/mocking-#{Rails.env}.txt"
120
+ end
121
+
122
+ def hot_mock_file
123
+ Rails.root.join(hot_mock_dir, "hot_mocks.yml")
124
+ end
125
+
126
+ def mocks
127
+ return [] unless Dir.exist?(hot_mock_dir)
128
+
129
+ mocks = []
130
+
131
+ all_hot_mock_files.each do |file|
132
+ file_mocks = YAML.load_file(file)
133
+ mocks.concat(file_mocks) if file_mocks.is_a?(Array)
134
+ end
135
+
136
+ mocks
137
+ end
138
+
139
+ def all_hot_mock_files
140
+ Dir.glob(File.join(hot_mock_dir, "**", "*.{yml,yaml}"))
141
+ end
33
142
  end
34
143
  end
35
144
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday-hot_mock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Hogge