peeky 0.0.19 → 0.0.33

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.
@@ -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)
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peeky
4
+ module Renderer
5
+ # Class Debug Render
6
+ class ClassDebugRender
7
+ attr_reader :class_info
8
+
9
+ def initialize(class_info)
10
+ @key_width = 30
11
+ @class_info = class_info
12
+ end
13
+
14
+ # Render the class interface
15
+ # rubocop:disable Metrics/AbcSize
16
+ def render
17
+ output = []
18
+ output.push class_details
19
+ attributes = render_accessors + render_readers + render_writers
20
+
21
+ if attributes.length.positive?
22
+ output.push("-- Attributes #{'-' * 56}")
23
+ output.push(*attributes)
24
+ output.push('')
25
+ end
26
+
27
+ methods = render_methods
28
+
29
+ if methods.length.positive?
30
+ output.push("-- Public Methods #{'-' * 52}")
31
+ output.push(*methods)
32
+ output.push('')
33
+ end
34
+
35
+ output.pop if output.last == ''
36
+
37
+ output.join("\n")
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ private
42
+
43
+ def lj(value, size = 20)
44
+ value.to_s.ljust(size)
45
+ end
46
+
47
+ def kv(key, value)
48
+ "#{key.to_s.ljust(@key_width)}: #{value}"
49
+ end
50
+
51
+ def class_details
52
+ [
53
+ '-' * 70,
54
+ kv('class name', @class_info.class_name),
55
+ kv('module name', @class_info.module_name),
56
+ kv('class full name', @class_info.class_full_name),
57
+ ''
58
+ ]
59
+ end
60
+
61
+ def render_accessors
62
+ @class_info.accessors.map { |attr| kv('attr_accessor', attr.name) }
63
+ end
64
+
65
+ def render_readers
66
+ @class_info.readers.map { |attr| kv('attr_reader', attr.name) }
67
+ end
68
+
69
+ def render_writers
70
+ @class_info.writers.map { |attr| kv('attr_writer', attr.name) }
71
+ end
72
+
73
+ def render_methods
74
+ @class_info.methods.flat_map do |method|
75
+ [
76
+ "#{method.name}::",
77
+ *render_paramaters(method.parameters),
78
+ ''
79
+ ]
80
+ end
81
+ end
82
+
83
+ def render_paramaters(parameters)
84
+ result = [
85
+ "#{lj('name')} #{lj('param format')} #{lj('type')}",
86
+ '-' * 70
87
+ ]
88
+
89
+ result + parameters.map do |param|
90
+ "#{lj(param.name)} #{lj(param.signature_format)} #{lj(param.type)}"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -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,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string'
4
+
5
+ module Peeky
6
+ module Renderer
7
+ # Render: Class Interface with YARD documentation
8
+ class ClassInterfaceYardRender
9
+ # Indentation prefix as a string, defaults to +''+
10
+ #
11
+ # If you were writing a class into a file with an existing
12
+ # module, you may set the indent to +' '+ if you wanted this
13
+ # render to indent by two spaces
14
+ attr_accessor :indent
15
+
16
+ # Default param type when documenting positional and named parameters.
17
+ # Defaults to <String>
18
+ attr_accessor :default_param_type
19
+
20
+ # Default param type when documenting splat *parameters.
21
+ # Defaults to <Object>
22
+ attr_accessor :default_splat_param_type
23
+
24
+ # ClassInfo with information about the class instance to be rendered.
25
+ attr_reader :class_info
26
+
27
+ def initialize(class_info)
28
+ @class_info = class_info
29
+ @indent = ''
30
+ @default_param_type = 'String'
31
+ @default_splat_param_type = 'Object'
32
+ end
33
+
34
+ # Render the class interface with YARD documentation
35
+ def render
36
+ output = []
37
+ output.push render_start
38
+ @indent += ' '
39
+ output += (render_accessors + render_readers + render_writers + render_methods)
40
+ output.pop if output.last == ''
41
+
42
+ @indent = @indent[0..-3]
43
+
44
+ output.push render_end
45
+
46
+ output.join("\n")
47
+ end
48
+
49
+ private
50
+
51
+ def render_start
52
+ [
53
+ "#{@indent}# #{@class_info.class_name.titleize.humanize}",
54
+ "#{@indent}class #{@class_info.class_name}"
55
+ ]
56
+ end
57
+
58
+ def render_accessors
59
+ result = []
60
+ @class_info.accessors.map.with_index do |attr, index|
61
+ result.push '' if index.positive?
62
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
63
+ result.push "#{@indent}attr_accessor :#{attr.name}"
64
+ end
65
+ result.push '' unless result.length.zero?
66
+ result
67
+ end
68
+
69
+ def render_readers
70
+ result = []
71
+ @class_info.readers.map.with_index do |attr, index|
72
+ result.push '' if index.positive?
73
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
74
+ result.push "#{@indent}attr_reader :#{attr.name}"
75
+ end
76
+ result.push '' unless result.length.zero?
77
+ result
78
+ end
79
+
80
+ def render_writers
81
+ result = []
82
+ class_info.writers.map.with_index do |attr, index|
83
+ result.push '' if index.positive?
84
+ result.push "#{@indent}# #{attr.name.to_s.humanize}"
85
+ result.push "#{@indent}attr_writer :#{attr.name}"
86
+ end
87
+ result.push '' unless result.length.zero?
88
+ result
89
+ end
90
+
91
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
92
+ def render_methods
93
+ result = []
94
+ class_info.methods.map.with_index do |method_signature, index|
95
+ result.push '' if index.positive?
96
+ result.push "#{@indent}# #{method_signature.name.to_s.humanize}"
97
+
98
+ method_signature.parameters.each_with_index do |parameter, param_index|
99
+ result.push "#{@indent}#" if param_index.zero?
100
+
101
+ case parameter.type
102
+ when :splat
103
+ result.push "#{@indent}# @param #{parameter.name} [Array<#{default_splat_param_type}>] *#{parameter.name} - list of #{parameter.name.to_s.humanize.downcase}"
104
+ when :double_splat
105
+ result.push "#{@indent}# @param #{parameter.name} [<key: value>...] **#{parameter.name} - list of key/values"
106
+ when :block
107
+ result.push "#{@indent}# @param #{parameter.name} [Block] &#{parameter.name}"
108
+ when :key_required
109
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (required)"
110
+ when :key_optional
111
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (optional)"
112
+ when :param_required
113
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (required)"
114
+ when :param_optional
115
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (optional)"
116
+ else
117
+ result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase}"
118
+ end
119
+ end
120
+
121
+ if method_signature.name.to_s.end_with?('?')
122
+ result.push ''
123
+ result.push "#{@indent}# @return [Boolean] true when #{method_signature.name.to_s.humanize.downcase}"
124
+ end
125
+
126
+ render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
127
+ render_signature.indent = @indent
128
+ render_signature.style = :default
129
+ result.push render_signature.render
130
+ end
131
+ result.push '' unless result.length.zero?
132
+ result
133
+ end
134
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
135
+
136
+ def render_end
137
+ "#{@indent}end"
138
+ end
139
+ end
140
+ end
141
+ end