topoisomerase 0.1.0 → 0.1.1

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: b85e0899515e89773ce6a546b58fab50d4fb802d8752ca7f7836c254ea072dd5
4
- data.tar.gz: 0773b1feacf0423d6e14b8793e8dee8074bfb0e4e355611a2902a9e58d7b24b8
3
+ metadata.gz: cf5043b7dcc2f4ba70d7a034d42683de8975d67a0d2a92d51cab289b4f0b1996
4
+ data.tar.gz: 8d3ceca3787dbb577e9fb3e994a4180e5d5706c7cbc7ffd37c8238a7249131ab
5
5
  SHA512:
6
- metadata.gz: c3323e825f54fcb67243e7d763743ecd2337635a7c0e3a2d132e1264b7bba2c5f2c5ac21c9da1198bd8b49d3c913f6fdeb69d7697736666b0f43610e86258da8
7
- data.tar.gz: d00d0ff0f245067be77d161e66e2255364993777aa878eb107057b6ea98ca4146bb427450687f4278d45352f7e75ef974e5d30076245900e85e304ef03deacd3
6
+ metadata.gz: b8b8ad151f546440ee8d9a64122680d0a4177fec31536d944f1c4cbefb76d4f7e79f8c2d7fe2735768dccb6c3d9867893b5d9fc3e37d704e56e2253a9eb11bff
7
+ data.tar.gz: f3ed5723e43bb23279e084f5efec789c6352fb610bad3eb6b6024ea730e0798ceba8df0f1738e8c6bdb8ebff059a2be836a501bd5a152770c5900b8fe291cd3e
data/.gitignore CHANGED
@@ -8,6 +8,7 @@
8
8
  /tmp/
9
9
  /.idea/
10
10
  /stub/
11
+ Gemfile.lock
11
12
 
12
13
  # rspec failure tracking
13
14
  .rspec_status
data/ChangeLog ADDED
@@ -0,0 +1,4 @@
1
+ Version 0.1.1
2
+ * Use 'comments_added' to add comments matching a regular expression
3
+ Version 0.1.0
4
+ * Initial working of the gem making basic stubs for dynamic methods objects inheriting from a class
data/README.md CHANGED
@@ -34,7 +34,29 @@ Or install it yourself as:
34
34
 
35
35
  ## Usage
36
36
 
37
- TODO: Write usage instructions here
37
+ To create a stub file for a class, call the `create_stubs_for` passing that class.
38
+
39
+ E.g:
40
+ ```ruby
41
+ Topoisomerase.create_stubs_for BasicClass
42
+ ```
43
+
44
+ By default stubs will be created in the `stubs` folder.
45
+ This can be changed by setting the `stub_folder` variable.
46
+
47
+ ```ruby
48
+ Topoisomerase.stub_folder = 'custom_folder'
49
+ ```
50
+
51
+ Note that this will by default attempt to instantiate the class with ClassName.new. If parameters are
52
+ needed pass a block to the `create_stubs_for` as shown below. The result of the block will be the
53
+ instance of the class used in creating stubs.
54
+
55
+ ```ruby
56
+ Topoisomerase.create_stubs_for ParameterizedClass do
57
+ ParameterizedClass.new 5
58
+ end
59
+ ```
38
60
 
39
61
  ## Development
40
62
 
data/lib/topoisomerase.rb CHANGED
@@ -2,49 +2,92 @@ require 'topoisomerase/version'
2
2
  require 'method_source'
3
3
  require 'fileutils'
4
4
  require 'topoisomerase/core_ext/string'
5
+ require 'topoisomerase/comment_adder'
5
6
 
6
- # USE PRY instead
7
+ # Module for parsing and creating stubs for dynamic methods
7
8
  module Topoisomerase
8
9
  @stub_folder = 'stub'
10
+ @comments_added = {}
11
+ # @return [Object] Class to create stubs for
12
+ @class_name = nil
9
13
 
14
+ # For errors raised related to Topoisomerase
10
15
  class Error < StandardError; end
11
- # Your code goes here...
12
16
  class << self
13
17
  # @return [String] Folder where stubs are stored
14
18
  attr_accessor :stub_folder
19
+ # @todo Use custom object for this
20
+ # @return [Hash] Comments added to stubs generated grouped by inheriting class and then by
21
+ # message, matcher patterns
22
+ attr_accessor :comments_added
15
23
 
24
+ # @todo This is only covering 'define_method'. More methods will be added later
25
+ # @return [Boolean] Whether method is defined dynamically
16
26
  def dynamic_method?(method)
17
27
  source_code = method.source
18
28
  source_code.strip.start_with? 'define_method'
19
29
  end
20
30
 
21
31
  # @param [Object] class_name Class to retrieve dynamic methods for
22
- # @return [Hash]
32
+ # @return [Hash] Hash with dynamic methods as keys and values being the source, comment
33
+ # and location
23
34
  def dynamic_instance_methods(class_name)
24
- class_instance = if block_given?
25
- yield
26
- else
27
- class_name.new
28
- end
35
+ class_instance = block_given? ? yield(class_name) : class_name.new
29
36
  methods_hash = {}
30
37
  (class_instance.public_methods - Object.public_methods).each do |method|
31
38
  method_obj = class_instance.method(method)
32
- if dynamic_method? class_instance.method(method)
33
- methods_hash.merge!(method => { source: method_obj.source, comment: method_obj.comment,
34
- location: method_obj.source_location })
35
- end
39
+ next unless dynamic_method? class_instance.method(method)
40
+
41
+ methods_hash.merge!(method => { source: method_obj.source, comment: method_obj.comment,
42
+ location: method_obj.source_location,
43
+ parameters: method_obj.parameters.collect { |p| p.last.to_s } })
44
+ # TODO: May be worth checking required parameters and documenting non-required differently
45
+ # receiver - Shows object that will call method
46
+ # owner - shows class from which method belongs to
47
+ # parameters - Array with params and :req
36
48
  end
37
- # methods.each do |method|
38
- # method_obj = class_instance.method(method)
39
- # methods_hash.merge(method => { source: method_obj.source, comment: method_obj.comment,
40
- # location: method_obj.source_location })
41
- # end
42
49
  methods_hash
43
50
  end
44
51
 
52
+ # @return [String] Comment extracted from dynamic element
53
+ def extracted_comment(method_data)
54
+ method_data[:comment].strip.empty? ? '' : "Extracted comment #{method_data[:comment].strip}"
55
+ end
56
+
57
+ # @return [Boolean]
58
+ def match?(value_to_test, matcher_value)
59
+ value_to_test = value_to_test.to_s
60
+ case matcher_value
61
+ when Regexp then !value_to_test[matcher_value].nil?
62
+ when String then value_to_test == matcher_value
63
+ else
64
+ raise "Unknown matcher type for #{value_to_test}, value of #{matcher_value}"
65
+ end
66
+ end
67
+
68
+ # Reads from 'comments_added' variable if no comment added above dynamic method
69
+ # @return [String] Comment defined by 'comments_added'
70
+ def comment_for(method_to_stub, method_data)
71
+ return extracted_comment(method_data) unless comments_added[@inheriting_class]
72
+
73
+ comments_added[@inheriting_class].each do |comment_matcher|
74
+ matchers = comment_matcher[:matchers]
75
+ return ERB.new(comment_matcher[:message]).result(binding) if matchers.all? do |matcher_type, matcher_value|
76
+ extracted_match = case matcher_type
77
+ when :method_name then match?(method_to_stub, matcher_value)
78
+ when :source then match? method_data[:source], matcher_value
79
+ else
80
+ raise Topoisomerase::Error, "Undefined matcher #{matcher_type}"
81
+ end
82
+ extracted_match
83
+ end
84
+ end
85
+ ''
86
+ end
87
+
45
88
  # Create stub file for passed in class
46
89
  # @param [Object] class_name Object to create stub file for
47
- def create_stubs_for(class_name)
90
+ def create_stubs_for(class_name, inner_folder = nil)
48
91
  template = File.join(File.dirname(__FILE__), 'topoisomerase', 'stub_template.rb.erb')
49
92
  @class_name = class_name
50
93
  @class_instance = if block_given?
@@ -55,8 +98,9 @@ module Topoisomerase
55
98
  @dynamic_methods = dynamic_instance_methods(class_name) do
56
99
  @class_instance
57
100
  end
58
- FileUtils.mkdir_p stub_folder
59
- filename = File.join(stub_folder, "#{class_name.to_s.snakecase}.rb")
101
+ class_folder = inner_folder ? File.join(stub_folder, inner_folder) : stub_folder
102
+ FileUtils.mkdir_p class_folder
103
+ filename = File.join(class_folder, "#{class_name.to_s.snakecase}.rb")
60
104
  IO.write filename, ERB.new(File.read(template)).result(binding)
61
105
  `rubocop -a #{filename}`
62
106
  end
@@ -64,12 +108,13 @@ module Topoisomerase
64
108
  # Create stub files for each class that inherits from passed in class
65
109
  # @param [Object] inheriting_class Object to check for inheriting classes for
66
110
  def create_stubs_based_on(inheriting_class)
111
+ @inheriting_class = inheriting_class
67
112
  classes = ObjectSpace.each_object(Class).select { |class_name| class_name < inheriting_class }.reject do |class_name|
68
113
  class_name.to_s.split(':')[0] == inheriting_class.to_s
69
114
  end
70
115
  classes.each do |class_name|
71
116
  @class_instance = block_given? ? yield(class_name) : class_name.new
72
- create_stubs_for(class_name) { @class_instance }
117
+ create_stubs_for(class_name, inheriting_class.to_s.snakecase) { @class_instance }
73
118
  end
74
119
  end
75
120
  end
@@ -0,0 +1,40 @@
1
+ module Topoisomerase
2
+ # Class to set up comments that will be added to stubs for dynamic methods
3
+ class Comments
4
+ class << self
5
+ # @return [Object] Class for which stubs will be created from and for
6
+ # which rules to add comments for are defined
7
+ attr_accessor :inheriting_class
8
+
9
+ # Add a comment that is returned if ALL matchers are true
10
+ # @example Match a method ending with '_element' and include it's name without the '_element'
11
+ # comment "@return [Object] The '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
12
+ # method_name: /_element$/
13
+ # @param [String] comment Comment to add above class
14
+ def comment(comment, matchers)
15
+ Topoisomerase.comments_added[inheriting_class] << {
16
+ message: comment,
17
+ matchers: matchers
18
+ }
19
+ end
20
+
21
+ # Add comments according to matchers for an inheriting class
22
+ #
23
+ # The order in which they are added is the order in which matching will occur.
24
+ # The first comment that has a match will be used.
25
+ # If several matchers are used on 1 comment, all must be true to be considered a match
26
+ #
27
+ # @example Add a comment where method name ends with '=' and source contains '.text'
28
+ # Comments.add_for_class PageObject do
29
+ # comment 'Comment to add', method_name: /=$/, source: /.text/
30
+ # end
31
+ #
32
+ # @param [Object] inheriting_class Class for which stubs will be created from and for
33
+ # which rules to add comments for are defined
34
+ def add_for_class(inheriting_class, &script)
35
+ self.inheriting_class = inheriting_class
36
+ instance_eval(&script)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,13 +2,12 @@ require 'topoisomerase'
2
2
  # Stub for <%= @class_name %> created by 'topoisomerase'
3
3
  class <%= @class_name %>
4
4
  <% @dynamic_methods.each do |method_to_stub, method_data| %>
5
- # Defined at <%= method_data[:location] %>
6
- # Extracted comment <%= method_data[:comment].strip %>
7
- def <%= method_to_stub %>
8
- raise Topoisomerase::Error, "Method '<%= method_to_stub %>' called when it
5
+ # <%= comment_for(method_to_stub, method_data) %>
6
+ def <%= method_to_stub %> <%= method_data[:parameters].join(',') %>
7
+ # Defined at <%= method_data[:location] %>
8
+ raise Topoisomerase::Error, "Method '<%= method_to_stub %>' called with when it
9
9
  should not have been. Don't require stub files"
10
- # This is merely a stub
11
- # Contents is below
10
+ # This is merely a stub. Contents is below
12
11
  <%= method_data[:source] %>
13
12
  end
14
13
  <% end %>
@@ -0,0 +1,69 @@
1
+ # Load comments for page_object and create stubs for classes inheriting from it
2
+
3
+ # This blocks shows the manual addition of comments to the comments_added accessor
4
+ # Preferred way is to Topoisomerase::Comments.add_for_class
5
+ #
6
+ # Topoisomerase.comments_added[PageObject] = [
7
+ # {
8
+ # message: "@return [PageObject::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
9
+ # matchers: { method_name: /_element$/ } # If either matcher matches, result returned
10
+ # },
11
+ # {
12
+ # message: "@return [Boolean] Whether the '<%= method_to_stub.to_s.gsub('?', '') %>' element is present",
13
+ # matchers: { method_name: /\?$/ }
14
+ # },
15
+ # # Testing whether 2 matchers use AND
16
+ # {
17
+ # message: "Set the text of the '<%= method_to_stub.to_s.gsub('?', '') %>' text field\n # @param [String] _value Value to set",
18
+ # matchers: { method_name: /=$/, source: /text_field_value_set/ }
19
+ # },
20
+ # {
21
+ # message: "Set the value of '<%= method_to_stub.to_s.gsub('?', '') %>' element\n # @param [String] _value Value to set",
22
+ # matchers: { method_name: /=$/ }
23
+ # },
24
+ # {
25
+ # message: "@return [String] The text of the '<%= method_to_stub.to_s.gsub('?', '') %>' element",
26
+ # matchers: { source: /\.text/ }
27
+ # },
28
+ # {
29
+ # message: "Click the '<%= method_to_stub.to_s.gsub('?', '') %>' element",
30
+ # matchers: { source: /\.click/ }
31
+ # }
32
+ # ]
33
+
34
+
35
+ # message: "@return [Watir::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
36
+ # matchers: { method_name: /_element$/ } # If either matcher matches, result returned
37
+ # },
38
+
39
+ Topoisomerase::Comments.add_for_class PageObject do
40
+ comment "Url of the page. Used by 'goto' method and when using 'visit'", method_name: 'page_url_value'
41
+ comment 'Open the page on the browser', method_name: 'goto'
42
+ comment "@return [PageObject::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
43
+ method_name: /_element$/
44
+ end
45
+
46
+ # This block demonstrates how a real browser could be used to create stubs.
47
+ # This however should not be necessary and be resorted to only if the MockBrowser does not
48
+ # work
49
+ #
50
+ # @return [Array] List of Selenium options
51
+ # def options
52
+ # [args: %w[--disable-popup-blocking
53
+ # --no-sandbox --disable-dev-shm-usage],
54
+ # headless: true]
55
+ # end
56
+ #
57
+ # require 'watir'
58
+ # browser = Watir::Browser.new :chrome, *options
59
+ # browser.close
60
+
61
+ # Mock class just to make instantiating a page object with a browser happy
62
+ class MockBrowser
63
+ # Called by PageObject though not needed for documentation
64
+ def bridge; end
65
+ end
66
+
67
+ Topoisomerase.create_stubs_based_on PageObject do |inheriting_class|
68
+ inheriting_class.new(MockBrowser.new, false)
69
+ end
@@ -1,3 +1,4 @@
1
1
  module Topoisomerase
2
- VERSION = '0.1.0'.freeze
2
+ # @return [String] Version of the gem
3
+ VERSION = '0.1.1'.freeze
3
4
  end
data/todo.md ADDED
@@ -0,0 +1,4 @@
1
+ * Handle other ways of dynamically defining methods
2
+ * define_singleton_method
3
+ * instance_eval
4
+ * etc
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: topoisomerase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Garratt
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-08 00:00:00.000000000 Z
11
+ date: 2019-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: method_source
@@ -135,8 +135,8 @@ files:
135
135
  - ".rspec"
136
136
  - ".rubocop.yml"
137
137
  - CODE_OF_CONDUCT.md
138
+ - ChangeLog
138
139
  - Gemfile
139
- - Gemfile.lock
140
140
  - LICENSE.txt
141
141
  - README.md
142
142
  - Rakefile
@@ -144,10 +144,12 @@ files:
144
144
  - bin/setup
145
145
  - exe/topoisomerase
146
146
  - lib/topoisomerase.rb
147
+ - lib/topoisomerase/comment_adder.rb
147
148
  - lib/topoisomerase/core_ext/string.rb
148
- - lib/topoisomerase/override_define.rb
149
149
  - lib/topoisomerase/stub_template.rb.erb
150
+ - lib/topoisomerase/template/page_object.rb
150
151
  - lib/topoisomerase/version.rb
152
+ - todo.md
151
153
  - topoisomerase.gemspec
152
154
  homepage: https://gitlab.com/samuel-garratt/topoisomerase
153
155
  licenses:
data/Gemfile.lock DELETED
@@ -1,93 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- topoisomerase (0.1.0)
5
- method_source
6
- rubocop
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- ast (2.4.0)
12
- childprocess (1.0.1)
13
- rake (< 13.0)
14
- concurrent-ruby (1.1.5)
15
- data_magic (1.2)
16
- faker (>= 1.1.2)
17
- yml_reader (>= 0.6)
18
- diff-lcs (1.3)
19
- faker (1.9.3)
20
- i18n (>= 0.7)
21
- i18n (1.6.0)
22
- concurrent-ruby (~> 1.0)
23
- jaro_winkler (1.5.2)
24
- method_source (0.9.2)
25
- mini_portile2 (2.4.0)
26
- nokogiri (1.10.3)
27
- mini_portile2 (~> 2.4.0)
28
- page-object (2.2.5)
29
- page_navigation (>= 0.10)
30
- selenium-webdriver (~> 3.0)
31
- watir (~> 6.8)
32
- page_navigation (0.10)
33
- data_magic (>= 0.22)
34
- parallel (1.17.0)
35
- parser (2.6.0.0)
36
- ast (~> 2.4.0)
37
- powerpack (0.1.2)
38
- psych (3.1.0)
39
- rainbow (3.0.0)
40
- rake (10.5.0)
41
- regexp_parser (1.3.0)
42
- rspec (3.8.0)
43
- rspec-core (~> 3.8.0)
44
- rspec-expectations (~> 3.8.0)
45
- rspec-mocks (~> 3.8.0)
46
- rspec-core (3.8.0)
47
- rspec-support (~> 3.8.0)
48
- rspec-expectations (3.8.3)
49
- diff-lcs (>= 1.2.0, < 2.0)
50
- rspec-support (~> 3.8.0)
51
- rspec-mocks (3.8.0)
52
- diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.8.0)
54
- rspec-support (3.8.0)
55
- rubocop (0.65.0)
56
- jaro_winkler (~> 1.5.1)
57
- parallel (~> 1.10)
58
- parser (>= 2.5, != 2.5.1.1)
59
- powerpack (~> 0.1)
60
- psych (>= 3.1.0)
61
- rainbow (>= 2.2.2, < 4.0)
62
- ruby-progressbar (~> 1.7)
63
- unicode-display_width (~> 1.4.0)
64
- ruby-progressbar (1.10.0)
65
- rubyzip (1.2.2)
66
- selenium-webdriver (3.142.0)
67
- childprocess (>= 0.5, < 2.0)
68
- rubyzip (~> 1.2, >= 1.2.2)
69
- unicode-display_width (1.4.1)
70
- watir (6.16.5)
71
- regexp_parser (~> 1.2)
72
- selenium-webdriver (~> 3.6)
73
- webdrivers (3.8.0)
74
- nokogiri (~> 1.6)
75
- rubyzip (~> 1.0)
76
- selenium-webdriver (~> 3.0)
77
- yard (0.9.19)
78
- yml_reader (0.7)
79
-
80
- PLATFORMS
81
- ruby
82
-
83
- DEPENDENCIES
84
- bundler (~> 2.0)
85
- page-object
86
- rake
87
- rspec (~> 3.0)
88
- topoisomerase!
89
- webdrivers
90
- yard
91
-
92
- BUNDLED WITH
93
- 2.0.1
@@ -1,13 +0,0 @@
1
- # This is very dangerous. Overriding define_method.
2
- # Only to be used for specific task of documenting dynamic methods used
3
- class Object
4
- def self.define_method(method_name)
5
- Topoisomerase.dynamic_methods[self] = method_name
6
- end
7
-
8
- # I don't think I should include this. Safer and makes sense
9
- # just to focus on definitions in custom classes
10
- # def define_method(method_name)
11
- # Topoisomerase.dynamic_methods[self] = method_name
12
- # end
13
- end