rspec-jumpstart 1.1.1

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 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: []