peeky 0.0.19 → 0.0.25

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17aca44d1863e4d7d811c2fdf09bbe4aec22c8aa5a2d0d09983a8d9c50f14430
4
- data.tar.gz: ff605fe00998f4e7ca15725f20171180329edd882256c46eaa20c96d41491488
3
+ metadata.gz: 75fed761de619d444f8e84f4e08df5df41e9f12b89e95051b60dd0f60bafa57e
4
+ data.tar.gz: 05e1ba1ec76ffe0689f536e8e2c78c86fd16570051212ca9031ef3cf5f302e9b
5
5
  SHA512:
6
- metadata.gz: e212ec6b8cb3e88e78e57f42d7a880aabf73e46cabd0b23e8d3ce93a0819a74d36f7dbf1d1af95f2677824aa885e277d003445c4d8bc656afd4e1245f8cd8d04
7
- data.tar.gz: 3498282a3c71032ef6866783d06f642b7c834079d3cfae164d577a734ba5194c082b820b956bef116f7277fed19993f7833776e3fe97c1401d1485cf976e176c
6
+ metadata.gz: 19bb5a15dc9c0db1a5346632f448da2ff7396f247c474a89b1dee7c2097e1fdf824bce8227a35762bc85db49d4301463f613f78999a20f1656edb870e4eabb51
7
+ data.tar.gz: 89ae53bd7a7a840a92214337bc7a08c5e813eab892a94391cb69b2e27a0ad87546c7489f03c710f20328b770abafd3ccf45c92a5bdaf22ed0d5ffb2b93fcd2cf
@@ -4,8 +4,23 @@ AllCops:
4
4
  NewCops: enable
5
5
  Exclude:
6
6
  - "_/**/*"
7
+ - "lib/peeky/example/yard_sample.rb"
7
8
 
8
9
  # My Preferences - Start
10
+ Style/EmptyMethod:
11
+ Exclude:
12
+ - "**/spec/**/*"
13
+ Metrics/ParameterLists:
14
+ Exclude:
15
+ - "**/spec/**/*"
16
+ Layout/EmptyLineBetweenDefs:
17
+ Exclude:
18
+ - "**/spec/**/*"
19
+
20
+ Lint/AmbiguousBlockAssociation:
21
+ Exclude:
22
+ - "**/spec/**/*"
23
+
9
24
  Style/AccessorGrouping:
10
25
  Enabled: false
11
26
 
@@ -16,6 +31,7 @@ Layout/SpaceBeforeComma:
16
31
  Metrics/BlockLength:
17
32
  Exclude:
18
33
  - "**/spec/*"
34
+ - "*.gemspec"
19
35
  ExcludedMethods:
20
36
  - configure
21
37
  - context
@@ -21,6 +21,10 @@ Gemspec/RequiredRubyVersion:
21
21
  Exclude:
22
22
  - 'peeky.gemspec'
23
23
 
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - 'peeky.gemspec'
27
+
24
28
  # Offense count: 1
25
29
  # Cop supports --auto-correct.
26
30
  # Configuration parameters: EnforcedStyle.
data/Gemfile CHANGED
@@ -5,13 +5,16 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in poc_github_ap.gemspec
6
6
  gemspec
7
7
 
8
+ group :development do
9
+ # pry on steroids
10
+ gem 'pry-coolline', github: 'owst/pry-coolline', branch: 'support_new_pry_config_api'
11
+ gem 'jazz_fingers'
12
+ end
13
+
8
14
  group :development, :test do
9
15
  gem 'guard-bundler'
10
16
  gem 'guard-rspec'
11
17
  gem 'guard-rubocop'
12
- # pry on steroids
13
- # gem 'pry-coolline', github: 'owst/pry-coolline', branch: 'support_new_pry_config_api'
14
- gem 'jazz_fingers'
15
18
  gem 'rake', '~> 12.0'
16
19
  # this is used for cmdlets 'self-executing gems'
17
20
  gem 'rake-compiler'
data/Guardfile CHANGED
@@ -23,8 +23,8 @@ group :green_pass_then_cop, halt_on_fail: true do
23
23
  watch(%r{^lib/peeky/commands/(.+)\.rb$}) { |m| "spec/unit/commands/#{m[1]}_spec.rb" }
24
24
  end
25
25
 
26
- guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
27
- watch(%r{.+\.rb$})
28
- watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
- end
26
+ # guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
27
+ # watch(%r{.+\.rb$})
28
+ # watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
+ # end
30
30
  end
@@ -4,7 +4,37 @@
4
4
 
5
5
  As a Ruby Developer, I should be able to Reverse engineer classes and methods, so that I can document and understand them
6
6
 
7
- ## Stories
7
+ ## Development rader
8
+
9
+
10
+ ### Tasks next on list
11
+
12
+ As a Developer, I can render a class with RDoc documentation, so that I do not have to manually type RDoc references
13
+
14
+ - Add simplified API with examples
15
+ - Start documenting usage instructions
16
+
17
+ As a Developer, I can use Peeky with a simple API, so that I use this GEM quickly
18
+
19
+ - Add simplified API with examples
20
+ - Start documenting usage instructions
21
+
22
+ As a Developer, I can quickly build requirements, so that I can document project features
23
+
24
+ - Add support for backlog stories and tasks
25
+ - Add usage instructions
26
+
27
+
28
+
29
+ ## Stories and tasks
30
+
31
+ ### Stories - completed
32
+
33
+ As a David, I can edify Karin, because she is cool
34
+
35
+ - karin_is_awesome
36
+ - i_am_very_grateful_for_karin
37
+ - karin_the_beautiful
8
38
 
9
39
  As a Developer, I can render a class with instance attributes and methods, So that I can quickly mock out an entire class
10
40
 
@@ -35,15 +65,13 @@ As a Developer, I should be able to interrogate class instance information, so t
35
65
  - ClassInfo stores information about a ruby class. Only support instance methods
36
66
 
37
67
 
38
- ## Tasks
39
-
68
+ ##$ Tasks - completed
40
69
 
41
70
  Setup GitHub Action (test and lint)
42
71
 
43
72
  - Setup Rspec action
44
73
  - Setup RuboCop action
45
74
 
46
-
47
75
  Setup new Ruby GEM
48
76
 
49
77
  - Build out a standard GEM structure
data/README.md CHANGED
@@ -2,12 +2,8 @@
2
2
 
3
3
  > Peeky is a Ruby GEM for peaking into ruby classes and extracting meta
4
4
 
5
- Welcome to your new ruby gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/peeky`.
6
-
7
5
  When using the source code for this gem, start by running `bin/setup` to install locally or `bundle install`
8
6
 
9
- To experiment with that code, run `bin/console` for an interactive prompt or run `exe/peeky` to see a list of commands.
10
-
11
7
  ## Installation
12
8
 
13
9
  Add this line to your application's Gemfile:
@@ -62,7 +58,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
62
58
 
63
59
  ## Code of Conduct
64
60
 
65
- Everyone interacting in the Peeky project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/peeky/blob/master/CODE_OF_CONDUCT.md).
61
+ Everyone interacting in the Peeky project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the.
66
62
 
67
63
  ## Copyright
68
64
 
@@ -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'
@@ -11,6 +13,7 @@ require 'peeky/predicates/attr_reader_predicate'
11
13
  require 'peeky/predicates/attr_writer_predicate'
12
14
 
13
15
  require 'peeky/renderer/class_interface_render'
16
+ require 'peeky/renderer/class_interface_yard_render'
14
17
  require 'peeky/renderer/method_call_minimum_params_render'
15
18
  require 'peeky/renderer/method_signature_render'
16
19
  require 'peeky/renderer/method_signature_with_debug_render'
@@ -0,0 +1,70 @@
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)
20
+ Peeky::ClassInfo.new(instance)
21
+ end
22
+
23
+ # Render a class using a predefined class renderer
24
+ def render_class(render_key, class_info: nil, instance: nil, **_opts)
25
+ raise 'Call render_class with class_info OR instance.' if class_info.nil? && instance.nil?
26
+ raise 'Call render_class with class_info OR instance, these parameters are mutually exclusive' if !class_info.nil? && !instance.nil?
27
+
28
+ renderer = class_renderer(render_key)
29
+
30
+ class_info = Peeky::ClassInfo.new(instance) if class_info.nil?
31
+
32
+ renderer.new(class_info).render
33
+ end
34
+
35
+ # Get a method renderer by :key
36
+ #
37
+ # TODO: Refactor to a configurable system
38
+ def method_renderer(key)
39
+ case key
40
+ when :signature
41
+ Peeky::Renderer::MethodSignatureRender
42
+ when :signature_with_debug
43
+ Peeky::Renderer::MethodSignatureWithDebugRender
44
+ when :call_minimum_params
45
+ Peeky::Renderer::MethodCallMinimumParamsRender
46
+ else
47
+ raise "Unknown method renderer: #{key}"
48
+ end
49
+ end
50
+
51
+ # Get a class renderer by :key
52
+ #
53
+ # TODO: Refactor to a configurable system
54
+ def class_renderer(key)
55
+ case key
56
+ when :class_interface
57
+ Peeky::Renderer::ClassInterfaceRender
58
+ when :class_interface_yard
59
+ Peeky::Renderer::ClassInterfaceYardRender
60
+ else
61
+ raise "Unknown class renderer: #{key}"
62
+ end
63
+ end
64
+
65
+ # def render_method()
66
+ # end
67
+ end
68
+ end
69
+
70
+ 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,106 @@ module Peeky
17
17
  @instance = instance
18
18
  end
19
19
 
20
- def class_name
20
+ # Class full name includes the module namespace
21
+ def class_full_name
21
22
  instance.class.name
22
23
  end
23
24
 
25
+ # Class name
26
+ def class_name
27
+ @_class_name ||= class_full_name.to_s.gsub(/^.*::/, '')
28
+ # instance.class.name.split('::').last
29
+ end
30
+
31
+ # Module name
32
+ def module_name
33
+ @_module_name ||= class_full_name.to_s.gsub(/(.*)::.*/, '\1')
34
+ end
35
+
36
+ # Get a list of :attr_accessor on the class
37
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
24
38
  def accessors
25
- @accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
39
+ @_accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
40
+ end
41
+
42
+ # Get a list of :attr_accessors ordered the way they are in the source code
43
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
44
+ def accessors_source_order
45
+ # TODO: This feature is required
46
+ # May be best to have a sort object that can be created for each type of ordering that is needed
47
+ accessors
48
+ end
49
+
50
+ # Attribute infos
51
+ def attribute_infos
52
+ @_attribute_infos ||= begin
53
+ grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
54
+
55
+ grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
56
+ end
57
+ end
58
+
59
+ # Method by name
60
+ #
61
+ # @param name [String] name (required)
62
+ def method_by_name(name)
63
+ signatures_by_name(name, filter_type: :method).first
64
+ end
65
+
66
+ # Get a list methods
67
+ # @return [Array<MethodInfo>] list of MethodInfo where type is :method
68
+ def methods
69
+ @_methods ||= signatures.select { |signature| signature.implementation_type == :method }
70
+ end
71
+
72
+ # Get a list methods ordered the way they are in the source code
73
+ # @return [Array<MethodInfo>] list of MethodInfo
74
+ def methods_source_order
75
+ # TODO: This feature is required
76
+ # May be best to have a sort object that can be created for each type of ordering that is needed
77
+ methods
78
+ end
79
+
80
+ # Reader by name
81
+ #
82
+ # @param name [String] name (required)
83
+ def reader_by_name(name)
84
+ signatures_by_name(name, filter_type: :attr_reader).first
26
85
  end
27
86
 
87
+ # Get a list of :attr_reader on the class
88
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
28
89
  def readers
29
- @readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
90
+ @_readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
30
91
  end
31
92
 
93
+ # Get a list of :attr_reader ordered the way they are in the source code
94
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_reader
95
+ def readers_source_order
96
+ # TODO: This feature is required
97
+ # May be best to have a sort object that can be created for each type of ordering that is needed
98
+ readers
99
+ end
100
+
101
+ # Get a list of :attr_writer on the class
102
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
32
103
  def writers
33
- @writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
104
+ @_writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
34
105
  end
35
106
 
36
- def methods
37
- @methods ||= signatures.select { |signature| signature.implementation_type == :method }
107
+ # Get a list of :attr_writer ordered the way they are in the source code
108
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
109
+ def writers_source_order
110
+ # TODO: This feature is required
111
+ # May be best to have a sort object that can be created for each type of ordering that is needed
112
+ writers
113
+ end
114
+
115
+ # Writer by name
116
+ #
117
+ # @param name [String] name (required)
118
+ def writer_by_name(name)
119
+ signatures_by_name(name, filter_type: :attr_writer).first
38
120
  end
39
121
 
40
122
  # def signatures(types = [:instance])
@@ -42,27 +124,45 @@ module Peeky
42
124
  # such as static, private vs public
43
125
  # deep, deep_to_level, this_instance.
44
126
  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 }
127
+ @_signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
50
128
  end
51
129
 
130
+ # Signatures by clean name
131
+ #
132
+ # @param clean_name [String] clean name (required)
52
133
  def signatures_by_clean_name(clean_name)
53
134
  signatures.select { |im| im.clean_name == clean_name }
54
135
  end
55
136
 
56
- def attribute_infos
57
- @attribute_infos ||= begin
58
- grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
137
+ # Signatures by name
138
+ #
139
+ # @param name [String] name (required)
140
+ # @param filter_type [String] filter_type: <value for filter type> (optional)
141
+ def signatures_by_name(name, filter_type: :all)
142
+ return signatures.select { |im| im.name == name } if filter_type == :all
59
143
 
60
- grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
61
- end
144
+ signatures.select { |im| im.name == name && im.implementation_type == filter_type }
145
+ end
146
+
147
+ # Build (not sure where I am going with this yet)
148
+ # TODO: Refact: Currently the idea is to pre-load data
149
+ # this is slower when you are not accessing things, but
150
+ # it is easier to debug, so think about what I really want
151
+ # here
152
+ def build()
153
+ ruby_instance_methods
154
+ ruby_instance_method_names
155
+ signatures
62
156
  end
63
157
 
158
+ # Debug
159
+ #
160
+ # Refact: PATTERN: Come up it an debug inclusion system so that
161
+ # so that debug helpers can be included for development and excluded
162
+ # for production
163
+ # @param format [String] format: <value for format> (optional)
64
164
  def debug(format: [:signatures])
65
- if format == :method_names
165
+ if format.include?(:method_names)
66
166
  puts '-' * 70
67
167
  puts 'Method Names'
68
168
  puts '-' * 70
@@ -71,7 +171,7 @@ module Peeky
71
171
  end
72
172
  end
73
173
 
74
- return unless format == :signatures
174
+ return unless format.include?(:signatures)
75
175
 
76
176
  puts '-' * 70
77
177
  puts 'Methods'
@@ -82,11 +182,15 @@ module Peeky
82
182
  private
83
183
 
84
184
  def ruby_instance_method_names
85
- @ruby_instance_method_names ||= instance.class.instance_methods(false).sort
185
+ @_ruby_instance_method_names ||= instance.class.instance_methods(false).sort
86
186
  end
87
187
 
88
188
  def ruby_instance_methods
89
- @ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
189
+ begin
190
+ @_ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
191
+ rescue => exception
192
+ puts exception
193
+ end
90
194
  end
91
195
  end
92
196
  end
@@ -0,0 +1,123 @@
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
+ # @return [Boolean] true when p available?
103
+ def p_available?
104
+ end
105
+
106
+ # Q danger will robinson!
107
+ def q_danger_will_robinson!
108
+ end
109
+
110
+ # Z complex
111
+ #
112
+ # @param aaa [String] aaa (required)
113
+ # @param bbb [String] bbb (optional)
114
+ # @param ccc [Array<Object>] *ccc - list of ccc
115
+ # @param ddd [String] ddd: <value for ddd> (required)
116
+ # @param eee [String] eee: <value for eee> (optional)
117
+ # @param fff [<key: value>...] **fff - list of key/values
118
+ # @param ggg [Block] &ggg
119
+ def z_complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
120
+ end
121
+ end
122
+ end
123
+ 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
@@ -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)
@@ -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,142 @@
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
+ result.push "#{@indent}# @return [Boolean] true when #{method_signature.name.to_s.humanize.downcase}" if method_signature.name.to_s.end_with?('?')
126
+
127
+ render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
128
+ render_signature.indent = @indent
129
+ render_signature.style = :default
130
+ result.push render_signature.render
131
+ end
132
+ result.push '' unless result.length.zero?
133
+ result
134
+ end
135
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
136
+
137
+ def render_end
138
+ "#{@indent}end"
139
+ end
140
+ end
141
+ end
142
+ 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
@@ -4,14 +4,54 @@ module Peeky
4
4
  module Renderer
5
5
  # Render: Method Signature in compact format
6
6
  #
7
- # Example output:
7
+ # === Example output (:default):
8
+ # def simple(first_param)
9
+ # end
10
+ #
11
+ # def complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
12
+ # end
13
+ #
14
+ # === Example output (:compact):
8
15
  # def simple(first_param); end
9
16
  # def complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg); end
10
17
  class MethodSignatureRender
18
+ # Indentation prefix as a string.
19
+ #
20
+ # Defaults to ''
21
+ #
22
+ # If you were writing a class into a file with an existing
23
+ # module, you may set the indent to ' ' if you wanted this
24
+ # render to indent by two spaces
25
+ attr_accessor :indent
26
+
27
+ # Style of method rendering [:default, :compact]
28
+ #
29
+ # default:: will render +def method_name+ and +end+ on separate lines
30
+ # compact:: will render +def method_name+ and +end+ on same line
31
+ # @return [Symbol] style
32
+ attr_accessor :style
33
+
34
+ # Method signature stores a MethodInfo object
11
35
  attr_reader :method_signature
12
36
 
13
- def initialize(method_signature)
37
+ def initialize(method_signature, **_opts)
14
38
  @method_signature = method_signature
39
+ @indent = ''
40
+ @style = :compact
41
+ end
42
+
43
+ # Render the method signature in the selected style
44
+ def render
45
+ output = []
46
+ if @style == :compact
47
+ signature = "#{render_signature};"
48
+ output.push "#{signature.ljust(80)}#{render_end}"
49
+ end
50
+ if @style == :default
51
+ output.push render_signature
52
+ output.push render_end
53
+ end
54
+ output.join("\n")
15
55
  end
16
56
 
17
57
  def render_signature
@@ -19,20 +59,11 @@ module Peeky
19
59
 
20
60
  formatted_parameters = method_signature.parameters.map(&:signature_format).join(', ')
21
61
  params = formatted_parameters.length.zero? ? '' : "(#{formatted_parameters})"
22
- "def #{name}#{params}"
62
+ "#{@indent}def #{name}#{params}"
23
63
  end
24
64
 
25
65
  def render_end
26
- 'end'
27
- end
28
-
29
- def render
30
- signature = "#{render_signature};"
31
- "#{signature.ljust(80)}#{render_end}"
32
- end
33
-
34
- def debug
35
- puts render
66
+ "#{@indent}end"
36
67
  end
37
68
  end
38
69
  end
@@ -22,14 +22,22 @@ module Peeky
22
22
  # puts ggg # &ggg is block with many calling options, example - instance_eval(&block) if block_given?
23
23
  # end
24
24
  class MethodSignatureWithDebugRender
25
+ # Method signature stores a MethodInfo object
25
26
  attr_reader :method_signature
26
27
 
27
- def initialize(method_signature)
28
+ def initialize(method_signature, **_opts)
28
29
  @method_signature = method_signature
29
30
 
30
31
  @render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
31
32
  end
32
33
 
34
+ # Render the method with debug statements for each parameter
35
+ def render
36
+ render_method
37
+ end
38
+
39
+ private
40
+
33
41
  def render_method
34
42
  name = method_signature.name
35
43
 
@@ -38,7 +46,7 @@ module Peeky
38
46
  indent = ' '
39
47
  output += "#{indent}puts 'method name: #{name}'\n"
40
48
 
41
- output += render_logic(indent, 30)
49
+ output += render_debug_logic(indent, 30)
42
50
 
43
51
  indent = ''
44
52
  output += "#{indent}#{@render_signature.render_end}\n"
@@ -47,7 +55,7 @@ module Peeky
47
55
  end
48
56
 
49
57
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
50
- def render_logic(indent, size)
58
+ def render_debug_logic(indent, size)
51
59
  output = ''
52
60
  method_signature.parameters.each do |parameter|
53
61
  line = ''
@@ -73,14 +81,6 @@ module Peeky
73
81
  output
74
82
  end
75
83
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
76
-
77
- def render
78
- render_method
79
- end
80
-
81
- def debug
82
- puts render
83
- end
84
84
  end
85
85
  end
86
86
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+
5
+ module Peeky
6
+ module Tools
7
+ # Generate Yard Documentation
8
+ class GenerateYardDocumentation
9
+
10
+
11
+ def initialize
12
+ end
13
+
14
+ def debug
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Peeky
4
- VERSION = '0.0.19'
4
+ VERSION = '0.0.25'
5
5
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.required_ruby_version = '>= 2.5'
7
7
  spec.name = 'peeky'
8
8
  spec.version = Peeky::VERSION
9
- spec.authors = ['David C']
9
+ spec.authors = ['David Cruwys']
10
10
  spec.email = ['david@ideasmen.com.au']
11
11
 
12
12
  spec.summary = 'Extracting meta data from ruby classes'
@@ -41,5 +41,6 @@ Gem::Specification.new do |spec|
41
41
  spec.require_paths = ['lib']
42
42
  # spec.extensions = ['ext/peeky/extconf.rb']
43
43
 
44
+ spec.add_dependency 'activesupport'
44
45
  # spec.add_dependency 'tty-box', '~> 0.5.0'
45
46
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peeky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.19
4
+ version: 0.0.25
5
5
  platform: ruby
6
6
  authors:
7
- - David C
7
+ - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |2
14
28
  Peeky is a Ruby GEM for peaking into ruby classes and extracting meta data.
15
29
  You can use this meta data to recreate classes, interfaces, documentation etc.
@@ -40,16 +54,20 @@ files:
40
54
  - hooks/pre-commit
41
55
  - hooks/update-version
42
56
  - lib/peeky.rb
57
+ - lib/peeky/api.rb
43
58
  - lib/peeky/attr_info.rb
44
59
  - lib/peeky/class_info.rb
60
+ - lib/peeky/example/yard_sample.rb
45
61
  - lib/peeky/method_info.rb
46
62
  - lib/peeky/parameter_info.rb
47
63
  - lib/peeky/predicates/attr_reader_predicate.rb
48
64
  - lib/peeky/predicates/attr_writer_predicate.rb
49
65
  - lib/peeky/renderer/class_interface_render.rb
66
+ - lib/peeky/renderer/class_interface_yard_render.rb
50
67
  - lib/peeky/renderer/method_call_minimum_params_render.rb
51
68
  - lib/peeky/renderer/method_signature_render.rb
52
69
  - lib/peeky/renderer/method_signature_with_debug_render.rb
70
+ - lib/peeky/tools/generate_yard_documentation.rb
53
71
  - lib/peeky/version.rb
54
72
  - peeky.gemspec
55
73
  homepage: http://appydave.com/gems/peeky