peeky 0.0.19 → 0.0.33

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)
@@ -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