wayfarer 0.4.6 → 0.4.8

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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/.env +17 -0
  3. data/.github/workflows/lint.yaml +27 -0
  4. data/.github/workflows/release.yaml +30 -0
  5. data/.github/workflows/tests.yaml +21 -0
  6. data/.gitignore +5 -1
  7. data/.rubocop.yml +36 -0
  8. data/.vale.ini +8 -0
  9. data/.yardopts +1 -3
  10. data/Dockerfile +6 -4
  11. data/Gemfile +24 -0
  12. data/Gemfile.lock +274 -164
  13. data/Rakefile +7 -51
  14. data/bin/wayfarer +1 -1
  15. data/docker-compose.yml +23 -13
  16. data/docs/cookbook/consent_screen.md +2 -2
  17. data/docs/cookbook/executing_javascript.md +3 -3
  18. data/docs/cookbook/navigation.md +12 -12
  19. data/docs/cookbook/querying_html.md +3 -3
  20. data/docs/cookbook/screenshots.md +2 -2
  21. data/docs/guides/callbacks.md +25 -125
  22. data/docs/guides/cli.md +71 -0
  23. data/docs/guides/configuration.md +10 -35
  24. data/docs/guides/development.md +67 -0
  25. data/docs/guides/handlers.md +60 -0
  26. data/docs/guides/index.md +1 -0
  27. data/docs/guides/jobs.md +142 -31
  28. data/docs/guides/navigation.md +1 -1
  29. data/docs/guides/networking/capybara.md +13 -22
  30. data/docs/guides/networking/custom_adapters.md +103 -41
  31. data/docs/guides/networking/ferrum.md +4 -4
  32. data/docs/guides/networking/http.md +9 -13
  33. data/docs/guides/networking/selenium.md +10 -11
  34. data/docs/guides/pages.md +78 -10
  35. data/docs/guides/redis.md +10 -0
  36. data/docs/guides/routing.md +156 -0
  37. data/docs/guides/tasks.md +53 -9
  38. data/docs/guides/tutorial.md +66 -0
  39. data/docs/guides/user_agents.md +115 -0
  40. data/docs/index.md +17 -40
  41. data/lib/wayfarer/base.rb +125 -46
  42. data/lib/wayfarer/batch_completion.rb +60 -0
  43. data/lib/wayfarer/callbacks.rb +22 -48
  44. data/lib/wayfarer/cli/route_printer.rb +85 -89
  45. data/lib/wayfarer/cli.rb +103 -0
  46. data/lib/wayfarer/gc.rb +18 -6
  47. data/lib/wayfarer/handler.rb +15 -7
  48. data/lib/wayfarer/kv.rb +28 -0
  49. data/lib/wayfarer/logging.rb +38 -0
  50. data/lib/wayfarer/middleware/base.rb +2 -0
  51. data/lib/wayfarer/middleware/batch_completion.rb +19 -0
  52. data/lib/wayfarer/middleware/chain.rb +7 -1
  53. data/lib/wayfarer/middleware/content_type.rb +59 -0
  54. data/lib/wayfarer/middleware/controller.rb +19 -15
  55. data/lib/wayfarer/middleware/dedup.rb +22 -13
  56. data/lib/wayfarer/middleware/dispatch.rb +17 -4
  57. data/lib/wayfarer/middleware/normalize.rb +7 -14
  58. data/lib/wayfarer/middleware/redis.rb +15 -0
  59. data/lib/wayfarer/middleware/router.rb +33 -35
  60. data/lib/wayfarer/middleware/stage.rb +5 -5
  61. data/lib/wayfarer/middleware/uri_parser.rb +31 -0
  62. data/lib/wayfarer/middleware/user_agent.rb +49 -0
  63. data/lib/wayfarer/networking/capybara.rb +1 -1
  64. data/lib/wayfarer/networking/context.rb +14 -3
  65. data/lib/wayfarer/networking/ferrum.rb +1 -4
  66. data/lib/wayfarer/networking/follow.rb +14 -7
  67. data/lib/wayfarer/networking/http.rb +1 -1
  68. data/lib/wayfarer/networking/pool.rb +23 -13
  69. data/lib/wayfarer/networking/selenium.rb +15 -7
  70. data/lib/wayfarer/networking/strategy.rb +2 -2
  71. data/lib/wayfarer/page.rb +34 -14
  72. data/lib/wayfarer/parsing/xml.rb +6 -6
  73. data/lib/wayfarer/parsing.rb +21 -0
  74. data/lib/wayfarer/redis/barrier.rb +26 -21
  75. data/lib/wayfarer/redis/counter.rb +18 -9
  76. data/lib/wayfarer/redis/pool.rb +1 -1
  77. data/lib/wayfarer/redis/resettable.rb +19 -0
  78. data/lib/wayfarer/routing/dsl.rb +166 -30
  79. data/lib/wayfarer/routing/hash_stack.rb +33 -0
  80. data/lib/wayfarer/routing/matchers/custom.rb +8 -5
  81. data/lib/wayfarer/routing/matchers/{suffix.rb → empty_params.rb} +2 -6
  82. data/lib/wayfarer/routing/matchers/host.rb +15 -9
  83. data/lib/wayfarer/routing/matchers/path.rb +11 -31
  84. data/lib/wayfarer/routing/matchers/query.rb +41 -17
  85. data/lib/wayfarer/routing/matchers/result.rb +12 -0
  86. data/lib/wayfarer/routing/matchers/scheme.rb +13 -5
  87. data/lib/wayfarer/routing/matchers/url.rb +13 -5
  88. data/lib/wayfarer/routing/path_consumer.rb +130 -0
  89. data/lib/wayfarer/routing/path_finder.rb +151 -23
  90. data/lib/wayfarer/routing/result.rb +1 -1
  91. data/lib/wayfarer/routing/root_route.rb +17 -1
  92. data/lib/wayfarer/routing/route.rb +66 -19
  93. data/lib/wayfarer/routing/serializable.rb +28 -0
  94. data/lib/wayfarer/routing/sub_route.rb +53 -0
  95. data/lib/wayfarer/routing/target_route.rb +17 -1
  96. data/lib/wayfarer/stringify.rb +21 -30
  97. data/lib/wayfarer/task.rb +9 -17
  98. data/lib/wayfarer/uri/normalization.rb +120 -0
  99. data/lib/wayfarer.rb +72 -5
  100. data/mise.toml +2 -0
  101. data/mkdocs.yml +44 -8
  102. data/rake/docs.rake +26 -0
  103. data/rake/lint.rake +9 -0
  104. data/rake/release.rake +23 -0
  105. data/rake/tests.rake +32 -0
  106. data/requirements.txt +1 -1
  107. data/spec/factories/job.rb +8 -0
  108. data/spec/factories/middleware.rb +2 -2
  109. data/spec/factories/path_finder.rb +11 -0
  110. data/spec/factories/redis.rb +19 -0
  111. data/spec/factories/task.rb +46 -2
  112. data/spec/spec_helpers.rb +55 -51
  113. data/spec/support/active_job_helpers.rb +8 -0
  114. data/spec/support/integration_helpers.rb +21 -0
  115. data/spec/support/redis_helpers.rb +9 -0
  116. data/spec/support/test_app.rb +66 -37
  117. data/spec/wayfarer/base_spec.rb +200 -0
  118. data/spec/wayfarer/batch_completion_spec.rb +142 -0
  119. data/spec/wayfarer/cli/job_spec.rb +88 -0
  120. data/spec/wayfarer/cli/routing_spec.rb +322 -0
  121. data/spec/{cli → wayfarer/cli}/version_spec.rb +1 -1
  122. data/spec/wayfarer/gc_spec.rb +29 -0
  123. data/spec/wayfarer/handler_spec.rb +9 -0
  124. data/spec/wayfarer/integration/callbacks_spec.rb +200 -0
  125. data/spec/wayfarer/integration/content_type_spec.rb +37 -0
  126. data/spec/wayfarer/integration/custom_routing_spec.rb +51 -0
  127. data/spec/wayfarer/integration/gc_spec.rb +40 -0
  128. data/spec/wayfarer/integration/handler_spec.rb +65 -0
  129. data/spec/wayfarer/integration/page_spec.rb +79 -0
  130. data/spec/wayfarer/integration/params_spec.rb +64 -0
  131. data/spec/wayfarer/integration/parsing_spec.rb +99 -0
  132. data/spec/wayfarer/integration/retry_spec.rb +112 -0
  133. data/spec/wayfarer/integration/stage_spec.rb +58 -0
  134. data/spec/wayfarer/middleware/batch_completion_spec.rb +33 -0
  135. data/spec/{middleware → wayfarer/middleware}/chain_spec.rb +24 -19
  136. data/spec/wayfarer/middleware/content_type_spec.rb +83 -0
  137. data/spec/{middleware → wayfarer/middleware}/controller_spec.rb +24 -22
  138. data/spec/wayfarer/middleware/dedup_spec.rb +66 -0
  139. data/spec/wayfarer/middleware/normalize_spec.rb +32 -0
  140. data/spec/wayfarer/middleware/router_spec.rb +102 -0
  141. data/spec/wayfarer/middleware/stage_spec.rb +63 -0
  142. data/spec/wayfarer/middleware/uri_parser_spec.rb +63 -0
  143. data/spec/wayfarer/middleware/user_agent_spec.rb +158 -0
  144. data/spec/wayfarer/networking/capybara_spec.rb +13 -0
  145. data/spec/{networking → wayfarer/networking}/context_spec.rb +46 -38
  146. data/spec/wayfarer/networking/ferrum_spec.rb +13 -0
  147. data/spec/{networking → wayfarer/networking}/follow_spec.rb +11 -6
  148. data/spec/wayfarer/networking/http_spec.rb +12 -0
  149. data/spec/{networking → wayfarer/networking}/pool_spec.rb +16 -14
  150. data/spec/wayfarer/networking/selenium_spec.rb +12 -0
  151. data/spec/{networking → wayfarer/networking}/strategy.rb +33 -54
  152. data/spec/wayfarer/page_spec.rb +69 -0
  153. data/spec/{parsing → wayfarer/parsing}/json_spec.rb +1 -1
  154. data/spec/wayfarer/parsing/xml_parse_spec.rb +25 -0
  155. data/spec/wayfarer/redis/barrier_spec.rb +39 -0
  156. data/spec/wayfarer/redis/counter_spec.rb +34 -0
  157. data/spec/{redis → wayfarer/redis}/pool_spec.rb +4 -3
  158. data/spec/{routing → wayfarer/routing}/dsl_spec.rb +12 -22
  159. data/spec/wayfarer/routing/hash_stack_spec.rb +63 -0
  160. data/spec/wayfarer/routing/integration_spec.rb +101 -0
  161. data/spec/wayfarer/routing/matchers/custom_spec.rb +39 -0
  162. data/spec/wayfarer/routing/matchers/host_spec.rb +56 -0
  163. data/spec/wayfarer/routing/matchers/matcher.rb +17 -0
  164. data/spec/wayfarer/routing/matchers/path_spec.rb +43 -0
  165. data/spec/wayfarer/routing/matchers/query_spec.rb +123 -0
  166. data/spec/wayfarer/routing/matchers/scheme_spec.rb +45 -0
  167. data/spec/wayfarer/routing/matchers/url_spec.rb +33 -0
  168. data/spec/wayfarer/routing/path_consumer_spec.rb +123 -0
  169. data/spec/wayfarer/routing/path_finder_spec.rb +409 -0
  170. data/spec/wayfarer/routing/root_route_spec.rb +51 -0
  171. data/spec/wayfarer/routing/route_spec.rb +74 -0
  172. data/spec/wayfarer/routing/sub_route_spec.rb +103 -0
  173. data/spec/wayfarer/task_spec.rb +13 -0
  174. data/spec/wayfarer/uri/normalization_spec.rb +98 -0
  175. data/spec/wayfarer_spec.rb +2 -2
  176. data/wayfarer.gemspec +18 -28
  177. metadata +797 -265
  178. data/.github/workflows/ci.yaml +0 -32
  179. data/.rbenv-gemsets +0 -1
  180. data/.ruby-version +0 -1
  181. data/RELEASING.md +0 -17
  182. data/docs/cookbook/user_agent.md +0 -7
  183. data/docs/guides/error_handling.md +0 -53
  184. data/docs/guides/networking.md +0 -94
  185. data/docs/guides/performance.md +0 -130
  186. data/docs/guides/reliability.md +0 -41
  187. data/docs/guides/routing/steering.md +0 -30
  188. data/docs/reference/api/base.md +0 -48
  189. data/docs/reference/cli.md +0 -61
  190. data/docs/reference/configuration_keys.md +0 -43
  191. data/docs/reference/environment_variables.md +0 -83
  192. data/lib/wayfarer/cli/base.rb +0 -45
  193. data/lib/wayfarer/cli/generate.rb +0 -17
  194. data/lib/wayfarer/cli/job.rb +0 -56
  195. data/lib/wayfarer/cli/route.rb +0 -29
  196. data/lib/wayfarer/cli/runner.rb +0 -34
  197. data/lib/wayfarer/cli/templates/Gemfile.tt +0 -5
  198. data/lib/wayfarer/cli/templates/job.rb.tt +0 -10
  199. data/lib/wayfarer/config/capybara.rb +0 -10
  200. data/lib/wayfarer/config/ferrum.rb +0 -11
  201. data/lib/wayfarer/config/networking.rb +0 -29
  202. data/lib/wayfarer/config/redis.rb +0 -14
  203. data/lib/wayfarer/config/root.rb +0 -11
  204. data/lib/wayfarer/config/selenium.rb +0 -21
  205. data/lib/wayfarer/config/strconv.rb +0 -45
  206. data/lib/wayfarer/config/struct.rb +0 -72
  207. data/lib/wayfarer/middleware/fetch.rb +0 -56
  208. data/lib/wayfarer/redis/connection.rb +0 -13
  209. data/lib/wayfarer/redis/version.rb +0 -19
  210. data/lib/wayfarer/routing/router.rb +0 -28
  211. data/spec/base_spec.rb +0 -224
  212. data/spec/callbacks_spec.rb +0 -102
  213. data/spec/cli/generate_spec.rb +0 -39
  214. data/spec/cli/job_spec.rb +0 -78
  215. data/spec/config/capybara_spec.rb +0 -18
  216. data/spec/config/ferrum_spec.rb +0 -24
  217. data/spec/config/networking_spec.rb +0 -73
  218. data/spec/config/redis_spec.rb +0 -32
  219. data/spec/config/root_spec.rb +0 -31
  220. data/spec/config/selenium_spec.rb +0 -56
  221. data/spec/config/strconv_spec.rb +0 -58
  222. data/spec/config/struct_spec.rb +0 -66
  223. data/spec/fixtures/dummy_job.rb +0 -7
  224. data/spec/gc_spec.rb +0 -59
  225. data/spec/handler_spec.rb +0 -11
  226. data/spec/integration/callbacks_spec.rb +0 -85
  227. data/spec/integration/page_spec.rb +0 -62
  228. data/spec/integration/params_spec.rb +0 -56
  229. data/spec/integration/stage_spec.rb +0 -51
  230. data/spec/integration/steering_spec.rb +0 -57
  231. data/spec/middleware/dedup_spec.rb +0 -88
  232. data/spec/middleware/dispatch_spec.rb +0 -43
  233. data/spec/middleware/fetch_spec.rb +0 -155
  234. data/spec/middleware/normalize_spec.rb +0 -29
  235. data/spec/middleware/router_spec.rb +0 -105
  236. data/spec/middleware/stage_spec.rb +0 -62
  237. data/spec/networking/capybara_spec.rb +0 -12
  238. data/spec/networking/ferrum_spec.rb +0 -12
  239. data/spec/networking/http_spec.rb +0 -12
  240. data/spec/networking/selenium_spec.rb +0 -12
  241. data/spec/page_spec.rb +0 -47
  242. data/spec/parsing/xml_spec.rb +0 -25
  243. data/spec/redis/barrier_spec.rb +0 -78
  244. data/spec/redis/counter_spec.rb +0 -32
  245. data/spec/redis/version_spec.rb +0 -13
  246. data/spec/routing/integration_spec.rb +0 -110
  247. data/spec/routing/matchers/custom_spec.rb +0 -31
  248. data/spec/routing/matchers/host_spec.rb +0 -49
  249. data/spec/routing/matchers/path_spec.rb +0 -43
  250. data/spec/routing/matchers/query_spec.rb +0 -137
  251. data/spec/routing/matchers/scheme_spec.rb +0 -25
  252. data/spec/routing/matchers/suffix_spec.rb +0 -41
  253. data/spec/routing/matchers/uri_spec.rb +0 -27
  254. data/spec/routing/path_finder_spec.rb +0 -33
  255. data/spec/routing/root_route_spec.rb +0 -29
  256. data/spec/routing/route_spec.rb +0 -43
  257. data/spec/routing/router_spec.rb +0 -24
  258. data/spec/task_spec.rb +0 -34
  259. data/spec/{stringify_spec.rb → wayfarer/stringify_spec.rb} +2 -2
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe Wayfarer::Redis::Counter, :redis do
6
+ subject(:counter) { build(:counter, task: task) }
7
+
8
+ let(:task) { build(:task, :redis_pool) }
9
+
10
+ describe "#redis_key" do
11
+ specify do
12
+ expect(counter.redis_key).to eq("wayfarer-counter-batch")
13
+ end
14
+ end
15
+
16
+ describe "#increment" do
17
+ specify do
18
+ expect { counter.increment }.to change(counter, :value).by(1)
19
+ end
20
+ end
21
+
22
+ describe "#decrement" do
23
+ specify do
24
+ expect { counter.decrement }.to change(counter, :value).by(-1)
25
+ end
26
+ end
27
+
28
+ describe "#reset!" do
29
+ it "resets" do
30
+ 3.times { counter.increment }
31
+ expect { counter.reset! }.to change(counter, :value).to(0)
32
+ end
33
+ end
34
+ end
@@ -2,13 +2,14 @@
2
2
 
3
3
  require "spec_helpers"
4
4
 
5
- describe Wayfarer::Redis::Pool, redis: true do
5
+ describe Wayfarer::Redis::Pool, :redis do
6
+ subject(:pool) { described_class.send(:new) }
7
+
6
8
  let(:conn) { spy }
7
- subject(:pool) { Wayfarer::Redis::Pool.send(:new) }
8
9
 
9
10
  describe "::with" do
10
11
  before do
11
- Wayfarer.config.redis.factory = ->(_) { conn }
12
+ Wayfarer.config[:redis][:factory] = ->(_) { conn }
12
13
  end
13
14
 
14
15
  it "instantiates the factory and yields" do
@@ -4,18 +4,17 @@ require "spec_helpers"
4
4
 
5
5
  describe Wayfarer::Routing::DSL do
6
6
  subject(:root) { Wayfarer::Routing::RootRoute.new }
7
+
7
8
  let(:child) { root.children.first }
9
+ let(:path_finder) { build(:path_finder, url: "http://example.com/alpha/beta") }
8
10
 
9
11
  describe "#to" do
10
12
  it "adds a child with an always-matching matcher" do
11
- expect(root.to(:foobar)).to be_a(Wayfarer::Routing::Route)
12
- expect(child.action).to be(:foobar)
13
- expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::Custom)
14
- expect(child.parent).to be(root)
13
+ expect(root.to(:foobar)).to be(child)
15
14
 
16
- %w[http://example.com http://w3c.org http://google.com].each do |url|
17
- expect(child.matcher.match(url)).to be(true)
18
- end
15
+ expect(child.action(path_finder)).to be(:foobar)
16
+ expect(child.matcher).to be_nil
17
+ expect(child.parent).to be(root)
19
18
  end
20
19
  end
21
20
 
@@ -23,7 +22,7 @@ describe Wayfarer::Routing::DSL do
23
22
  it "adds a child with an URL matcher" do
24
23
  expect(root.url("http://example.com")).to be_a(Wayfarer::Routing::Route)
25
24
  expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::URL)
26
- expect(child.matcher.url).to eq("http://example.com")
25
+ expect(child.matcher.url.to_s).to eq("http://example.com")
27
26
  expect(child.parent).to be(root)
28
27
  end
29
28
  end
@@ -41,14 +40,14 @@ describe Wayfarer::Routing::DSL do
41
40
  it "adds a child with a path matcher" do
42
41
  expect(root.path("/foo/bar")).to be_a(Wayfarer::Routing::Route)
43
42
  expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::Path)
44
- expect(child.matcher.path).to eq("/foo/bar")
43
+ expect(child.matcher.pattern.expand).to eq("/foo/bar")
45
44
  expect(child.parent).to be(root)
46
45
  end
47
46
  end
48
47
 
49
48
  describe "#query" do
50
49
  it "adds a child with a query matcher" do
51
- expect(root.query(foo: "bar")).to be_a(Wayfarer::Routing::Route)
50
+ expect(root.query({ foo: "bar" })).to be_a(Wayfarer::Routing::Route)
52
51
  expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::Query)
53
52
  expect(child.matcher.fields).to eq({ foo: "bar" })
54
53
  expect(child.parent).to be(root)
@@ -64,28 +63,19 @@ describe Wayfarer::Routing::DSL do
64
63
  end
65
64
  end
66
65
 
67
- describe "#suffix" do
68
- it "adds a child with a suffix matcher" do
69
- expect(root.suffix(".png")).to be_a(Wayfarer::Routing::Route)
70
- expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::Suffix)
71
- expect(child.matcher.suffix).to eq(".png")
72
- expect(child.parent).to be(root)
73
- end
74
- end
75
-
76
66
  describe "#custom" do
77
67
  it "adds a child with a custom matcher" do
78
- expect(root.custom(->(_) { true })).to be_a(Wayfarer::Routing::Route)
68
+ expect(root.custom { |_url, root| root }).to be_a(Wayfarer::Routing::Route)
79
69
  expect(child.matcher).to be_a(Wayfarer::Routing::Matchers::Custom)
80
70
  expect(child.matcher.delegate).to be_a(Proc)
81
71
  expect(child.parent).to be(root)
82
72
  end
83
73
  end
84
74
 
85
- describe "Complex routing tree" do
75
+ describe "routing tree" do
86
76
  it "constructs the expected tree" do
87
77
  root.host "example.com", path: "/foo" do
88
- query foo: 4, bar: 2
78
+ query({ foo: 4, bar: 2 })
89
79
  end
90
80
 
91
81
  expect(root.children.count).to be(1)
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ RSpec.describe Wayfarer::Routing::HashStack do
6
+ subject(:hash_stack) { described_class.new(initial_state) }
7
+
8
+ let(:initial_state) { { foo: "bar" } }
9
+
10
+ describe "::empty" do
11
+ subject { described_class.empty.to_h }
12
+
13
+ it { is_expected.to be_empty }
14
+ end
15
+
16
+ describe "#to_h" do
17
+ it { is_expected.to have_attributes(to_h: { foo: "bar" }) }
18
+
19
+ context "with push" do
20
+ before { hash_stack.push(bar: "baz") }
21
+
22
+ it { is_expected.to have_attributes(to_h: { foo: "bar", bar: "baz" }) }
23
+ end
24
+
25
+ context "with multiple pushes" do
26
+ before do
27
+ hash_stack.push(bar: "baz")
28
+ hash_stack.push(baz: "qux")
29
+ end
30
+
31
+ it { is_expected.to have_attributes(to_h: { foo: "bar", bar: "baz", baz: "qux" }) }
32
+ end
33
+ end
34
+
35
+ describe "#push" do
36
+ before { hash_stack.push(bar: "baz") }
37
+
38
+ it "does not mutate previous stack entries" do
39
+ expect {
40
+ hash_stack.pop
41
+ }.to change(hash_stack, :to_h).from({ foo: "bar", bar: "baz" }).to({ foo: "bar" })
42
+ end
43
+ end
44
+
45
+ describe "#pop" do
46
+ context "with one element on the stack" do
47
+ before { hash_stack.pop }
48
+
49
+ it "raises when popping past the bottom" do
50
+ expect { hash_stack.pop }.to raise_error(described_class::EmptyStackError)
51
+ end
52
+ end
53
+
54
+ context "with multiple elements" do
55
+ before do
56
+ hash_stack.push(bar: "baz")
57
+ hash_stack.pop
58
+ end
59
+
60
+ it { is_expected.to have_attributes(to_h: { foo: "bar" }) }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe Wayfarer::Routing::DSL do # rubocop:disable RSpec/SpecFilePathFormat
6
+ let(:route) { Wayfarer::Routing::RootRoute.new }
7
+
8
+ shared_examples "routes" do |matches:, mismatches:|
9
+ def invoke(route, url)
10
+ route.invoke(build(:task, :uri, url: url))
11
+ end
12
+
13
+ matches.each do |url|
14
+ specify do
15
+ expect(invoke(route, url)).to be_a(Wayfarer::Routing::Result::Match)
16
+ end
17
+ end
18
+
19
+ mismatches.each do |url|
20
+ specify do
21
+ expect(invoke(route, url)).to be_a(Wayfarer::Routing::Result::Mismatch)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "root path matching" do
27
+ before { route.path "/" }
28
+
29
+ it_behaves_like "routes",
30
+ matches: ["https://example.com/"],
31
+ mismatches: ["https://example.com/foo"]
32
+ end
33
+
34
+ describe "host and path" do
35
+ before { route.host "example.com", path: "/foo" }
36
+
37
+ it_behaves_like "routes",
38
+ matches: ["https://example.com/foo"],
39
+ mismatches: ["https://example.com/bar"]
40
+ end
41
+
42
+ describe "query constraints" do
43
+ before do
44
+ route.host "example.com", path: "/foo" do
45
+ query({ page: 1..10 })
46
+ end
47
+ end
48
+
49
+ it_behaves_like "routes",
50
+ matches: ["https://example.com/foo?page=4"],
51
+ mismatches: [
52
+ "https://example.com/foo?page=11",
53
+ "https://example.com/foo",
54
+ "https://example.com?page=2"
55
+ ]
56
+ end
57
+
58
+ describe "multiple paths under host" do
59
+ before do
60
+ route.host "example.com" do
61
+ path "/contact"
62
+ path "/users/:id"
63
+ end
64
+ end
65
+
66
+ it_behaves_like "routes",
67
+ matches: [
68
+ "https://example.com/contact",
69
+ "https://example.com/users/123"
70
+ ],
71
+ mismatches: [
72
+ "https://example.com/contact/foo",
73
+ "https://example.com/users/123/images",
74
+ "https://example.com/users"
75
+ ]
76
+ end
77
+
78
+ describe "nested routing structure" do
79
+ before do
80
+ route.host "example.com" do
81
+ path "/contact"
82
+ path "/users/:user_id" do
83
+ path "/images/:id"
84
+ path "/friends"
85
+ end
86
+ end
87
+ end
88
+
89
+ it_behaves_like "routes",
90
+ matches: [
91
+ "https://example.com/contact",
92
+ "https://example.com/users/123/images/23",
93
+ "https://example.com/users/123/friends"
94
+ ],
95
+ mismatches: [
96
+ "https://example.com/users/123",
97
+ "https://example.com/users/123/images/23/foo",
98
+ "https://example.com/users/123/foobar"
99
+ ]
100
+ end
101
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::Custom do
7
+ subject(:matcher) { described_class.new(delegate) }
8
+
9
+ let(:url) { "https://example.com" }
10
+ let(:path_finder) { build(:path_finder, url: url) }
11
+
12
+ shared_examples "returns root" do
13
+ subject(:result) { matcher.evaluate(path_finder) }
14
+
15
+ it { is_expected.to be_a(Wayfarer::Routing::RootRoute) }
16
+ end
17
+
18
+ context "when matching" do
19
+ let(:delegate) { ->(route, _uri, _task) { route.to(:foobar) } }
20
+
21
+ it_behaves_like "returns root"
22
+ it_behaves_like "params", {}
23
+ end
24
+
25
+ context "when mismatching" do
26
+ let(:delegate) { ->(route, _uri, _task) { route } }
27
+
28
+ it_behaves_like "returns root"
29
+ it_behaves_like "params", {}
30
+ end
31
+
32
+ describe "#to_h" do
33
+ subject { matcher.to_h }
34
+
35
+ let(:delegate) { -> {} }
36
+
37
+ it { is_expected.to eq(custom: "Proc") }
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::Host do
7
+ # rubocop:disable RSpec/DescribedClass
8
+ subject(:matcher) { Wayfarer::Routing::Matchers::Host.new(str_or_regexp) }
9
+ # rubocop:enable RSpec/DescribedClass
10
+
11
+ let(:path_finder) { build(:path_finder, url: url) }
12
+
13
+ context String do
14
+ let(:str_or_regexp) { "example.com" }
15
+
16
+ context "when matching" do
17
+ let(:url) { "http://example.com/foo/bar" }
18
+
19
+ it_behaves_like "matches", true
20
+ it_behaves_like "params", {}
21
+ end
22
+
23
+ context "when mismatching" do
24
+ let(:url) { "http://google.com/bar/qux" }
25
+
26
+ it_behaves_like "matches", false
27
+ it_behaves_like "params", {}
28
+ end
29
+ end
30
+
31
+ context Regexp do
32
+ let(:str_or_regexp) { /example.com/ }
33
+
34
+ context "when matching" do
35
+ let(:url) { "http://sub.example.com" }
36
+
37
+ it_behaves_like "matches", true
38
+ it_behaves_like "params", {}
39
+ end
40
+
41
+ context "when mismatching" do
42
+ let(:url) { "http://example.sub.com" }
43
+
44
+ it_behaves_like "matches", false
45
+ it_behaves_like "params", {}
46
+ end
47
+ end
48
+
49
+ describe "#to_h" do
50
+ subject { matcher.to_h }
51
+
52
+ let(:str_or_regexp) { "" }
53
+
54
+ it { is_expected.to eq(name: str_or_regexp) }
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ RSpec.shared_examples "matches" do |expected|
6
+ subject(:result) { matcher.evaluate(path_finder) }
7
+
8
+ it { is_expected.to be(expected) }
9
+ end
10
+
11
+ RSpec.shared_examples "params" do |expected_params|
12
+ describe "params" do
13
+ subject { matcher.params(path_finder) }
14
+
15
+ it { is_expected.to eq(expected_params) }
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::Path do
7
+ subject(:matcher) { described_class.new(path) }
8
+
9
+ let(:route) { Wayfarer::Routing::RootRoute.new }
10
+ let(:path_finder) { build(:path_finder, url: "http://example.com/alpha/beta") }
11
+
12
+ context "with valid consumer state" do
13
+ let(:path) { ":foo/:bar" }
14
+ let(:route) { Wayfarer::Routing::RootRoute.new.path(path) }
15
+
16
+ before do
17
+ path_finder.enter(route)
18
+ end
19
+
20
+ it_behaves_like "matches", true
21
+ it_behaves_like "params", { "foo" => "alpha", "bar" => "beta" }
22
+ end
23
+
24
+ context "with invalid consumer state" do
25
+ let(:path) { ":foo/:bar/:qux" }
26
+ let(:route) { Wayfarer::Routing::RootRoute.new.path(path) }
27
+
28
+ before do
29
+ path_finder.enter(route)
30
+ end
31
+
32
+ it_behaves_like "matches", false
33
+ it_behaves_like "params", {}
34
+ end
35
+
36
+ describe "#to_h" do
37
+ subject { matcher.to_h }
38
+
39
+ let(:path) { "/" }
40
+
41
+ it { is_expected.to eq(pattern: path) }
42
+ end
43
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::Query do
7
+ # rubocop:disable RSpec/DescribedClass
8
+ subject(:matcher) { Wayfarer::Routing::Matchers::Query.new(fields) }
9
+ # rubocop:enable RSpec/DescribedClass
10
+
11
+ let(:path_finder) { build(:path_finder, url: url) }
12
+
13
+ context String do
14
+ let(:fields) { { arg: "foo" } }
15
+
16
+ context "when matching" do
17
+ let(:url) { "http://example.com?arg=foo" }
18
+
19
+ it_behaves_like "matches", true
20
+ it_behaves_like "params", { "arg" => "foo" }
21
+ end
22
+
23
+ context "when mismatching" do
24
+ let(:url) { "http://example.com?arg=bar" }
25
+
26
+ it_behaves_like "matches", false
27
+ it_behaves_like "params", {}
28
+ end
29
+ end
30
+
31
+ context Integer do
32
+ let(:fields) { { arg: 0 } }
33
+
34
+ context "when matching" do
35
+ let(:url) { "http://example.com?arg=0" }
36
+
37
+ it_behaves_like "matches", true
38
+ it_behaves_like "params", { "arg" => "0" }
39
+ end
40
+
41
+ %w[http://example.com?arg=1 http://example.com?arg=foo].each do |url|
42
+ context "when mismatching" do
43
+ let(:url) { url }
44
+
45
+ it_behaves_like "matches", false
46
+ it_behaves_like "params", {}
47
+ end
48
+ end
49
+ end
50
+
51
+ context Regexp do
52
+ let(:fields) { { arg: /foo/ } }
53
+
54
+ context "when matching" do
55
+ let(:url) { "http://example.com?arg=foobar" }
56
+
57
+ it_behaves_like "matches", true
58
+ it_behaves_like "params", { "arg" => "foobar" }
59
+ end
60
+
61
+ context "when mismatching" do
62
+ let(:url) { "http://example.com?arg=bar" }
63
+
64
+ it_behaves_like "matches", false
65
+ it_behaves_like "params", {}
66
+ end
67
+ end
68
+
69
+ context Range do
70
+ let(:fields) { { arg: 1..10 } }
71
+
72
+ context "when absent" do
73
+ let(:url) { "http://example.com" }
74
+
75
+ it_behaves_like "matches", false
76
+ it_behaves_like "params", {}
77
+ end
78
+
79
+ context "when matching" do
80
+ let(:url) { "http://example.com?arg=5" }
81
+
82
+ it_behaves_like "matches", true
83
+ it_behaves_like "params", { "arg" => "5" }
84
+ end
85
+
86
+ %w[http://example.com?arg=11 http://example.com?arg=foo&bar=qux].each do |url|
87
+ context "when mismatching" do
88
+ let(:url) { url }
89
+
90
+ it_behaves_like "matches", false
91
+ it_behaves_like "params", {}
92
+ end
93
+ end
94
+ end
95
+
96
+ context "with multiple fields" do
97
+ let(:fields) do
98
+ { foo: 1..5, bar: /baz/, qux: "zot", toto: 2 }
99
+ end
100
+
101
+ context "when matching" do
102
+ let(:url) { "http://example.com?foo=4&bar=bazqux&qux=zot&toto=2" }
103
+
104
+ it_behaves_like "matches", true
105
+ it_behaves_like "params", "foo" => "4", "bar" => "bazqux", "qux" => "zot", "toto" => "2"
106
+ end
107
+
108
+ context "when mismatching" do
109
+ let(:url) { "http://example.com?foo=bar&bar=qux&qux=6&toto=0" }
110
+
111
+ it_behaves_like "matches", false
112
+ it_behaves_like "params", {}
113
+ end
114
+ end
115
+
116
+ describe "#to_h" do
117
+ subject { matcher.to_h }
118
+
119
+ let(:fields) { { foo: "bar", bar: 42, qux: /foo/, zet: 1..3 } }
120
+
121
+ it { is_expected.to eq(foo: "bar", bar: 42, qux: "/foo/", zet: { min: 1, max: 3 }) }
122
+ end
123
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::Scheme do
7
+ # rubocop:disable RSpec/DescribedClass
8
+ subject(:matcher) { Wayfarer::Routing::Matchers::Scheme.new(scheme) }
9
+ # rubocop:enable RSpec/DescribedClass
10
+
11
+ let(:url) { "http://example.com" }
12
+ let(:path_finder) { build(:path_finder, url: url) }
13
+
14
+ context Symbol do
15
+ context "when matching" do
16
+ let(:scheme) { :http }
17
+
18
+ it_behaves_like "matches", true
19
+ it_behaves_like "params", {}
20
+ end
21
+
22
+ context "when mismatching" do
23
+ let(:scheme) { :https }
24
+
25
+ it_behaves_like "matches", false
26
+ it_behaves_like "params", {}
27
+ end
28
+ end
29
+
30
+ context String do
31
+ context "when matching" do
32
+ let(:scheme) { "http" }
33
+
34
+ it_behaves_like "matches", true
35
+ it_behaves_like "params", {}
36
+ end
37
+
38
+ context "when mismatching" do
39
+ let(:scheme) { "https" }
40
+
41
+ it_behaves_like "matches", false
42
+ it_behaves_like "params", {}
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+ require_relative "matcher"
5
+
6
+ describe Wayfarer::Routing::Matchers::URL do
7
+ subject(:matcher) { described_class.new(matcher_url) }
8
+
9
+ let(:url) { "http://example.com/foo/bar" }
10
+ let(:path_finder) { build(:path_finder, url: url) }
11
+
12
+ context "when matching" do
13
+ let(:matcher_url) { url }
14
+
15
+ it_behaves_like "matches", true
16
+ it_behaves_like "params", {}
17
+ end
18
+
19
+ context "when mismatching" do
20
+ let(:matcher_url) { "https://example.com" }
21
+
22
+ it_behaves_like "matches", false
23
+ it_behaves_like "params", {}
24
+ end
25
+
26
+ describe "#to_h" do
27
+ subject { matcher.to_h }
28
+
29
+ let(:matcher_url) { "https://example.com" }
30
+
31
+ it { is_expected.to eq(url: matcher_url) }
32
+ end
33
+ end