Module: Topoisomerase

Defined in:
lib/topoisomerase.rb,
lib/topoisomerase/version.rb,
lib/topoisomerase/comments.rb

Overview

Module for parsing and creating stubs for dynamic methods

Defined Under Namespace

Classes: Comments, Error

Constant Summary collapse

VERSION =

Returns Version of the gem.

Returns:

  • (String)

    Version of the gem

'0.1.5'.freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.comments_addedHash

TODO:

Use custom object for this

Returns Comments added to stubs generated grouped by inheriting class and then by message, matcher patterns.

Returns:

  • (Hash)

    Comments added to stubs generated grouped by inheriting class and then by message, matcher patterns



23
24
25
# File 'lib/topoisomerase.rb', line 23

def comments_added
  @comments_added
end

.stub_folderString

Returns Folder where stubs are stored.

Returns:

  • (String)

    Folder where stubs are stored



19
20
21
# File 'lib/topoisomerase.rb', line 19

def stub_folder
  @stub_folder
end

Class Method Details

.comment_for(stub_method, method_data) ⇒ String

Reads from ‘comments_added’ variable if no comment added above dynamic method

Returns:

  • (String)

    Comment defined by ‘comments_added’



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/topoisomerase.rb', line 71

def comment_for(stub_method, method_data)
  return extracted_comment(method_data) unless comments_added[@inheriting_class]

  stub_method = stub_method.to_s
  comments_added[@inheriting_class].each do |comment_matcher|
    matchers = comment_matcher[:matchers]
    return ERB.new(comment_matcher[:message]).result(binding) if matchers.all? do |matcher_type, matcher_value|
      extracted_match = case matcher_type
                        when :method_name then match?(stub_method, matcher_value)
                        when :source then match? method_data[:source], matcher_value
                        else
                          raise Topoisomerase::Error, "Undefined matcher #{matcher_type}"
                        end
      extracted_match
    end
  end
  ''
end

.create_stubs_based_on(inheriting_class) ⇒ Object

Create stub files for each class that inherits from passed in class

Parameters:

  • inheriting_class (Object)

    Object to check for inheriting classes for



110
111
112
113
114
115
116
117
118
119
# File 'lib/topoisomerase.rb', line 110

def create_stubs_based_on(inheriting_class)
  @inheriting_class = inheriting_class
  classes = ObjectSpace.each_object(Class).select { |class_name| class_name < inheriting_class }.reject do |class_name|
    class_name.to_s.split(':')[0] == inheriting_class.to_s
  end
  classes.each do |class_name|
    @class_instance = block_given? ? yield(class_name) : class_name.new
    create_stubs_for(class_name, inheriting_class.to_s.snakecase) { @class_instance }
  end
end

.create_stubs_for(class_name, inner_folder = nil) ⇒ Object

Create stub file for passed in class

Parameters:

  • class_name (Object)

    Object to create stub file for



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/topoisomerase.rb', line 92

def create_stubs_for(class_name, inner_folder = nil)
  template = File.join(File.dirname(__FILE__), 'topoisomerase', 'stub_template.rb.erb')
  @class_name = class_name
  @class_instance = block_given? ? yield(class_name) : class_name.new
  @dynamic_methods = dynamic_instance_methods(class_name) do
    @class_instance
  end
  class_folder = inner_folder ? File.join(stub_folder, inner_folder) : stub_folder
  filename = File.join(class_folder, "#{class_name.to_s.snakecase}.rb")
  puts "Creating stubs for #{class_name} at #{filename}"
  FileUtils.mkdir_p File.dirname(filename)
  IO.write filename, ERB.new(File.read(template)).result(binding)
  RuboCop::CLI.new.run(['-a', filename, '-o', 'stub_log'])
  FileUtils.rm 'stub_log' # TODO: Should be a way of not producing this in first place
end

.dynamic_instance_methods(class_name) ⇒ Hash

Returns Hash with dynamic methods as keys and values being the source, comment and location.

Parameters:

  • class_name (Object)

    Class to retrieve dynamic methods for

Returns:

  • (Hash)

    Hash with dynamic methods as keys and values being the source, comment and location



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/topoisomerase.rb', line 35

def dynamic_instance_methods(class_name)
  class_instance = block_given? ? yield(class_name) : class_name.new
  methods_hash = {}
  (class_instance.public_methods - Object.public_methods).each do |method|
    method_obj = class_instance.method(method)
    next unless dynamic_method? class_instance.method(method)

    methods_hash.merge!(method => { source: method_obj.source, comment: method_obj.comment,
                                    location: method_obj.source_location,
                                    parameters: method_obj.parameters.collect { |p| p.last.to_s } })
    # TODO: May be worth checking required parameters and documenting non-required differently
    # receiver - Shows object that will call method
    # owner - shows class from which method belongs to
    # parameters - Array with params and :req
  end
  methods_hash
end

.dynamic_method?(method) ⇒ Boolean

TODO:

This is only covering ‘define_method’. More methods will be added later

Returns Whether method is defined dynamically.

Returns:

  • (Boolean)

    Whether method is defined dynamically



27
28
29
30
# File 'lib/topoisomerase.rb', line 27

def dynamic_method?(method)
  source_code = method.source
  source_code.strip.start_with? 'define_method'
end

.extracted_comment(method_data) ⇒ String

Returns Comment extracted from dynamic element.

Returns:

  • (String)

    Comment extracted from dynamic element



54
55
56
# File 'lib/topoisomerase.rb', line 54

def extracted_comment(method_data)
  method_data[:comment].strip.empty? ? '' : "Extracted comment #{method_data[:comment].strip}"
end

.match?(value_to_test, matcher_value) ⇒ Boolean

Returns Whether there is a match based on value and matcher.

Returns:

  • (Boolean)

    Whether there is a match based on value and matcher



59
60
61
62
63
64
65
66
67
# File 'lib/topoisomerase.rb', line 59

def match?(value_to_test, matcher_value)
  value_to_test = value_to_test.to_s
  case matcher_value
  when Regexp then !value_to_test[matcher_value].nil?
  when String then value_to_test == matcher_value
  else
    raise "Unknown matcher type for #{value_to_test}, value of #{matcher_value}"
  end
end