peeky 0.0.15 → 0.0.31

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,11 +20,8 @@ module Peeky
20
20
  # type of the parameter
21
21
  attr_accessor :type
22
22
 
23
- # ruby code format when used in a signature
24
- attr_accessor :signature_format
25
-
26
- # minimal required usage in a call to the method with this paramater
27
- attr_accessor :minimal_call_format
23
+ # default value for positional or keyed parameters
24
+ attr_accessor :default_value
28
25
 
29
26
  def initialize(param)
30
27
  map(param)
@@ -58,47 +55,125 @@ module Peeky
58
55
  puts "minimal_call_format : #{minimal_call_format}"
59
56
  end
60
57
 
58
+ # ruby code formatted for use in a method signature
59
+ def signature_format
60
+ @_signature_format ||= begin
61
+ method_name = "signature_format_#{@type}".to_sym
62
+
63
+ m = method(method_name)
64
+ m.call
65
+ end
66
+ end
67
+
68
+ # minimal required usage in a call to the method with this paramater
69
+ def minimal_call_format
70
+ @_minimal_call_format ||= begin
71
+ method_name = "minimal_call_format_#{@type}".to_sym
72
+
73
+ if respond_to?(method_name, true)
74
+ m = method(method_name)
75
+ m.call
76
+ else
77
+ minimal_call_format_ignore
78
+ end
79
+ end
80
+ end
81
+
61
82
  private
62
83
 
63
84
  # Convert the limited information provided by ruby method.parameters
64
85
  # to a richer structure.
65
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
86
+ # rubocop:disable Metrics/CyclomaticComplexity
66
87
  def map(param)
67
88
  @name = param.length > 1 ? param[1].to_s : ''
68
89
 
90
+ @default_value = nil
91
+
69
92
  case param[0]
70
93
  when :req
71
94
  @type = :param_required
72
- @signature_format = name.to_s
73
- @minimal_call_format = "'#{name}'"
74
95
  when :opt
75
96
  @type = :param_optional
76
- @signature_format = "#{name} = nil"
77
- @minimal_call_format = ''
78
97
  when :rest
79
98
  @type = :splat
80
- @signature_format = "*#{name}"
81
- @minimal_call_format = ''
82
99
  when :keyreq
83
100
  @type = :key_required
84
- @signature_format = "#{name}:"
85
- @minimal_call_format = "#{name}: '#{name}'"
86
101
  when :key
87
102
  @type = :key_optional
88
- @signature_format = "#{name}: nil"
89
- @minimal_call_format = ''
90
103
  when :keyrest
91
104
  @type = :double_splat
92
- @signature_format = "**#{name}"
93
- @minimal_call_format = ''
94
105
  when :block
95
106
  @type = :block
96
- @signature_format = "&#{name}"
97
- @minimal_call_format = ''
98
107
  else
99
108
  raise 'unknown type'
100
109
  end
101
110
  end
102
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
111
+ # rubocop:enable Metrics/CyclomaticComplexity
112
+
113
+ # Signature format *: Is used to format a parameter when it is used
114
+ # inside of a method signature, eg. def my_method(p1, p2 = 'xyz', p3: :name_value)
115
+
116
+ def signature_format_param_required
117
+ name.to_s
118
+ end
119
+
120
+ def signature_format_param_optional
121
+ "#{name} = nil" # signature format needs to be moved to a method
122
+ end
123
+
124
+ def signature_format_splat
125
+ "*#{name}"
126
+ end
127
+
128
+ def signature_format_key_required
129
+ "#{name}:"
130
+ end
131
+
132
+ def signature_format_key_optional
133
+ "#{name}: nil"
134
+ end
135
+
136
+ def signature_format_double_splat
137
+ "**#{name}"
138
+ end
139
+
140
+ def signature_format_block
141
+ "&#{name}"
142
+ end
143
+
144
+ # Minimal call format *: Is used to format a call to a method with the least
145
+ # number of parameters needed to make it work.
146
+
147
+ def minimal_call_format_ignore
148
+ ''
149
+ end
150
+
151
+ def minimal_call_format_param_required
152
+ "'#{@name}'"
153
+ end
154
+
155
+ # def minimal_call_format_param_optional
156
+ # ''
157
+ # end
158
+
159
+ # def minimal_call_format_splat
160
+ # ''
161
+ # end
162
+
163
+ def minimal_call_format_key_required
164
+ "#{@name}: '#{@name}'"
165
+ end
166
+
167
+ # def minimal_call_format_key_optional
168
+ # ''
169
+ # end
170
+
171
+ # def minimal_call_format_double_splat
172
+ # ''
173
+ # end
174
+
175
+ # def minimal_call_format_block
176
+ # ''
177
+ # end
103
178
  end
104
179
  end
@@ -7,27 +7,39 @@ module Peeky
7
7
  # Attr Reader Predicate will match true if the method info could be considered
8
8
  # a valid attr_reader
9
9
  class AttrReaderPredicate
10
+ # Match will return true if the method_info seems to be an :attr_reader
11
+ #
12
+ # @param instance [Object] instance the object that has this method (required)
13
+ # @param method_info [String] method info (required)
10
14
  def match(instance, method_info)
11
15
  return false unless prerequisites(instance, method_info)
12
16
 
13
- variable_name = "@#{method_info.name}"
14
17
  method_name = method_info.name
15
18
 
16
19
  # Refactor: Fragile
17
20
  # Really need to handle exceptions and types better
18
21
  # old_value = instance.send(method_name)
22
+
23
+ # This code works by
24
+ # 1. Set @name_of_method variable to random value
25
+ # 2. Call method name and see if it returns that value
26
+ # 3. Return match<true> if the values are equal
19
27
  new_value = SecureRandom.alphanumeric(20)
20
28
  code = <<-RUBY
21
- #{variable_name} = '#{new_value}' # @variable = 'a3bj7a3bj7a3bj7a3bj7'
29
+ @#{method_name} = '#{new_value}' # eg. @variable = 'a3bj7a3bj7a3bj7a3bj7'
22
30
  RUBY
23
- instance.instance_eval(code)
24
- current_value = instance.send(method_name)
31
+
32
+ cloned = instance.clone
33
+
34
+ cloned.instance_eval(code)
35
+ current_value = cloned.send(method_name)
25
36
  current_value == new_value
26
37
  end
27
38
 
28
39
  private
29
40
 
30
41
  def prerequisites(instance, method_info)
42
+ # look for obvious NON :attr_reader patterns
31
43
  return false if %w[! ? =].include?(method_info.name.to_s[-1..-1])
32
44
  return false unless method_info.parameters.length.zero?
33
45
  return false unless instance.respond_to?(method_info.name)
@@ -7,16 +7,24 @@ module Peeky
7
7
  # Attr Writer Predicate will match true if the method info could be considered
8
8
  # a valid attr_writer
9
9
  class AttrWriterPredicate
10
+ # Match will return true if the method_info seems to be an :attr_writer
11
+ #
12
+ # @param instance [Object] instance the object that has this method (required)
13
+ # @param method_info [String] method info (required)
10
14
  def match(instance, method_info)
11
15
  return false unless prerequisites(instance, method_info)
12
16
 
13
17
  param = method_info.parameters.first
18
+ # Taking advantage of an odd reflection concept in ruby where by
19
+ # method.parameters returns this array value [:req] for :attr_writer
20
+ # while ordinary methods return [:req, some_param_name]
14
21
  param.type == :param_required && param.name.empty?
15
22
  end
16
23
 
17
24
  private
18
25
 
19
26
  def prerequisites(instance, method_info)
27
+ # look for obvious NON :attr_writer patterns
20
28
  return false if %w[! ?].include?(method_info.name.to_s[-1..-1])
21
29
  return false unless method_info.name.to_s.end_with?('=')
22
30
  return false unless instance.respond_to?(method_info.name)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Peeky
4
4
  module Renderer
5
- # Class Interface Render
5
+ # Render: Class Interface
6
6
  #
7
7
  # Example output:
8
8
  # class SampleClassClassInterfaceRender
@@ -30,12 +30,14 @@ module Peeky
30
30
  # def z(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg); end
31
31
  # end
32
32
  class ClassInterfaceRender
33
+ # ClassInfo with information about the class instance to be rendered.
33
34
  attr_reader :class_info
34
35
 
35
36
  def initialize(class_info)
36
37
  @class_info = class_info
37
38
  end
38
39
 
40
+ # Render the class interface
39
41
  def render
40
42
  @indent = ''
41
43
  output = []
@@ -53,30 +55,32 @@ module Peeky
53
55
  output.join("\n")
54
56
  end
55
57
 
58
+ private
59
+
56
60
  def render_start
57
- "#{@indent}class #{class_info.class_name}"
61
+ "#{@indent}class #{@class_info.class_name}"
58
62
  end
59
63
 
60
64
  def render_accessors
61
- result = class_info.accessors.map { |attr| "#{@indent}attr_accessor :#{attr.name}" }
65
+ result = @class_info.accessors.map { |attr| "#{@indent}attr_accessor :#{attr.name}" }
62
66
  result.push '' unless result.length.zero?
63
67
  result
64
68
  end
65
69
 
66
70
  def render_readers
67
- result = class_info.readers.map { |attr| "#{@indent}attr_reader :#{attr.name}" }
71
+ result = @class_info.readers.map { |attr| "#{@indent}attr_reader :#{attr.name}" }
68
72
  result.push '' unless result.length.zero?
69
73
  result
70
74
  end
71
75
 
72
76
  def render_writers
73
- result = class_info.writers.map { |attr| "#{@indent}attr_writer :#{attr.name}" }
77
+ result = @class_info.writers.map { |attr| "#{@indent}attr_writer :#{attr.name}" }
74
78
  result.push '' unless result.length.zero?
75
79
  result
76
80
  end
77
81
 
78
82
  def render_methods
79
- result = class_info.methods.map do |method_signature|
83
+ result = @class_info.methods.map do |method_signature|
80
84
  render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
81
85
  "#{@indent}#{render_signature.render}"
82
86
  end
@@ -87,10 +91,6 @@ module Peeky
87
91
  def render_end
88
92
  "#{@indent}end"
89
93
  end
90
-
91
- def debug
92
- puts render
93
- end
94
94
  end
95
95
  end
96
96
  end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require 'active_support/core_ext'
4
+ require 'active_support/core_ext/string'
5
+
6
+ module Peeky
7
+ module Renderer
8
+ # Render: Class Interface with YARD documentation
9
+ class ClassInterfaceYardRender
10
+ # Indentation prefix as a string, defaults to +''+
11
+ #
12
+ # If you were writing a class into a file with an existing
13
+ # module, you may set the indent to +' '+ if you wanted this
14
+ # render to indent by two spaces
15
+ attr_accessor :indent
16
+
17
+ # Default param type when documenting positional and named parameters.
18
+ # Defaults to <String>
19
+ attr_accessor :default_param_type
20
+
21
+ # Default param type when documenting splat *parameters.
22
+ # Defaults to <Object>
23
+ attr_accessor :default_splat_param_type
24
+
25
+ # ClassInfo with information about the class instance to be rendered.
26
+ attr_reader :class_info
27
+
28
+ def initialize(class_info)
29
+ @class_info = class_info
30
+ @indent = ''
31
+ @default_param_type = 'String'
32
+ @default_splat_param_type = 'Object'
33
+ end
34
+
35
+ # Render the class interface with YARD documentation
36
+ def render
37
+ output = []
38
+ output.push render_start
39
+ @indent += ' '
40
+ output += render_accessors
41
+ output += render_readers
42
+ output += render_writers
43
+ output += render_methods
44
+ output.pop if output.last == ''
45
+
46
+ @indent = @indent[0..-3]
47
+
48
+ output.push render_end
49
+
50
+ output.join("\n")
51
+ end
52
+
53
+ private
54
+
55
+ def render_start
56
+ [
57
+ "#{@indent}# #{@class_info.class_name.titleize.humanize}",
58
+ "#{@indent}class #{@class_info.class_name}"
59
+ ]
60
+ end
61
+
62
+ def render_accessors
63
+ result = []
64
+ @class_info.accessors.map.with_index do |attr, index|
65
+ result.push '' if index.positive?
66
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
67
+ result.push "#{@indent}attr_accessor :#{attr.name}"
68
+ end
69
+ result.push '' unless result.length.zero?
70
+ result
71
+ end
72
+
73
+ def render_readers
74
+ result = []
75
+ @class_info.readers.map.with_index do |attr, index|
76
+ result.push '' if index.positive?
77
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
78
+ result.push "#{@indent}attr_reader :#{attr.name}"
79
+ end
80
+ result.push '' unless result.length.zero?
81
+ result
82
+ end
83
+
84
+ def render_writers
85
+ result = []
86
+ class_info.writers.map.with_index do |attr, index|
87
+ result.push '' if index.positive?
88
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
89
+ result.push "#{@indent}attr_writer :#{attr.name}"
90
+ end
91
+ result.push '' unless result.length.zero?
92
+ result
93
+ end
94
+
95
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
96
+ def render_methods
97
+ result = []
98
+ class_info.methods.map.with_index do |method_signature, index|
99
+ result.push '' if index.positive?
100
+ result.push "#{@indent}# #{method_signature.name.to_s.humanize}"
101
+
102
+ method_signature.parameters.each_with_index do |parameter, param_index|
103
+ result.push "#{@indent}#" if param_index.zero?
104
+
105
+ case parameter.type
106
+ when :splat
107
+ result.push "#{@indent}# @param #{parameter.name} [Array<#{default_splat_param_type}>] *#{parameter.name} - list of #{parameter.name.to_s.humanize.downcase}"
108
+ when :double_splat
109
+ result.push "#{@indent}# @param #{parameter.name} [<key: value>...] **#{parameter.name} - list of key/values"
110
+ when :block
111
+ result.push "#{@indent}# @param #{parameter.name} [Block] &#{parameter.name}"
112
+ when :key_required
113
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (required)"
114
+ when :key_optional
115
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (optional)"
116
+ when :param_required
117
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (required)"
118
+ when :param_optional
119
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (optional)"
120
+ else
121
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase}"
122
+ end
123
+ end
124
+
125
+ if method_signature.name.to_s.end_with?('?')
126
+ result.push ''
127
+ result.push "#{@indent}# @return [Boolean] true when #{method_signature.name.to_s.humanize.downcase}"
128
+ end
129
+
130
+ render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
131
+ render_signature.indent = @indent
132
+ render_signature.style = :default
133
+ result.push render_signature.render
134
+ end
135
+ result.push '' unless result.length.zero?
136
+ result
137
+ end
138
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
139
+
140
+ def render_end
141
+ "#{@indent}end"
142
+ end
143
+ end
144
+ end
145
+ end
@@ -8,13 +8,16 @@ module Peeky
8
8
  # instance.simple('first_param')
9
9
  # instance.complex('aaa', ddd: 'ddd')
10
10
  class MethodCallMinimumParamsRender
11
+ # Method signature stores a MethodInfo object
11
12
  attr_reader :method_signature
12
13
 
13
- def initialize(method_signature, instance_name = 'instance')
14
+ def initialize(method_signature, **opts)
15
+ instance_name = opts[:instance_name] || 'instance'
14
16
  @instance_name = instance_name
15
17
  @method_signature = method_signature
16
18
  end
17
19
 
20
+ # Render the a method call with minimal parameters
18
21
  def render
19
22
  name = method_signature.name
20
23
 
@@ -28,10 +31,6 @@ module Peeky
28
31
 
29
32
  "#{@instance_name}.#{name}#{params}"
30
33
  end
31
-
32
- def debug
33
- puts render
34
- end
35
34
  end
36
35
  end
37
36
  end