peeky 0.0.19 → 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'peeky/version'
4
4
 
5
+ require 'peeky/api'
6
+
5
7
  require 'peeky/attr_info'
6
8
  require 'peeky/class_info'
7
9
  require 'peeky/method_info'
@@ -10,7 +12,9 @@ require 'peeky/parameter_info'
10
12
  require 'peeky/predicates/attr_reader_predicate'
11
13
  require 'peeky/predicates/attr_writer_predicate'
12
14
 
15
+ require 'peeky/renderer/class_debug_render'
13
16
  require 'peeky/renderer/class_interface_render'
17
+ require 'peeky/renderer/class_interface_yard_render'
14
18
  require 'peeky/renderer/method_call_minimum_params_render'
15
19
  require 'peeky/renderer/method_signature_render'
16
20
  require 'peeky/renderer/method_signature_with_debug_render'
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Peeky module provides access to the API via the
4
+ # module method Peeky.api
5
+ module Peeky
6
+ class << self
7
+ attr_accessor :api
8
+ end
9
+
10
+ # API has factory and creational patterns for easy usage
11
+ # of the Peeky reflection system
12
+ class Api
13
+ # Build a {Peeky::ClassInfo} structure based around a
14
+ # ruby class instance.
15
+ #
16
+ # ClassInfo stores information about the instance of a
17
+ # class that is passed in including methods, attr_accessors
18
+ # attr_readers and attr_writers.
19
+ def build_class_info(instance, lazy: true)
20
+ ci = Peeky::ClassInfo.new(instance)
21
+ ci.load unless lazy
22
+ ci
23
+ end
24
+
25
+ # Render a class using a predefined class renderer
26
+ def render_class(render_key, class_info: nil, instance: nil, **_opts)
27
+ raise 'Call render_class with class_info OR instance.' if class_info.nil? && instance.nil?
28
+ raise 'Call render_class with class_info OR instance, these parameters are mutually exclusive' if !class_info.nil? && !instance.nil?
29
+
30
+ renderer = class_renderer(render_key)
31
+
32
+ class_info = Peeky::ClassInfo.new(instance) if class_info.nil?
33
+
34
+ renderer.new(class_info).render
35
+ end
36
+
37
+ # Get a method renderer by :key
38
+ #
39
+ # TODO: Refactor to a configurable system
40
+ def method_renderer(key)
41
+ case key
42
+ when :signature
43
+ Peeky::Renderer::MethodSignatureRender
44
+ when :signature_with_debug
45
+ Peeky::Renderer::MethodSignatureWithDebugRender
46
+ when :call_minimum_params
47
+ Peeky::Renderer::MethodCallMinimumParamsRender
48
+ else
49
+ raise "Unknown method renderer: #{key}"
50
+ end
51
+ end
52
+
53
+ # Get a class renderer by :key
54
+ #
55
+ # TODO: Refactor to a configurable system
56
+ def class_renderer(key)
57
+ case key
58
+ when :class_debug
59
+ Peeky::Renderer::ClassDebugRender
60
+ when :class_interface
61
+ Peeky::Renderer::ClassInterfaceRender
62
+ when :class_interface_yard
63
+ Peeky::Renderer::ClassInterfaceYardRender
64
+ else
65
+ raise "Unknown class renderer: #{key}"
66
+ end
67
+ end
68
+
69
+ # def render_method()
70
+ # end
71
+ end
72
+ end
73
+
74
+ Peeky.api = Peeky::Api.new
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Peeky
4
- # Class Info stores information about the
5
- # class instance that you pass in.
4
+ # Class Info stores information about the class instance that is provided.
6
5
  #
7
6
  # The information is collected into MethodInfo objects
8
7
  # that live within the signatures accessor.
@@ -10,6 +9,7 @@ module Peeky
10
9
  # This information is then separated out into
11
10
  # :methods, :attr_accessors, :attr_readers and :attr_writers
12
11
  class ClassInfo
12
+ # Holds an instance to the class you are gathering information from
13
13
  attr_reader :instance
14
14
 
15
15
  # Peak into class information
@@ -17,24 +17,123 @@ module Peeky
17
17
  @instance = instance
18
18
  end
19
19
 
20
- def class_name
20
+ def to_s
21
+ class_full_name
22
+ end
23
+
24
+ # Load class_info
25
+ #
26
+ # Accessing information about methods and parameters is currently
27
+ # lazy-loaded via memoization.
28
+ #
29
+ # At times during debug or other edge cases, it may be useful to
30
+ # pre-load this information early.
31
+ def load
32
+ ruby_instance_methods
33
+ ruby_instance_method_names
34
+ signatures
35
+ end
36
+
37
+ # Class full name includes the module namespace
38
+ def class_full_name
21
39
  instance.class.name
22
40
  end
23
41
 
42
+ # Class name
43
+ def class_name
44
+ @_class_name ||= class_full_name.to_s.gsub(/^.*::/, '')
45
+ # instance.class.name.split('::').last
46
+ end
47
+
48
+ # Module name
49
+ def module_name
50
+ @_module_name ||= class_full_name.to_s.gsub(/(.*)::.*/, '\1')
51
+ end
52
+
53
+ # Get a list of :attr_accessor on the class
54
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
24
55
  def accessors
25
- @accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
56
+ @_accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
57
+ end
58
+
59
+ # Get a list of :attr_accessors ordered the way they are in the source code
60
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
61
+ def accessors_source_order
62
+ # TODO: This feature is required
63
+ # May be best to have a sort object that can be created for each type of ordering that is needed
64
+ accessors
26
65
  end
27
66
 
67
+ # Attribute infos
68
+ def attribute_infos
69
+ @_attribute_infos ||= begin
70
+ grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
71
+
72
+ grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
73
+ end
74
+ end
75
+
76
+ # Method by name
77
+ #
78
+ # @param name [String] name (required)
79
+ def method_by_name(name)
80
+ signatures_by_name(name, filter_type: :method).first
81
+ end
82
+
83
+ # Get a list methods
84
+ # @return [Array<MethodInfo>] list of MethodInfo where type is :method
85
+ def methods
86
+ @_methods ||= signatures.select { |signature| signature.implementation_type == :method }
87
+ end
88
+
89
+ # Get a list methods ordered the way they are in the source code
90
+ # @return [Array<MethodInfo>] list of MethodInfo
91
+ def methods_source_order
92
+ # TODO: This feature is required
93
+ # May be best to have a sort object that can be created for each type of ordering that is needed
94
+ methods
95
+ end
96
+
97
+ # Reader by name
98
+ #
99
+ # @param name [String] name (required)
100
+ def reader_by_name(name)
101
+ signatures_by_name(name, filter_type: :attr_reader).first
102
+ end
103
+
104
+ # Get a list of :attr_reader on the class
105
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
28
106
  def readers
29
- @readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
107
+ @_readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
30
108
  end
31
109
 
110
+ # Get a list of :attr_reader ordered the way they are in the source code
111
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_reader
112
+ def readers_source_order
113
+ # TODO: This feature is required
114
+ # May be best to have a sort object that can be created for each type of ordering that is needed
115
+ readers
116
+ end
117
+
118
+ # Get a list of :attr_writer on the class
119
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
32
120
  def writers
33
- @writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
121
+ @_writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
34
122
  end
35
123
 
36
- def methods
37
- @methods ||= signatures.select { |signature| signature.implementation_type == :method }
124
+ # Get a list of :attr_writer ordered the way they are in the source code
125
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
126
+ def writers_source_order
127
+ # TODO: This feature is required
128
+ # May be best to have a sort object that can be created for each type of ordering that is needed
129
+ writers
130
+ end
131
+
132
+ # Writer by name
133
+ #
134
+ # @param name [String] name (required)
135
+ def writer_by_name(name)
136
+ signatures_by_name(name, filter_type: :attr_writer).first
38
137
  end
39
138
 
40
139
  # def signatures(types = [:instance])
@@ -42,36 +141,36 @@ module Peeky
42
141
  # such as static, private vs public
43
142
  # deep, deep_to_level, this_instance.
44
143
  def signatures
45
- @signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
46
- end
47
-
48
- def signatures_by_name(name)
49
- signatures.select { |im| im.name == name }
144
+ @_signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
50
145
  end
51
146
 
147
+ # Signatures by clean name
148
+ #
149
+ # @param clean_name [String] clean name (required)
52
150
  def signatures_by_clean_name(clean_name)
53
151
  signatures.select { |im| im.clean_name == clean_name }
54
152
  end
55
153
 
56
- def attribute_infos
57
- @attribute_infos ||= begin
58
- grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
154
+ # Signatures by name
155
+ #
156
+ # @param name [String] name (required)
157
+ # @param filter_type [String] filter_type: <value for filter type> (optional)
158
+ def signatures_by_name(name, filter_type: :all)
159
+ return signatures.select { |im| im.name == name } if filter_type == :all
59
160
 
60
- grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
61
- end
161
+ signatures.select { |im| im.name == name && im.implementation_type == filter_type }
62
162
  end
63
163
 
164
+ # Debug
165
+ #
166
+ # Refact: PATTERN: Come up it an debug inclusion system so that
167
+ # so that debug helpers can be included for development and excluded
168
+ # for production
169
+ # @param format [String] format: <value for format> (optional)
64
170
  def debug(format: [:signatures])
65
- if format == :method_names
66
- puts '-' * 70
67
- puts 'Method Names'
68
- puts '-' * 70
69
- ruby_instance_method_names.each do |method_name|
70
- puts method_name
71
- end
72
- end
171
+ debug_method_names if format.include?(:method_names)
73
172
 
74
- return unless format == :signatures
173
+ return unless format.include?(:signatures)
75
174
 
76
175
  puts '-' * 70
77
176
  puts 'Methods'
@@ -79,14 +178,25 @@ module Peeky
79
178
  signatures.each(&:debug)
80
179
  end
81
180
 
181
+ def debug_method_names
182
+ puts '-' * 70
183
+ puts 'Method Names'
184
+ puts '-' * 70
185
+ ruby_instance_method_names.each do |method_name|
186
+ puts method_name
187
+ end
188
+ end
189
+
82
190
  private
83
191
 
84
192
  def ruby_instance_method_names
85
- @ruby_instance_method_names ||= instance.class.instance_methods(false).sort
193
+ @_ruby_instance_method_names ||= instance.class.instance_methods(false).sort
86
194
  end
87
195
 
88
196
  def ruby_instance_methods
89
- @ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
197
+ @_ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
198
+ rescue StandardError => e
199
+ puts e
90
200
  end
91
201
  end
92
202
  end
@@ -0,0 +1,124 @@
1
+ module Peeky
2
+ module Example
3
+ # Yard sample
4
+ class YardSample
5
+ # A read write1
6
+ attr_accessor :a_read_write1
7
+
8
+ # A read write2
9
+ attr_accessor :a_read_write2
10
+
11
+ # A read write3
12
+ attr_accessor :a_read_write3
13
+
14
+ # B reader1
15
+ attr_reader :b_reader1
16
+
17
+ # B reader2
18
+ attr_reader :b_reader2
19
+
20
+ # E looks like an attr reader
21
+ attr_reader :e_looks_like_an_attr_reader
22
+
23
+ # C writer1
24
+ attr_writer :c_writer1
25
+
26
+ # C writer2
27
+ attr_writer :c_writer2
28
+
29
+ # Alpha sort1
30
+ def alpha_sort1
31
+ end
32
+
33
+ # Alpha sort2
34
+ def alpha_sort2
35
+ end
36
+
37
+ # D do something method
38
+ def d_do_something_method
39
+ end
40
+
41
+ # E method with required param
42
+ #
43
+ # @param first_name [String] first name (required)
44
+ def e_method_with_required_param(first_name)
45
+ end
46
+
47
+ # F method with required param and optional param
48
+ #
49
+ # @param first_name [String] first name (required)
50
+ # @param last_name [String] last name (optional)
51
+ def f_method_with_required_param_and_optional_param(first_name, last_name = nil)
52
+ end
53
+
54
+ # G method with required param and two optional params
55
+ #
56
+ # @param first_name [String] first name (required)
57
+ # @param last_name [String] last name (optional)
58
+ # @param age [String] age (optional)
59
+ def g_method_with_required_param_and_two_optional_params(first_name, last_name = nil, age = nil)
60
+ end
61
+
62
+ # H list of optional parameters
63
+ #
64
+ # @param command_args [Array<Object>] *command_args - list of command args
65
+ def h_list_of_optional_parameters(*command_args)
66
+ end
67
+
68
+ # I method with two required params and list of optional params
69
+ #
70
+ # @param first_name [String] first name (required)
71
+ # @param last_name [String] last name (required)
72
+ # @param alias_names [Array<Object>] *alias_names - list of alias names
73
+ def i_method_with_two_required_params_and_list_of_optional_params(first_name, last_name, *alias_names)
74
+ end
75
+
76
+ # J method with list of named arguments
77
+ #
78
+ # @param options [<key: value>...] **options - list of key/values
79
+ def j_method_with_list_of_named_arguments(**options)
80
+ end
81
+
82
+ # K method with block
83
+ #
84
+ # @param code_block [Block] &code_block
85
+ def k_method_with_block(&code_block)
86
+ end
87
+
88
+ # L method with key value param required
89
+ #
90
+ # @param name [String] name: <value for name> (required)
91
+ def l_method_with_key_value_param_required(name:)
92
+ end
93
+
94
+ # N method with key value param required and optional key value
95
+ #
96
+ # @param last_name [String] last_name: <value for last name> (required)
97
+ # @param salutation [String] salutation: <value for salutation> (optional)
98
+ def n_method_with_key_value_param_required_and_optional_key_value(last_name:, salutation: nil)
99
+ end
100
+
101
+ # P available?
102
+
103
+ # @return [Boolean] true when p available?
104
+ def p_available?
105
+ end
106
+
107
+ # Q danger will robinson!
108
+ def q_danger_will_robinson!
109
+ end
110
+
111
+ # Z complex
112
+ #
113
+ # @param aaa [String] aaa (required)
114
+ # @param bbb [String] bbb (optional)
115
+ # @param ccc [Array<Object>] *ccc - list of ccc
116
+ # @param ddd [String] ddd: <value for ddd> (required)
117
+ # @param eee [String] eee: <value for eee> (optional)
118
+ # @param fff [<key: value>...] **fff - list of key/values
119
+ # @param ggg [Block] &ggg
120
+ def z_complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -12,18 +12,19 @@ module Peeky
12
12
  attr_accessor :parameters
13
13
 
14
14
  # MethodInfo delegates to the underlying ruby method object
15
- attr_reader :method
15
+ attr_reader :focal_method
16
16
 
17
- def_delegators :method, :name, :receiver, :arity, :super_method
17
+ def_delegators :focal_method, :name, :receiver, :arity, :super_method
18
18
 
19
19
  # Stage 2 is the method likely to be an attribute reader or writer
20
20
 
21
- # Implemented As indicates the probable representation of this
21
+ # Implementation type indicates the probable representation of this
22
22
  # method in ruby, was it `def method` or `attr_reader` / `attr_writer`
23
23
  attr_reader :implementation_type
24
24
 
25
+ # TODO: target_instance is required...
25
26
  def initialize(method, target_instance = nil)
26
- @method = method
27
+ @focal_method = method
27
28
  @parameters = ParameterInfo.from_method(method)
28
29
  # stage 1
29
30
  # @implementation_type = :method