with_clues 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9250314165c2529d8ac1a12ccc085a2abf2098f057b5778b1544c34dff21f9c
4
- data.tar.gz: 9f737b4264d64955b17174ac44c9336d8dfa8fe79152b00cf96447b48ac15208
3
+ metadata.gz: 270e2b6f2af29d8d7871cba1103db16ec84efd97380acb4592b094729adfb994
4
+ data.tar.gz: 22de0039993571e0a491cbe9ec5d1b53021d184793d99bfc47ffb34d64ca833a
5
5
  SHA512:
6
- metadata.gz: 9bf1631b167e606916d8d7e87bba3236bf829c0614c722b05fc5ac99c103cd96fb44f4c1e548bbcdbfcc71205e3661ba9d9d58f0a844cf7c004dc618174ab253
7
- data.tar.gz: cdb5e7836e91a48764ad70afeeed9f1aeaf872b3cf748c4663431e522cfc7f4006a00045ab7a03b7fa905d9865f5442dba5621b0e47172fa3c18b4738d1022fe
6
+ metadata.gz: f019bd0a5354f762b4a7304d88509054fe53078528c188e8186aa8af134ff40d99042b0c3556ea2c2497317e0d899e764a6649e78f22ed1aea53e5301ee83c6f
7
+ data.tar.gz: d106562538a1cd625d422e1e500aa34564c5f1c97bfeea30fa6083c7e7f1b99d4addf59f7046c29575122f0b43a15c61b7efefc6fba16ef8a0aeec0107b15148
data/.circleci/config.yml CHANGED
@@ -1,35 +1,98 @@
1
- version: 2.1
2
- orbs:
3
- # See https://circleci.com/developer/orbs/orb/circleci/ruby
4
- ruby: circleci/ruby@1.1.2
5
- jobs: # keyword
6
- test: # my name for the job
7
- parameters: # keyword
8
- ruby-version: # my parameter name
9
- type: string # type is a keyword
10
- docker: # keyword
11
- - image: cimg/base:stable
12
- steps: # keyword
13
- - checkout # magic name
14
- - ruby/install: # ruby/ is from the orb name, install is a command in that orb
15
- version: << parameters.ruby-version >> # magic nonsense for param subst
16
- - run:
17
- command: "bin/setup"
18
- - run:
19
- name: "Create the test results directory because you can't just store_test_results with a file and if you do you do not get any sort of error because wtf is with this platform?"
20
- command: mkdir -p /tmp/test-results
21
- - run:
22
- command: bin/ci /tmp/test-results/rspec_results.xml
23
- - store_test_results: # store_test_results is magic from circle
24
- path: /tmp/test-results # path is a param to store_test_results and it must be a directory not a file
25
- - store_artifacts: # store_artifacts is magic from circle
26
- path: /tmp/test-results # path is the param to store_artifacts
27
- workflows: # keyword
28
- all-rubies: # my name for the workflow
29
- jobs: # keyword
30
- - test: # my name for the job
31
- matrix: # keyword
32
- parameters: # keyword
33
- # All rubies being maintained per this page:
34
- # https://www.ruby-lang.org/en/downloads/branches/
35
- ruby-version: [ "2.5", "2.6", "2.7", "3.0" ]
1
+ # THIS IS GENERATED - DO NOT EDIT
2
+ # regenerate with bin/mk_circle_config
3
+ # You are very welcome
4
+ ---
5
+ version: '2.1'
6
+ jobs:
7
+ ruby__2_6:
8
+ docker:
9
+ - image: cimg/ruby:2.6
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: Setup for build
14
+ command: bin/setup
15
+ - run:
16
+ name: Ensure bin/setup is idempotent
17
+ command: bin/setup
18
+ - run:
19
+ name: Create the test results dir
20
+ command: mkdir -p /tmp/test-results/2.6
21
+ - run:
22
+ name: Run all tests
23
+ command: bin/ci /tmp/test-results/2.6/rspec_results.xml
24
+ - store_test_results:
25
+ path: "/tmp/test-results/2.6"
26
+ - store_artifacts:
27
+ path: "/tmp/test-results/2.6"
28
+ ruby__2_7:
29
+ docker:
30
+ - image: cimg/ruby:2.7
31
+ steps:
32
+ - checkout
33
+ - run:
34
+ name: Setup for build
35
+ command: bin/setup
36
+ - run:
37
+ name: Ensure bin/setup is idempotent
38
+ command: bin/setup
39
+ - run:
40
+ name: Create the test results dir
41
+ command: mkdir -p /tmp/test-results/2.7
42
+ - run:
43
+ name: Run all tests
44
+ command: bin/ci /tmp/test-results/2.7/rspec_results.xml
45
+ - store_test_results:
46
+ path: "/tmp/test-results/2.7"
47
+ - store_artifacts:
48
+ path: "/tmp/test-results/2.7"
49
+ ruby__3_0:
50
+ docker:
51
+ - image: cimg/ruby:3.0
52
+ steps:
53
+ - checkout
54
+ - run:
55
+ name: Setup for build
56
+ command: bin/setup
57
+ - run:
58
+ name: Ensure bin/setup is idempotent
59
+ command: bin/setup
60
+ - run:
61
+ name: Create the test results dir
62
+ command: mkdir -p /tmp/test-results/3.0
63
+ - run:
64
+ name: Run all tests
65
+ command: bin/ci /tmp/test-results/3.0/rspec_results.xml
66
+ - store_test_results:
67
+ path: "/tmp/test-results/3.0"
68
+ - store_artifacts:
69
+ path: "/tmp/test-results/3.0"
70
+ ruby__3_1:
71
+ docker:
72
+ - image: cimg/ruby:3.1
73
+ steps:
74
+ - checkout
75
+ - run:
76
+ name: Setup for build
77
+ command: bin/setup
78
+ - run:
79
+ name: Ensure bin/setup is idempotent
80
+ command: bin/setup
81
+ - run:
82
+ name: Create the test results dir
83
+ command: mkdir -p /tmp/test-results/3.1
84
+ - run:
85
+ name: Run all tests
86
+ command: bin/ci /tmp/test-results/3.1/rspec_results.xml
87
+ - store_test_results:
88
+ path: "/tmp/test-results/3.1"
89
+ - store_artifacts:
90
+ path: "/tmp/test-results/3.1"
91
+ workflows:
92
+ version: 2
93
+ all_rubies:
94
+ jobs:
95
+ - ruby__2_6
96
+ - ruby__2_7
97
+ - ruby__3_0
98
+ - ruby__3_1
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.1.0
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ See [https://github.com/sustainable-rails/with\_clues/releases](https://github.com/sustainable-rails/with_clues/releases)
data/README.md CHANGED
@@ -107,13 +107,15 @@ There are three clues included:
107
107
  `with_clues` is intended as a diagnostic tool you can develop and enhance over time. As your team writes more code or develops
108
108
  more conventions, you can develop diagnostics as well.
109
109
 
110
- To add one, create a class that implements `dump(notifier, context:)`:
110
+ To add one, create a class that implements `dump(notifier, context:)` or `dump(notifier, context:, page:)`:
111
111
 
112
112
  * `notifier` is a `WithClues::Notifier` that you should use to produce output:
113
113
  * `notify` - output text, preceded with `[ with_clues ]` (this is so you can tell output from your code vs from `with_clues`)
114
114
  * `blank_line` - a blank line (no prefix)
115
115
  * `notify_raw` - output text without a prefix, useful for removing ambiguity about what is being output
116
116
  * `context:` the context passed into `with_clues` (nil if it was omitted)
117
+ * `page:` If `dump` requires this keyword, your clue will only be used in a browser context when the Capybara `page` object is available.
118
+ In that case, that is what is passed in.
117
119
 
118
120
  For example, suppose you want to output information about an Active Record like so:
119
121
 
@@ -153,6 +155,9 @@ WithClues::Method.use_custom_clue ActiveRecordClues
153
155
 
154
156
  You can use multiple clues by repeatedly calling `use_custom_clue`
155
157
 
158
+ Note that if your clue implements the three-arg version of `dump` ( `dump(notifier, context:, page:)` ), it will *only* be used when in
159
+ a context where Capybara's `page` element in in play.
160
+
156
161
  ## Developing
157
162
 
158
163
  * Get set up with `bin/setup`
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ circle_config = {
7
+ "version" => "2.1",
8
+ "jobs" => {},
9
+ "workflows" => {
10
+ "version" => 2,
11
+ "all_rubies" => {
12
+ "jobs" => [
13
+ ],
14
+ },
15
+ }
16
+ }
17
+
18
+ supported_rubies = [
19
+ "2.6",
20
+ "2.7",
21
+ "3.0",
22
+ "3.1",
23
+ ]
24
+
25
+ supported_rubies.each do |ruby_verison|
26
+
27
+ test_results_dir = "/tmp/test-results/#{ruby_verison}"
28
+ job_name = "ruby__#{ruby_verison.gsub(/\./,"_")}"
29
+
30
+ job = {
31
+ "docker" => [
32
+ {
33
+ "image" => "cimg/ruby:#{ruby_verison}",
34
+ }
35
+ ],
36
+ "steps" => [
37
+ "checkout",
38
+ {
39
+ "run" => {
40
+ "name" => "Setup for build",
41
+ "command" => "bin/setup",
42
+ }
43
+ },
44
+ {
45
+ "run" => {
46
+ "name" => "Ensure bin/setup is idempotent",
47
+ "command" => "bin/setup",
48
+ }
49
+ },
50
+ {
51
+ "run" => {
52
+ "name" => "Create the test results dir",
53
+ "command" => "mkdir -p #{test_results_dir}",
54
+ }
55
+ },
56
+ {
57
+ "run" => {
58
+ "name" => "Run all tests",
59
+ "command" => "bin/ci #{test_results_dir}/rspec_results.xml",
60
+ }
61
+ },
62
+ {
63
+ "store_test_results" => {
64
+ "path" => test_results_dir,
65
+ }
66
+ },
67
+ {
68
+ "store_artifacts" => {
69
+ "path" => test_results_dir,
70
+ }
71
+ },
72
+ ]
73
+ }
74
+ circle_config["jobs"][job_name] = job
75
+ circle_config["workflows"]["all_rubies"]["jobs"] << job_name
76
+ end
77
+
78
+ circle_config_file = (Pathname(__FILE__).dirname / ".." / ".circleci" / "config.yml").expand_path
79
+ File.open(circle_config_file,"w") do |file|
80
+ file.puts "# THIS IS GENERATED - DO NOT EDIT"
81
+ file.puts "# regenerate with bin/mk_circle_config"
82
+ file.puts "# You are very welcome"
83
+ file.puts circle_config.to_yaml
84
+ end
@@ -6,24 +6,35 @@ module WithClues
6
6
  return
7
7
  end
8
8
  if page.driver.respond_to?(:browser)
9
- if page.driver.browser.respond_to?(:manage)
10
- if page.driver.browser.manage.respond_to?(:logs)
11
- logs = page.driver.browser.manage.logs
12
- browser_logs = logs.get(:browser)
13
- notifier.notify "BROWSER LOGS {"
14
- browser_logs.each do |log|
15
- notifier.notify_raw log.message
16
- end
17
- notifier.notify "} END BROWSER LOGS"
18
- else
19
- notifier.notify "NO BROWSER LOGS: page.driver.browser.manage #{page.driver.browser.manage.class} does not respond to #logs"
9
+ logs = locate_logs(page.driver.browser, notifier: notifier)
10
+ if !logs.nil?
11
+ browser_logs = logs.get(:browser)
12
+ notifier.notify "BROWSER LOGS {"
13
+ browser_logs.each do |log|
14
+ notifier.notify_raw log.message
20
15
  end
21
- else
22
- notifier.notify "NO BROWSER LOGS: page.driver.browser #{page.driver.browser.class} does not respond to #manage"
16
+ notifier.notify "} END BROWSER LOGS"
23
17
  end
24
18
  else
25
- notifier.notify "NO BROWSER LOGS: page.driver #{page.driver.class} does not respond to #browser"
19
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver #{page.driver.class} does not respond to #browser"
26
20
  end
27
21
  end
22
+
23
+ private
24
+
25
+ def locate_logs(browser, notifier:)
26
+ if browser.respond_to?(:logs)
27
+ return browser.logs
28
+ elsif browser.respond_to?(:manage)
29
+ if browser.manage.respond_to?(:logs)
30
+ return browser.manage.logs
31
+ end
32
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver.browser.manage #{browser.manage.class} does not respond to #logs"
33
+ else
34
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver.browser #{browser.class} does not respond to #manage or #logs"
35
+ end
36
+ nil
37
+ end
38
+
28
39
  end
29
40
  end
@@ -1,14 +1,16 @@
1
1
  module WithClues
2
2
  class Html
3
3
  def dump(notifier, page:, context:)
4
- if !page.respond_to?(:html)
5
- notifier.notify "Something may be wrong. page (#{page.class}) does not respond to #html"
6
- return
7
- end
8
4
  notifier.blank_line
9
5
  notifier.notify "HTML {"
10
6
  notifier.blank_line
11
- notifier.notify_raw page.html
7
+ if page.respond_to?(:html)
8
+ notifier.notify_raw page.html
9
+ elsif page.respond_to?(:native)
10
+ notifier.notify_raw page.native
11
+ else
12
+ notifier.notify "[!] Something may be wrong. page (#{page.class}) does not respond to #html or #native"
13
+ end
12
14
  notifier.blank_line
13
15
  notifier.notify "} END HTML"
14
16
  end
@@ -1,6 +1,7 @@
1
1
  require_relative "html"
2
2
  require_relative "browser_logs"
3
3
  require_relative "notifier"
4
+ require_relative "private/custom_clue_method_analysis"
4
5
 
5
6
  module WithClues
6
7
  module Method
@@ -23,18 +24,25 @@ module WithClues
23
24
  @@clue_classes[:custom].each do |klass|
24
25
  klass.new.dump(notifier, context: context)
25
26
  end
26
- if !defined?(page)
27
- raise ex
28
- end
29
- notifier.notify "Test failed: #{ex.message}"
30
- @@clue_classes[:require_page].each do |klass|
31
- klass.new.dump(notifier, context: context, page: page)
27
+ if defined?(page)
28
+ notifier.notify "Test failed: #{ex.message}"
29
+ @@clue_classes[:require_page].each do |klass|
30
+ klass.new.dump(notifier, context: context, page: page)
31
+ end
32
32
  end
33
33
  raise ex
34
34
  end
35
35
 
36
36
  def self.use_custom_clue(klass)
37
- @@clue_classes[:custom] << klass
37
+ dump_method = klass.instance_method(:dump)
38
+ analysis = WithClues::Private::CustomClueMethodAnalysis.from_method(dump_method)
39
+ if analysis.standard_implementation?
40
+ @@clue_classes[:custom] << klass
41
+ elsif analysis.requires_page_object?
42
+ @@clue_classes[:require_page] << klass
43
+ else
44
+ analysis.raise_exception!
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -0,0 +1,139 @@
1
+ module WithClues
2
+ module Private
3
+ class CustomClueMethodAnalysis
4
+
5
+ def self.from_method(unbound_method)
6
+
7
+ params = unbound_method.parameters.map { |param_array| Param.new(param_array) }
8
+
9
+ if params.size == 2
10
+ two_arg_method = TwoArgMethod.new(params)
11
+ if two_arg_method.valid?
12
+ return StandardImplementation.new
13
+ end
14
+
15
+ return BadParams.new(two_arg_method.errors)
16
+
17
+ elsif params.size == 3
18
+ three_arg_method = ThreeArgMethod.new(params)
19
+ if three_arg_method.valid?
20
+ return RequiresPageObject.new
21
+ end
22
+ return BadParams.new(three_arg_method.errors)
23
+ end
24
+
25
+ BadParams.new([])
26
+ end
27
+
28
+ def standard_implementation?
29
+ false
30
+ end
31
+
32
+ def requires_page_object?
33
+ false
34
+ end
35
+
36
+ def raise_exception!
37
+ raise StandardError.new("Unimplemented condition found inside #from_method")
38
+ end
39
+
40
+ class Param
41
+
42
+ def initialize(method_param_array)
43
+ @type = method_param_array[0]
44
+ @name = method_param_array[1]
45
+
46
+ end
47
+
48
+ def required?
49
+ @type == :req
50
+ end
51
+ def keyword_required?
52
+ @type == :keyreq
53
+ end
54
+
55
+ def named?(*allowed_names)
56
+ allowed_names.include?(@name)
57
+ end
58
+ def name
59
+ if self.keyword_required?
60
+ "#{@name}:"
61
+ else
62
+ @name
63
+ end
64
+ end
65
+ end
66
+
67
+ class TwoArgMethod
68
+ attr_reader :errors
69
+ def initialize(params)
70
+ @errors = []
71
+ if !params[0].required?
72
+ @errors << "Param 1, #{params[0].name}, is not required"
73
+ end
74
+ require_keyword(2,params[1])
75
+ end
76
+
77
+ def valid?
78
+ @errors.empty?
79
+ end
80
+ private
81
+
82
+ def require_keyword(param_number, param)
83
+ if !param.keyword_required?
84
+ @errors << "Param #{param_number}, #{param.name}, is not a required keyword param"
85
+ end
86
+ if !param.named?(*allowed_names)
87
+ @errors << "Param #{param_number}, #{param.name}, should be named context:"
88
+ end
89
+ end
90
+
91
+ def allowed_names
92
+ [ :context ]
93
+ end
94
+ end
95
+
96
+ class ThreeArgMethod < TwoArgMethod
97
+ def initialize(params)
98
+ super(params)
99
+ require_keyword(3,params[2])
100
+ end
101
+ private
102
+ def allowed_names
103
+ [ :context, :page ]
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ class GoodParams < CustomClueMethodAnalysis
110
+ def raise_exception!
111
+ raise StandardError.new("You should not have called .exception on a #{self.class.name}")
112
+ end
113
+ end
114
+
115
+ class RequiresPageObject < CustomClueMethodAnalysis
116
+ def requires_page_object?
117
+ true
118
+ end
119
+ end
120
+
121
+ class StandardImplementation < CustomClueMethodAnalysis
122
+ def standard_implementation?
123
+ true
124
+ end
125
+ end
126
+
127
+ class BadParams < CustomClueMethodAnalysis
128
+ def initialize(errors)
129
+ @message = errors.empty? ? DEFAULT_ERROR : errors.join(", ")
130
+ end
131
+
132
+ DEFAULT_ERROR = "dump must take one required param, one keyword param named context: and an optional keyword param named page:"
133
+
134
+ def raise_exception!
135
+ raise NameError.new(@message)
136
+ end
137
+ end
138
+ end
139
+ end
@@ -1,3 +1,3 @@
1
1
  module WithClues
2
- VERSION="1.0.0"
2
+ VERSION="1.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_clues
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Copeland
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-20 00:00:00.000000000 Z
11
+ date: 2022-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -76,6 +76,8 @@ files:
76
76
  - ".circleci/config.yml"
77
77
  - ".gitignore"
78
78
  - ".rspec"
79
+ - ".tool-versions"
80
+ - CHANGELOG.md
79
81
  - CODE_OF_CONDUCT.md
80
82
  - CONTRIBUTING.md
81
83
  - Gemfile
@@ -84,6 +86,7 @@ files:
84
86
  - Rakefile
85
87
  - bin/ci
86
88
  - bin/console
89
+ - bin/mk_circle_config
87
90
  - bin/mk_gem
88
91
  - bin/rake
89
92
  - bin/rspec
@@ -93,6 +96,7 @@ files:
93
96
  - lib/with_clues/html.rb
94
97
  - lib/with_clues/method.rb
95
98
  - lib/with_clues/notifier.rb
99
+ - lib/with_clues/private/custom_clue_method_analysis.rb
96
100
  - lib/with_clues/version.rb
97
101
  - with_clues.gemspec
98
102
  homepage: https://sustainable-rails.com
@@ -117,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
121
  - !ruby/object:Gem::Version
118
122
  version: '0'
119
123
  requirements: []
120
- rubygems_version: 3.1.2
124
+ rubygems_version: 3.3.3
121
125
  signing_key:
122
126
  specification_version: 4
123
127
  summary: WTF does this do?