rspec-jumpstart 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72abb831cb7e020319ad9069bcdfaba93eecb595
4
+ data.tar.gz: 88b785a7ee71f18bfa1c6c0a4dd291d34b58001e
5
+ SHA512:
6
+ metadata.gz: 8d81a980d209b1d09931775637a75bfb121ef15e9f3872960f5ec864c000ca441fa73855ae87084e52f3336be612103ab2ffad26ae8c7295bf58ae3831b5abc2
7
+ data.tar.gz: e52935530d0bc8bdca3b44b00f57477eb2cecb0e3d2c59b177da8f9384b85df210f4e8164935283a4a53d8b711f8b29b564e2b77ebf67b3da19e104f886c154b
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # rspec-jumpstart [dir/*.rb]
4
+ #
5
+
6
+ require 'optparse'
7
+ require 'rspec_jumpstart/version'
8
+ require 'rspec_jumpstart/generator'
9
+
10
+ print_version = false
11
+ force_write = false
12
+ dry_run = false
13
+ rails_mode = false
14
+ spec_dir = './spec'
15
+ delta_path = nil
16
+ exc_spec_dir = nil
17
+ full_path = nil
18
+
19
+ opt = OptionParser.new
20
+ opt.on('-f', 'Create if absent or append to the existing spec') { |_| force_write = true }
21
+ opt.on('--force', '') { |_| force_write = true }
22
+
23
+ opt.on('-n', 'Dry run mode (shows generated code to console)') { |_| dry_run = true }
24
+ opt.on('--dry-run', '') { |_| dry_run = true }
25
+
26
+ opt.on('-r', 'Run in Rails mode') { |_| rails_mode = true }
27
+ opt.on('--rails', '') { |_| rails_mode = true }
28
+
29
+ opt.on('-o VAL', 'Output directory (default: ./spec)') { |dir| spec_dir = dir }
30
+ opt.on('--output-dir VAL', '') { |dir| spec_dir = dir }
31
+
32
+ opt.on('-e VAL', 'Exclude directory (default: ./spec/no-run/**)') { |dir| exc_spec_dir = dir }
33
+ opt.on('--exc-dir VAL', '') { |dir| exc_spec_dir = dir }
34
+
35
+ opt.on('-D VAL', 'Delta template path (e.g. ./rails_controller_delta.erb)') { |path| delta_path = path }
36
+ opt.on('--delta-template VAL', '') { |path| delta_path = path }
37
+
38
+ opt.on('-F VAL', 'Full template path (e.g. ./rails_controller_full.erb)') { |path| full_path = path }
39
+ opt.on('--full-template VAL', '') { |path| full_path = path }
40
+
41
+ opt.on('-v', 'Print version') { |_| print_version = true }
42
+ opt.on('--version', '') { |_| print_version = true }
43
+
44
+ args = opt.parse(ARGV)
45
+
46
+ if print_version
47
+ puts "rspec-jumpstart #{RSpecJumpstart::VERSION}"
48
+ exit 0
49
+ end
50
+
51
+ dir_or_file = args.first
52
+ if dir_or_file.nil?
53
+ puts "Usage: rspec-jumpstart [dir/*.rb] -options"
54
+ exit 1
55
+ end
56
+ unless dir_or_file.match(/.rb$/)
57
+ dir_or_file = dir_or_file.gsub(/\/$/, '') + "/**/*.rb"
58
+ end
59
+
60
+ delta_template = nil
61
+ if delta_path
62
+ if File.exist?(delta_path)
63
+ delta_template = File.read(delta_path)
64
+ else
65
+ puts "'#{delta_path}' is not found."
66
+ exit 1
67
+ end
68
+ end
69
+
70
+ full_template = nil
71
+ if full_path
72
+ if File.exist?(full_path)
73
+ full_template = File.read(full_path)
74
+ else
75
+ puts "'#{full_path}' is not found."
76
+ exit 1
77
+ end
78
+ end
79
+
80
+ generator = RSpecJumpstart::Generator.new(spec_dir, delta_template, full_template)
81
+
82
+ files = Dir.glob(dir_or_file)
83
+ files -= Dir.glob(exc_spec_dir)
84
+
85
+ files.each do |file_path|
86
+ generator.write_spec(file_path, force_write, dry_run, rails_mode)
87
+ end
88
+
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec_jumpstart/generator'
4
+ require 'rspec_jumpstart/config'
5
+ require 'rspec_jumpstart/version'
6
+
7
+ #
8
+ # RSpecJumpstart Facade
9
+ #
10
+ module RSpecJumpstart
11
+ class << self
12
+ # Returns RSpecJumpstart's configuration object.
13
+ # @api private
14
+ def config
15
+ @config ||= RSpecJumpstart::Config.instance
16
+ yield @config if block_given?
17
+ @config
18
+ end
19
+ alias configure config
20
+
21
+ def version
22
+ VERSION
23
+ end
24
+ end
25
+
26
+ def self.write_spec(file_path, spec_dir = './spec', force_write = false, dry_run = false)
27
+ generator = RSpecJumpstart::Generator.new(spec_dir)
28
+ generator.write_spec(file_path, force_write, dry_run)
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module RSpecJumpstart
6
+ # Global configuration affecting all threads.
7
+ class Config
8
+ include Singleton
9
+
10
+ attr_accessor :behaves_like_exclusions
11
+
12
+ def initialize
13
+ @behaves_like_exclusions = []
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'rspec_jumpstart'
5
+ require 'rspec_jumpstart/erb_templates'
6
+
7
+ #
8
+ # ERB instance provider
9
+ #
10
+ module RSpecJumpstart
11
+ class ERBFactory
12
+
13
+ def initialize(custom_template)
14
+ @custom_template = custom_template
15
+ end
16
+
17
+ #
18
+ # Returns ERB instance for creating new spec
19
+ #
20
+ def get_instance_for_new_spec(rails_mode, target_path)
21
+ template = get_erb_template(@custom_template, true, rails_mode, target_path)
22
+ ERB.new(template, nil, '-', '_new_spec_code')
23
+ end
24
+
25
+ #
26
+ # Returns ERB instance for appending lacking tests
27
+ #
28
+ def get_instance_for_appending(rails_mode, target_path)
29
+ template = get_erb_template(@custom_template, false, rails_mode, target_path)
30
+ ERB.new(template, nil, '-', '_additional_spec_code')
31
+ end
32
+
33
+ private
34
+
35
+ #
36
+ # Returns ERB template
37
+ #
38
+ def get_erb_template(custom_template, is_full, rails_mode, target_path)
39
+ if custom_template
40
+ custom_template
41
+ elsif rails_mode && target_path.match(/controllers/)
42
+ get_rails_controller_template(is_full)
43
+ elsif rails_mode && target_path.match(/models/)
44
+ get_rails_model_template(is_full)
45
+ elsif rails_mode && target_path.match(/helpers/)
46
+ get_rails_helper_template(is_full)
47
+ else
48
+ get_basic_template(is_full)
49
+ end
50
+ end
51
+
52
+ def get_rails_controller_template(is_full)
53
+ if is_full
54
+ RSpecJumpstart::ERBTemplates::RAILS_CONTROLLER_NEW_SPEC_TEMPLATE
55
+ else
56
+ RSpecJumpstart::ERBTemplates::RAILS_CONTROLLER_METHODS_PART_TEMPLATE
57
+ end
58
+ end
59
+
60
+ def get_rails_model_template(is_full)
61
+ if is_full
62
+ RSpecJumpstart::ERBTemplates::RAILS_MODEL_NEW_SPEC_TEMPLATE
63
+ else
64
+ RSpecJumpstart::ERBTemplates::RAILS_MODEL_METHODS_PART_TEMPLATE
65
+ end
66
+ end
67
+
68
+ def get_rails_helper_template(is_full)
69
+ if is_full
70
+ RSpecJumpstart::ERBTemplates::RAILS_HELPER_NEW_SPEC_TEMPLATE
71
+ else
72
+ RSpecJumpstart::ERBTemplates::RAILS_HELPER_METHODS_PART_TEMPLATE
73
+ end
74
+ end
75
+
76
+ def get_basic_template(is_full)
77
+ if is_full
78
+ RSpecJumpstart::ERBTemplates::BASIC_NEW_SPEC_TEMPLATE
79
+ else
80
+ RSpecJumpstart::ERBTemplates::BASIC_METHODS_PART_TEMPLATE
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'rspec_jumpstart'
5
+
6
+ #
7
+ # ERB templates
8
+ #
9
+ module RSpecJumpstart
10
+ module ERBTemplates
11
+
12
+ BASIC_METHODS_PART_TEMPLATE = <<~SPEC
13
+ <%- methods_to_generate.map { |method| %>
14
+ # TODO: auto-generated
15
+ describe '<%= decorated_name(method) %>' do
16
+ it '<%= method.name %>' do
17
+ <%- if get_instantiation_code(c, method) -%><%= get_instantiation_code(c, method) %><%- end -%>
18
+ <%- if get_params_initialization_code(method) -%><%= get_params_initialization_code(method) %><%- end -%>
19
+ result = <%= get_method_invocation_code(c, method).sub(c.to_s,'described_class') %>
20
+
21
+ expect(result).not_to be_nil
22
+ end
23
+ end
24
+ <% } %>
25
+ SPEC
26
+
27
+ BASIC_NEW_SPEC_TEMPLATE = <<~SPEC
28
+ # frozen_string_literal: true
29
+
30
+ <% if rails_mode then %>require 'rails_helper'
31
+ <% else -%>require 'spec_helper'
32
+ <% end -%>
33
+ <% unless rails_mode then %>require '<%= self_path %>'
34
+ <% end -%>
35
+
36
+ RSpec.describe <%= to_string_namespaced_path_whole(file_path) %> do
37
+ <%= ERB.new(BASIC_METHODS_PART_TEMPLATE, nil, '-').result(binding) -%>
38
+ end
39
+ SPEC
40
+
41
+ RAILS_CONTROLLER_METHODS_PART_TEMPLATE = <<~SPEC
42
+ <%- methods_to_generate.map { |method| %>
43
+ # TODO: auto-generated
44
+ describe '<%= decorated_name(method) %>' do
45
+ it '<%= get_rails_http_method(method.name).upcase %> <%= method.name %>' do
46
+ <%= get_rails_http_method(method.name) %> :<%= method.name %>, {}, {}
47
+
48
+ expect(response).to have_http_status(:ok)
49
+ end
50
+ end
51
+ <% } %>
52
+ SPEC
53
+
54
+ RAILS_CONTROLLER_NEW_SPEC_TEMPLATE = <<~SPEC
55
+ # frozen_string_literal: true
56
+
57
+ require 'rails_helper'
58
+
59
+ RSpec.describe <%= (to_string_namespaced_path(self_path) + get_complete_class_name(c)).split('::').uniq.join('::') %>, type: :controller do
60
+ <%= ERB.new(RAILS_CONTROLLER_METHODS_PART_TEMPLATE, nil, '-').result(binding) -%>
61
+ end
62
+ SPEC
63
+
64
+ RAILS_MODEL_METHODS_PART_TEMPLATE = <<~SPEC
65
+ <%= ERB.new(RAILS_MODEL_SCOPE_PART_TEMPLATE, nil, '-').result(binding) -%>
66
+ <%- methods_to_generate.map { |method| %>
67
+ # TODO: auto-generated
68
+ describe '<%= decorated_name(method) %>' do
69
+ it '<%= method.name %>' do
70
+ <%- if get_instantiation_code(c, method) -%><%= get_instantiation_code(c, method) %><%- end -%>
71
+ <%- if get_params_initialization_code(method) -%><%= get_params_initialization_code(method) %><%- end -%>
72
+ result = <%= get_method_invocation_code(c, method).sub(c.to_s,'described_class') %>
73
+
74
+ expect(result).not_to be_nil
75
+ end
76
+ end
77
+ <% } %>
78
+ SPEC
79
+
80
+ RAILS_MODEL_SCOPE_PART_TEMPLATE = <<~SPEC
81
+ <%- scope_methods_to_generate.map { |method| %>
82
+ # TODO: auto-generated
83
+ describe '.<%= method %>' do # scope test
84
+ it 'supports named scope <%= method %>' do
85
+ expect(described_class.limit(3).<%= method %>).to all(be_a(described_class))
86
+ end
87
+ end<% } %>
88
+ SPEC
89
+
90
+ RAILS_MODEL_NEW_SPEC_TEMPLATE = <<~SPEC
91
+ # frozen_string_literal: true
92
+
93
+ require 'rails_helper'
94
+ require 'shared_model_stuff'
95
+
96
+ RSpec.describe <%= (to_string_namespaced_path(self_path) + get_complete_class_name(c)).split('::').uniq.join('::') %>, type: :model do
97
+ it_behaves_like 'real_model'
98
+ <%= ERB.new(RAILS_MODEL_METHODS_PART_TEMPLATE, nil, '-').result(binding) -%>
99
+ end
100
+ SPEC
101
+
102
+ RAILS_HELPER_METHODS_PART_TEMPLATE = <<~SPEC
103
+ <%- methods_to_generate.map { |method| %>
104
+ # TODO: auto-generated
105
+ describe '<%= decorated_name(method) %>' do
106
+ it 'works' do
107
+ result = <%= get_rails_helper_method_invocation_code(method) %>
108
+
109
+ expect(result).not_to be_nil
110
+ end
111
+ end
112
+ <% } %>
113
+ SPEC
114
+
115
+ RAILS_HELPER_NEW_SPEC_TEMPLATE = <<~SPEC
116
+ # frozen_string_literal: true
117
+
118
+ require 'rails_helper'
119
+
120
+ RSpec.describe <%= (to_string_namespaced_path(self_path) + get_complete_class_name(c)).split('::').uniq.join('::') %>, type: :helper do
121
+ <%= ERB.new(RAILS_HELPER_METHODS_PART_TEMPLATE, nil, '-').result(binding) -%>
122
+ end
123
+ SPEC
124
+
125
+ end
126
+ end
@@ -0,0 +1,392 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rdoc'
4
+ require 'rspec_jumpstart'
5
+ require 'rspec_jumpstart/erb_factory'
6
+ require 'rspec_jumpstart/erb_templates'
7
+ require 'rspec_jumpstart/rdoc_factory'
8
+
9
+ #
10
+ # RSpec Code Generator
11
+ #
12
+ module RSpecJumpstart
13
+ class Generator
14
+ include RSpecJumpstart::ERBTemplates
15
+
16
+ attr_accessor :spec_dir, :delta_template, :full_template
17
+
18
+ def initialize(spec_dir = './spec', delta_template = nil, full_template = nil)
19
+ @spec_dir = spec_dir.gsub(/\/$/, '')
20
+ @delta_template = delta_template
21
+ @full_template = full_template
22
+ end
23
+
24
+ #
25
+ # Writes new spec or appends to the existing spec.
26
+ #
27
+ def write_spec(file_path, force_write = false, dry_run = false, rails_mode = false)
28
+ begin
29
+ code = ''
30
+ class_or_module = RSpecJumpstart::RDocFactory.get_rdoc_class_or_module(file_path)
31
+ if class_or_module
32
+ spec_path = get_spec_path(file_path)
33
+
34
+ code = if force_write && File.exist?(spec_path)
35
+ append_to_existing_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
36
+ else
37
+ create_new_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
38
+ end
39
+ else
40
+ puts red("#{file_path} skipped (Class/Module not found).")
41
+ end
42
+ rescue Exception => e
43
+ puts red("#{file_path} aborted - #{e.message}")
44
+ end
45
+
46
+ code
47
+ end
48
+
49
+ #
50
+ # Gets the complete class name from RDoc::NormalClass/RDoc::NormalModule instance.
51
+ #
52
+ def get_complete_class_name(class_or_module, name = class_or_module.name)
53
+ if class_or_module.parent.name && class_or_module.parent.is_a?(RDoc::NormalModule)
54
+ get_complete_class_name(class_or_module.parent, "#{class_or_module.parent.name}::#{name}")
55
+ else
56
+ name
57
+ end
58
+ end
59
+
60
+ def to_string_namespaced_path(self_path)
61
+ path = self_path.split('/').map { |x| camelize(x) }[1..-2].uniq.join('::')
62
+ path.empty? ? '' : path + '::'
63
+ end
64
+
65
+ def to_string_namespaced_path_whole(self_path)
66
+ self_path.
67
+ sub('.rb', '').
68
+ split('/').
69
+ map { |x| camelize(x) }[2..-1].
70
+ uniq.
71
+ join('::')
72
+ end
73
+
74
+ def decorated_name(method)
75
+ (method.singleton ? '.' : '#') + method.name
76
+ end
77
+
78
+ #
79
+ # Returns spec file path.
80
+ # e.g. "lib/foo/bar_baz.rb" -> "spec/foo/bar_baz_spec.rb"
81
+ #
82
+ def get_spec_path(file_path)
83
+ spec_dir + '/' + file_path.
84
+ gsub(/^\.\//, '').
85
+ gsub(%r{^(lib/)|(app/)}, '').
86
+ sub(/\.rb$/, '_spec.rb')
87
+ end
88
+
89
+ #
90
+ # Returns string value to require.
91
+ # e.g. "lib/foo/bar_baz.rb" -> "foo/bar_baz"
92
+ #
93
+ def to_string_value_to_require(file_path)
94
+ file_path.gsub(%r{^(lib/)|(app/)}, '').gsub(/\.rb$/, '')
95
+ end
96
+
97
+ #
98
+ # Returns snake_case name.
99
+ # e.g. FooBar -> "foo_bar"
100
+ #
101
+ def instance_name(c)
102
+ c.name.
103
+ gsub(/::/, '/').
104
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
105
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
106
+ tr('-', '_').
107
+ downcase
108
+ end
109
+
110
+ #
111
+ # Extracts parameter names as an *Array*.
112
+ # e.g. "()" -> []
113
+ # e.g. "(a, b = 'foo')" -> ["a", "b"]
114
+ #
115
+ def to_param_names_array(params)
116
+ params.
117
+ split(',').
118
+ map { |p| p.gsub(/[()\s]/, '').gsub(/=.+$/, '') }.
119
+ reject { |p| p.nil? || p.empty? }
120
+ end
121
+
122
+ #
123
+ # Returns params part
124
+ # e.g. ["a","b"] -> "(a, b)"
125
+ # e.g. [] -> ""
126
+ #
127
+ def to_params_part(params)
128
+ param_csv = to_param_names_array(params).join(', ')
129
+ param_csv.empty? ? '' : "(#{param_csv})"
130
+ end
131
+
132
+ #
133
+ # Creates new spec.
134
+ #
135
+ # rubocop:disable Metrics/AbcSize
136
+ def create_new_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
137
+ # These names are used in ERB template, don't delete.
138
+ methods_to_generate = public_methods_found(class_or_module)
139
+ scope_methods_to_generate = scopes(class_or_module, file_path, spec_path)
140
+ c = class_or_module
141
+ self_path = to_string_value_to_require(file_path)
142
+ # rubocop:enable Lint/UselessAssignment
143
+
144
+ erb = RSpecJumpstart::ERBFactory.
145
+ new(@full_template).
146
+ get_instance_for_new_spec(rails_mode, file_path)
147
+ code = erb.result(binding)
148
+
149
+ if dry_run
150
+ puts "----- #{spec_path} -----"
151
+ puts code
152
+ elsif File.exist?(spec_path)
153
+ # puts yellow("#{spec_path} already exists.")
154
+ else
155
+ FileUtils.mkdir_p(File.dirname(spec_path))
156
+ File.open(spec_path, 'w') { |f| f.write(code) }
157
+ puts green("#{spec_path} created.")
158
+ end
159
+
160
+ code
161
+ end
162
+
163
+ def public_methods_found(class_or_module)
164
+ class_or_module.method_list.select do |m|
165
+ m.visibility.equal?(:public) && m.name != 'new'
166
+ end
167
+ end
168
+
169
+ # rubocop:enable Metrics/AbcSize
170
+
171
+ #
172
+ # Appends new tests to the existing spec.
173
+ #
174
+ # rubocop:disable Metrics/AbcSize
175
+ def append_to_existing_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
176
+ existing_spec = File.read(spec_path)
177
+ if skip?(existing_spec)
178
+ return
179
+ end
180
+ lacking_methods = public_methods_found(class_or_module).
181
+ reject { |m| existing_spec.match(signature(m)) }
182
+
183
+ scope_methods_to_generate = scopes(class_or_module, file_path, spec_path)
184
+ if lacking_methods.empty? && scope_methods_to_generate.empty?
185
+ # puts yellow("#{spec_path} skipped.")
186
+ else
187
+ # These names are used in ERB template, don't delete.
188
+ methods_to_generate = lacking_methods
189
+ c = class_or_module
190
+ # rubocop:enable Lint/UselessAssignment
191
+
192
+ erb = RSpecJumpstart::ERBFactory.new(@delta_template).get_instance_for_appending(rails_mode, spec_path)
193
+ additional_spec = erb.result(binding)
194
+
195
+ last_end_not_found = true
196
+ code = existing_spec.split("\n").reverse.reject do |line|
197
+ before_modified = last_end_not_found
198
+ last_end_not_found = line.gsub(/#.+$/, '').strip != 'end' if before_modified
199
+ before_modified
200
+ end.reverse.join("\n") + "\n" + additional_spec + "\nend\n"
201
+
202
+ if dry_run
203
+ puts "----- #{spec_path} -----"
204
+ puts code
205
+ else
206
+ File.open(spec_path, 'w') { |f| f.write(code) }
207
+ end
208
+ puts green("#{spec_path} modified.")
209
+ end
210
+
211
+ code
212
+ end
213
+
214
+ def skip?(text)
215
+ RSpecJumpstart.config.behaves_like_exclusions.each do |exclude_pattern|
216
+ return true if text.match(exclude_pattern)
217
+ end
218
+
219
+ false
220
+ end
221
+
222
+ # rubocop:enable Metrics/AbcSize
223
+
224
+ # -----
225
+ # Code generation
226
+ # -----
227
+
228
+ #
229
+ # e.g.
230
+ # a = double('a')
231
+ # b = double('b')
232
+ # bar_baz = BarBaz.new(a, b)
233
+ #
234
+ def get_instantiation_code(c, method)
235
+ if method.singleton
236
+ ''
237
+ else
238
+ constructor = c.method_list.find { |m| m.name == 'new' }
239
+ if constructor.nil?
240
+ " #{instance_name(c)} = described_class.new\n"
241
+ else
242
+ get_params_initialization_code(constructor) +
243
+ " #{instance_name(c)} = described_class.new#{to_params_part(constructor.params)}\n"
244
+ end
245
+ end
246
+ end
247
+
248
+ #
249
+ # e.g.
250
+ # a = double('a')
251
+ # b = double('b')
252
+ #
253
+ def get_params_initialization_code(method)
254
+ code = to_param_names_array(method.params).map do |p|
255
+ x = p.sub('*', '').sub('&', '')
256
+ " #{x} = double('#{x}')" unless x.empty?
257
+ end.compact.join("\n")
258
+ code.empty? ? '' : "#{code}\n"
259
+ end
260
+
261
+ #
262
+ # e.g. BarBaz.do_something(a, b) { |c| }
263
+ #
264
+ def get_method_invocation_code(c, method)
265
+ target = method.singleton ? 'described_class' : instance_name(c)
266
+ "#{target}.#{method.name}#{to_params_part(method.params)}#{get_block_code(method)}"
267
+ end
268
+
269
+ #
270
+ # e.g. do_something(a, b) { |c| }
271
+ #
272
+ def get_rails_helper_method_invocation_code(method)
273
+ "#{method.name}#{to_params_part(method.params)}#{get_block_code(method)}"
274
+ end
275
+
276
+ #
277
+ # e.g. { |a, b| }
278
+ #
279
+ def get_block_code(method)
280
+ if method.block_params.nil? || method.block_params.empty?
281
+ ''
282
+ else
283
+ " { |#{method.block_params}| }"
284
+ end
285
+ end
286
+
287
+ def get_rails_http_method(method_name)
288
+ RAILS_RESOURCE_METHOD_AND_HTTP_METHOD[method_name] || 'get'
289
+ end
290
+
291
+ private
292
+
293
+ def parse_sexp(sexp, scopes, methods, stack = [])
294
+ case sexp[0]
295
+ when :module
296
+ parse_sexp(sexp[2], scopes, methods, stack + [sexp[0], sexp[1][1][1]])
297
+
298
+ when :vcall
299
+ name = sexp[1][1]
300
+ return if name.eql?('private')
301
+
302
+ when :command
303
+ if sexp[1][0] == :@ident && sexp[1][1] == 'scope'
304
+ name = sexp[2][1][0][1][1][1]
305
+ scopes << name
306
+ end
307
+
308
+ when :class
309
+ parse_sexp(sexp[3], scopes, methods, stack + [sexp[0], sexp[1][1][1]])
310
+
311
+ when :def
312
+ name = sexp[1][1]
313
+ # line_number = sexp[1][2][0]
314
+
315
+ parse_sexp(sexp[3], scopes, methods, stack + [sexp[0], sexp[1][1]])
316
+
317
+ # puts "#{line_number}: Method: #{stack.last}##{name}\n"
318
+ methods << name
319
+ else
320
+ if sexp.is_a?(Array)
321
+ sexp.each { |s| parse_sexp(s, scopes, methods, stack) if s.is_a?(Array) }
322
+ end
323
+ end
324
+ end
325
+
326
+ require 'ripper'
327
+
328
+ def scopes(_klass, file_path, spec_path)
329
+ content = File.read(file_path)
330
+ spec_content = (
331
+ begin
332
+ File.read(spec_path)
333
+ rescue
334
+ ''
335
+ end)
336
+ sexp = Ripper.sexp(content)
337
+ methods = []
338
+ scopes = []
339
+
340
+ parse_sexp(sexp, scopes, methods)
341
+
342
+ scope_methods = []
343
+ scopes.each do |method|
344
+ unless spec_content.include?("'.#{method}'")
345
+ scope_methods << method
346
+ end
347
+ end
348
+
349
+ scope_methods
350
+ end
351
+
352
+ def signature(method)
353
+ "'#{decorated_name(method).sub('?', '\?').gsub('[', '\[').gsub(']', '\]')}'"
354
+ end
355
+
356
+ def colorize(text, color_code)
357
+ "#{color_code}#{text}\033[0m"
358
+ end
359
+
360
+ def red(text)
361
+ colorize(text, "\033[31m")
362
+ end
363
+
364
+ def green(text)
365
+ colorize(text, "\033[32m")
366
+ end
367
+
368
+ def blue(text)
369
+ colorize(text, "\033[34m")
370
+ end
371
+
372
+ def yellow(text)
373
+ colorize(text, "\033[33m")
374
+ end
375
+
376
+ def camelize(str)
377
+ str.split('_').map { |w| w.capitalize }.join
378
+ end
379
+
380
+ RAILS_RESOURCE_METHOD_AND_HTTP_METHOD = {
381
+ 'index' => 'get',
382
+ 'new' => 'get',
383
+ 'create' => 'post',
384
+ 'show' => 'get',
385
+ 'edit' => 'get',
386
+ 'update' => 'patch',
387
+ 'destroy' => 'delete'
388
+ }.freeze
389
+
390
+ end
391
+ end
392
+
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rdoc'
4
+ require 'rdoc/generator'
5
+ require 'rdoc/options'
6
+ require 'rdoc/parser/ruby'
7
+ require 'rdoc/stats'
8
+ require 'rspec_jumpstart'
9
+
10
+ #
11
+ # RDoc instance factory
12
+ #
13
+ module RSpecJumpstart
14
+ class RDocFactory
15
+
16
+ #
17
+ # Returns RDoc::NormalClass/RDoc::NormalModule instance.
18
+ #
19
+ def self.get_rdoc_class_or_module(file_path)
20
+ top_level = get_ruby_parser(file_path).scan
21
+ extract_target_class_or_module(top_level)
22
+ end
23
+
24
+ #
25
+ # Creates new RDoc::Parser::Ruby instance.
26
+ #
27
+ def self.get_ruby_parser(file_path)
28
+ top_level = RDoc::TopLevel.new(file_path)
29
+ if RUBY_VERSION.to_f < 2.0
30
+ # reset is removed since 2.0
31
+ RDoc::TopLevel.reset
32
+ end
33
+
34
+ # RDoc::Stats initialization
35
+ if defined?(RDoc::Store)
36
+ # RDoc 4.0.0 requires RDoc::Store internally.
37
+ store = RDoc::Store.new
38
+ top_level.store = store
39
+ stats = RDoc::Stats.new(store, 1)
40
+ else
41
+ stats = RDoc::Stats.new(1)
42
+ end
43
+
44
+ RDoc::Parser::Ruby.new(
45
+ top_level,
46
+ file_path,
47
+ File.read(file_path),
48
+ RDoc::Options.new,
49
+ stats
50
+ )
51
+ end
52
+
53
+ #
54
+ # Extracts RDoc::NormalClass/RDoc::NormalModule from RDoc::TopLevel.
55
+ #
56
+ def self.extract_target_class_or_module(top_level)
57
+ c = top_level.classes.first
58
+ if c.nil?
59
+ m = top_level.modules.first
60
+ if m.nil?
61
+ top_level.is_a?(RDoc::NormalModule) ? top_level : nil
62
+ else
63
+ extract_target_class_or_module(m)
64
+ end
65
+ else
66
+ c
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Gem version
5
+ #
6
+ module RSpecJumpstart
7
+ VERSION = '1.1.1'
8
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-jumpstart
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Timothy Chambers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: rspec-jumpstart supports you writing tests for existing code.
14
+ email:
15
+ - tim@possibilogy.com
16
+ executables:
17
+ - rspec-jumpstart
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/rspec-jumpstart
22
+ - lib/rspec_jumpstart.rb
23
+ - lib/rspec_jumpstart/config.rb
24
+ - lib/rspec_jumpstart/erb_factory.rb
25
+ - lib/rspec_jumpstart/erb_templates.rb
26
+ - lib/rspec_jumpstart/generator.rb
27
+ - lib/rspec_jumpstart/rdoc_factory.rb
28
+ - lib/rspec_jumpstart/version.rb
29
+ homepage: https://github.com/tjchambers/rspec-jumpstart
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 2.5.1
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: rspec-jumpstart supports you writing tests for existing code.
53
+ test_files: []