spec_producer 0.11.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +73 -24
  5. data/lib/configuration.rb +41 -0
  6. data/lib/generators/spec_producer/install/install_generator.rb +13 -0
  7. data/lib/generators/spec_producer/install/templates/spec_producer +1 -0
  8. data/lib/spec_producer.rb +140 -47
  9. data/lib/spec_producer/factories_production_module.rb +17 -11
  10. data/lib/spec_producer/missing_files_module.rb +104 -41
  11. data/lib/spec_producer/missing_gems_module.rb +77 -0
  12. data/lib/spec_producer/producers.rb +10 -0
  13. data/lib/spec_producer/producers/base.rb +129 -0
  14. data/lib/spec_producer/producers/controllers_producer.rb +31 -0
  15. data/lib/spec_producer/producers/helpers_producer.rb +26 -0
  16. data/lib/spec_producer/producers/jobs_producer.rb +34 -0
  17. data/lib/spec_producer/producers/mailers_producer.rb +24 -0
  18. data/lib/spec_producer/producers/models_producer.rb +89 -0
  19. data/lib/spec_producer/producers/registry.rb +66 -0
  20. data/lib/spec_producer/producers/routes_producer.rb +44 -0
  21. data/lib/spec_producer/producers/serializers_producer.rb +46 -0
  22. data/lib/spec_producer/producers/views_producer.rb +25 -0
  23. data/lib/spec_producer/railtie.rb +13 -0
  24. data/lib/spec_producer/rspec_builders.rb +1 -0
  25. data/lib/spec_producer/rspec_builders/base.rb +148 -0
  26. data/lib/spec_producer/rspec_builders/builder.rb +220 -0
  27. data/lib/spec_producer/rspec_builders/matchers.rb +256 -0
  28. data/lib/spec_producer/spec_production_module.rb +80 -331
  29. data/lib/spec_producer/spec_runner.rb +14 -0
  30. data/lib/spec_producer/utils.rb +1 -0
  31. data/lib/spec_producer/utils/file_utils.rb +69 -0
  32. data/lib/spec_producer/version.rb +1 -1
  33. data/lib/tasks/spec_producer_tasks.rake +103 -0
  34. data/spec_producer.gemspec +6 -0
  35. metadata +111 -2
@@ -14,7 +14,7 @@ module SpecProducer::FactoriesProductionModule
14
14
  final_text = "FactoryGirl.define do\n"
15
15
  final_text << " factory :#{descendant.name.underscore}, :class => #{descendant.name} do\n"
16
16
 
17
- descendant.columns.each do |column|
17
+ descendant.columns.reject { |column| ['id', 'created_at', 'updated_at'].include? column.name }.each do |column|
18
18
  value = case column.type
19
19
  when :string then "'#{descendant.name.underscore.upcase}_#{column.name.underscore.upcase}'"
20
20
  when :text then "'#{descendant.name.underscore.upcase}_#{column.name.underscore.upcase}'"
@@ -31,28 +31,34 @@ module SpecProducer::FactoriesProductionModule
31
31
  final_text << " #{column.name} #{value}\n"
32
32
  end
33
33
 
34
+ descendant.reflections.each_pair do |key, reflection|
35
+ if reflection.macro == :has_one
36
+ final_text << " association :#{key.to_s}\n"
37
+ end
38
+ end
39
+
34
40
  final_text << " end\n"
35
41
  final_text << "end"
36
42
 
37
43
  if File.exists?(Rails.root.join("spec/factories/#{descendant.name.underscore}.rb"))
38
- puts '#'*100
39
- puts "Please, check whether the following lines are included in: spec/factories/" + descendant.name.underscore + ".rb\n"
40
- puts '#'*100
41
- puts "\n"
44
+ puts ('#'*100).colorize(:light_blue)
45
+ puts ("Please, check whether the following lines are included in: spec/factories/" + descendant.name.underscore + ".rb").colorize(:light_blue)
46
+ puts ('#'*100).colorize(:light_blue)
42
47
  puts final_text
48
+ puts "\n\n"
43
49
  else
44
50
  unless Dir.exists? Rails.root.join("spec")
45
- puts "Generating spec directory"
51
+ puts "Generating spec directory".colorize(:yellow)
46
52
  Dir.mkdir(Rails.root.join("spec"))
47
53
  end
48
54
 
49
55
  unless Dir.exists? Rails.root.join("spec/factories")
50
- puts "Generating spec/factories directory"
56
+ puts "Generating spec/factories directory".colorize(:yellow)
51
57
  Dir.mkdir(Rails.root.join("spec/factories"))
52
58
  end
53
59
 
54
60
  path = "spec/factories/#{descendant.name.underscore}.rb"
55
- puts "Producing factory file for: #{path}"
61
+ puts "Producing factory file for: #{path}".colorize(:green)
56
62
  f = File.open("#{Rails.root.join(path)}", 'wb+')
57
63
  f.write(final_text)
58
64
  f.close
@@ -61,8 +67,8 @@ module SpecProducer::FactoriesProductionModule
61
67
 
62
68
  nil
63
69
  rescue NameError => e
64
- puts "NameError '#{e}' was raised. Can't produce factories for this project."
70
+ puts "NameError '#{e}' was raised. Can't produce factories for this project.".colorize(:red)
65
71
  rescue Exception => e
66
- puts "Exception '#{e}' was raised. Skipping factories production."
72
+ puts "Exception '#{e}' was raised. Skipping factories production.".colorize(:red)
67
73
  end
68
- end
74
+ end
@@ -1,3 +1,5 @@
1
+ require 'colorize'
2
+
1
3
  module SpecProducer::MissingFilesModule
2
4
  def self.print_missing_model_specs(options = {})
3
5
  if options[:specific_namespace]
@@ -6,14 +8,16 @@ module SpecProducer::MissingFilesModule
6
8
  files_list = Dir["app/models/**/*.rb"]
7
9
  end
8
10
 
9
- puts "\n" << "## Searching for missing model specs..."
10
- files_list.each do |file|
11
- unless FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb'))
12
- puts "Missing model spec file for: #{file}"
13
- end
11
+ puts "\n" << "## Searching for missing model specs...".colorize(:light_blue)
12
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
13
+
14
+ missing.each do |file|
15
+ puts "Missing model spec file for: #{file}".colorize(:red)
14
16
  end
15
17
 
16
- nil
18
+ if missing.size == 0
19
+ puts "No missing model spec files found!".colorize(:green)
20
+ end
17
21
  end
18
22
 
19
23
  def self.print_missing_controller_specs(options = {})
@@ -23,14 +27,16 @@ module SpecProducer::MissingFilesModule
23
27
  files_list = Dir["app/controllers/**/*.rb"]
24
28
  end
25
29
 
26
- puts "\n" << "## Searching for missing controller specs..."
27
- files_list.each do |file|
28
- unless FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb'))
29
- puts "Missing controller spec file for: #{file}"
30
- end
30
+ puts "\n" << "## Searching for missing controller specs...".colorize(:light_blue)
31
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
32
+
33
+ missing.each do |file|
34
+ puts "Missing controller spec file for: #{file}".colorize(:red)
31
35
  end
32
36
 
33
- nil
37
+ if missing.size == 0
38
+ puts "No missing controller spec files found!".colorize(:green)
39
+ end
34
40
  end
35
41
 
36
42
  def self.print_missing_job_specs(options = {})
@@ -40,14 +46,16 @@ module SpecProducer::MissingFilesModule
40
46
  files_list = Dir["app/jobs/**/*.rb"]
41
47
  end
42
48
 
43
- puts "\n" << "## Searching for missing jobs specs..."
44
- files_list.each do |file|
45
- unless FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb'))
46
- puts "Missing job spec file for: #{file}"
47
- end
49
+ puts "\n" << "## Searching for missing jobs specs...".colorize(:light_blue)
50
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
51
+
52
+ missing.each do |file|
53
+ puts "Missing job spec file for: #{file}".colorize(:red)
48
54
  end
49
55
 
50
- nil
56
+ if missing.size == 0
57
+ puts "No missing job spec files found!".colorize(:green)
58
+ end
51
59
  end
52
60
 
53
61
  def self.print_missing_mailer_specs(options = {})
@@ -57,47 +65,102 @@ module SpecProducer::MissingFilesModule
57
65
  files_list = Dir["app/mailers/**/*.rb"]
58
66
  end
59
67
 
60
- puts "\n" << "## Searching for missing mailers specs..."
61
- files_list.each do |file|
62
- unless FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb'))
63
- puts "Missing mailer spec file for: #{file}"
64
- end
68
+ puts "\n" << "## Searching for missing mailers specs...".colorize(:light_blue)
69
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
70
+
71
+ missing.each do |file|
72
+ puts "Missing mailer spec file for: #{file}".colorize(:red)
65
73
  end
66
74
 
67
- nil
75
+ if missing.size == 0
76
+ puts "No missing mailer spec files found!".colorize(:green)
77
+ end
68
78
  end
69
79
 
70
80
  def self.print_missing_helper_specs(options = {})(options = {})
81
+ if options[:specific_namespace]
82
+ files_list = Dir["app/helpers/**/#{options[:specific_namespace].gsub('app/helpers', '')}/**/*.rb"]
83
+ else
84
+ files_list = Dir["app/helpers/**/*.rb"]
85
+ end
86
+
87
+ puts "\n" << "## Searching for missing helper specs...".colorize(:light_blue)
88
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
89
+
90
+ missing.each do |file|
91
+ puts "Missing helper spec file for: #{file}".colorize(:red)
92
+ end
93
+
94
+ if missing.size == 0
95
+ puts "No missing helper spec files found!".colorize(:green)
96
+ end
97
+ end
98
+
99
+ def self.print_missing_view_specs(options = {})
71
100
  if options[:specific_namespace]
72
- files_list = Dir["app/helpers/**/#{options[:specific_namespace].gsub('app/helpers', '')}/**/*.rb"]
101
+ files_list = Dir["app/views/**/#{options[:specific_namespace].gsub('app/views', '')}/**/*.erb"]
73
102
  else
74
- files_list = Dir["app/helpers/**/*.rb"]
103
+ files_list = Dir["app/views/**/*.erb"]
75
104
  end
76
105
 
77
- puts "\n" << "## Searching for missing helper specs..."
78
- files_list.each do |file|
79
- unless FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb'))
80
- puts "Missing helper spec file for: #{file}"
81
- end
106
+ puts "\n" << "## Searching for missing view specs...".colorize(:light_blue)
107
+ missing = files_list.select { |file| !FileTest.exists?("#{file.gsub('app/', 'spec/')}_spec.rb") }
108
+
109
+ missing.each do |file|
110
+ puts "Missing view spec file for: #{file}".colorize(:red)
82
111
  end
83
112
 
84
- nil
113
+ if missing.size == 0
114
+ puts "No missing view spec files found!".colorize(:green)
115
+ end
85
116
  end
86
117
 
87
- def self.print_missing_view_specs(options = {})
118
+ def self.print_missing_serializer_specs(options = {})
88
119
  if options[:specific_namespace]
89
- files_list = Dir["app/views/**/#{options[:specific_namespace].gsub('app/views', '')}/**/*.erb"]
120
+ files_list = Dir["app/serializers/**/#{options[:specific_namespace].gsub('app/serializers', '')}/**/*.erb"]
90
121
  else
91
- files_list = Dir["app/views/**/*.erb"]
122
+ files_list = Dir["app/serializers/**/*.erb"]
123
+ end
124
+
125
+ puts "\n" << "## Searching for missing serializers specs...".colorize(:light_blue)
126
+ missing = files_list.select { |file| !FileTest.exists?(file.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')) }
127
+
128
+ missing.each do |file|
129
+ puts "Missing serializer spec file for: #{file}".colorize(:red)
92
130
  end
93
131
 
94
- puts "\n" << "## Searching for missing view specs..."
95
- files_list.each do |file|
96
- unless FileTest.exists?("#{file.gsub('app/', 'spec/')}_spec.rb")
97
- puts "Missing spec file for: #{file}"
132
+ if missing.size == 0
133
+ puts "No missing serializer spec files found!".colorize(:green)
134
+ end
135
+ end
136
+
137
+ def self.print_missing_route_specs(options = {})
138
+ routes = Rails.application.routes.routes.map do |route|
139
+ path = route.path.spec.to_s.gsub(/\(\.:format\)/, "")
140
+ verb = %W{ GET POST PUT PATCH DELETE }.grep(route.verb).first.downcase.to_sym
141
+ controller = route.defaults[:controller]
142
+ action = route.defaults[:action]
143
+
144
+ if controller.present? && !/^rails/.match(controller)
145
+ { :path => path, :verb => verb, :controller => controller, :action => action }
146
+ end
147
+ end.compact
148
+
149
+ puts "\n" << "## Searching for missing route specs...".colorize(:light_blue)
150
+ missing = []
151
+
152
+ routes.group_by { |route| route[:controller] }.each do |route_group|
153
+ unless File.exists?(Rails.root.join("spec/routing/#{route_group[0]}_routing_spec.rb"))
154
+ missing << "spec/routing/#{route_group[0]}_routing_spec.rb"
98
155
  end
99
156
  end
100
157
 
101
- nil
158
+ missing.each do |file|
159
+ puts "Missing route spec file for: #{file}".colorize(:red)
160
+ end
161
+
162
+ if missing.size == 0
163
+ puts "No missing route spec files found!".colorize(:green)
164
+ end
102
165
  end
103
- end
166
+ end
@@ -0,0 +1,77 @@
1
+ require 'colorize'
2
+
3
+ module SpecProducer::MissingGemsModule
4
+ def self.set_up_necessities
5
+ # TODO: Update spec_helper or rails helper with proper configurations
6
+ gemfiles = Dir.glob(Rails.root.join('Gemfile'))
7
+
8
+ if gemfiles.size > 0
9
+ contents = File.read(gemfiles.first)
10
+ gems = contents.scan(/gem \'(?<gem>\S*)\'/).flatten.uniq
11
+ missing_gems = []
12
+
13
+ missing_gems << 'rspec-rails' unless (gems.include? 'rspec-rails')
14
+ missing_gems << 'factory_girl_rails' unless (gems.include? 'factory_girl_rails')
15
+ missing_gems << 'shoulda-matchers' unless (gems.include? 'shoulda-matchers')
16
+ missing_gems << 'webmock' unless (gems.include? 'webmock')
17
+ missing_gems << 'rubocop' unless (gems.include? 'rubocop')
18
+
19
+ # No need for capybara if there are no views to parse
20
+ missing_gems << 'capybara' unless ((gems.include? 'capybara') && Dir["app/views/**/*.erb"] != [])
21
+
22
+ if missing_gems.size > 0
23
+ actions_needed = ""
24
+ useful_info = "\nGems installed:\n"
25
+ contents << "\n\ngroup :test do\n"
26
+
27
+ missing_gems.each do |gem|
28
+ contents << " gem '#{gem}'\n"
29
+
30
+ if gem == 'rspec-rails'
31
+ useful_info << "# Rspec: https://github.com/rspec/rspec-rails\n"
32
+ elsif gem == 'factory_girl_rails'
33
+ useful_info << "# FactoryGirl: https://github.com/thoughtbot/factory_girl_rails\n"
34
+ elsif gem == 'shoulda-matchers'
35
+ useful_info << "# Shoulda Matchers: https://github.com/thoughtbot/shoulda-matchers\n"
36
+ elsif gem == 'webmock'
37
+ actions_needed << "# Add 'require \'webmock/rspec\'' in your spec_helper or rails_helper\n"
38
+ useful_info << "# Webmock: https://github.com/bblimke/webmock\n"
39
+ elsif gem == 'rubocop'
40
+ useful_info << "# RuboCop: https://github.com/bbatsov/rubocop\n"
41
+ end
42
+ end
43
+
44
+ contents << "end"
45
+
46
+ f = File.open(gemfiles.first, 'wb+')
47
+ f.write(contents)
48
+ f.close
49
+
50
+ if defined?(Bundler)
51
+ Bundler.with_clean_env do
52
+ puts "\n\nRunning bundle after setting list of gems in you Gemfile.".colorize(:yellow)
53
+
54
+ system 'bundle install'
55
+ end
56
+ end
57
+
58
+ if 'rspec-rails'.in? missing_gems
59
+ puts "\n\nInitializing Rspec files and helpers.".colorize(:yellow)
60
+
61
+ system 'rails generate rspec:install'
62
+ end
63
+
64
+ if actions_needed != ''
65
+ puts "\n\nYou will additionally need to:\n".colorize(:green)
66
+ puts actions_needed.colorize(:green)
67
+ end
68
+
69
+ puts useful_info.colorize(:light_blue)
70
+ else
71
+ puts 'Could not find anything missing!'.colorize(:light_blue)
72
+ end
73
+ else
74
+ puts "We couldn't find a Gemfile and setting up halted!".colorize(:red)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_producer/producers/base'
2
+ require 'spec_producer/producers/models_producer'
3
+ require 'spec_producer/producers/views_producer'
4
+ require 'spec_producer/producers/helpers_producer'
5
+ require 'spec_producer/producers/controllers_producer'
6
+ require 'spec_producer/producers/mailers_producer'
7
+ require 'spec_producer/producers/jobs_producer'
8
+ require 'spec_producer/producers/routes_producer'
9
+ require 'spec_producer/producers/serializers_producer'
10
+ require 'spec_producer/producers/registry'
@@ -0,0 +1,129 @@
1
+ require 'spec_producer/utils'
2
+ # Base module that needs to be included and implemented by
3
+ # corresponding producers. Note: This module should be prepended and
4
+ # not included / extended.
5
+ #
6
+ # Concrete subclasses need to implement 2 methods: call and resources
7
+ # We use the prepend and prepended callback in order to call Base#call
8
+ # method first and the invoke super which will call the corresponding
9
+ # concrete implementation to be executed. The #call method on every concrete
10
+ # implementation will receive the resource that we currently iterate over.
11
+ #
12
+ # Conrete implementation need to prepend this module and to provide
13
+ # #resources method which returns a collection of resources that
14
+ # will be iterated in order to generate the specs for. the resource
15
+ # object should respond to #name (ex. Some::User) and #type ('models', 'controllers' etc)
16
+ # We need these values in order to generate the Describe 'Some::Person' in rspec files.
17
+ #
18
+ # A minimal implementation could be
19
+ #
20
+ # class SomeSpecProducer
21
+ # prepend Base
22
+ #
23
+ # def resources
24
+ # [Clas.new(Object), Class.new(Object)].map { |obj|
25
+ # Resource.new(obj, obj.name, 'models')
26
+ # }
27
+ # end
28
+ #
29
+ #
30
+ # def call(resource)
31
+ # builder.context 'Some context' do |b|
32
+ # resource.attributes.each do |attr|
33
+ # b.responds_to(attr)
34
+ # end
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # The base class is also responsible to write for each spec file that we generate
40
+ # the top static lines requiring 'spec_helper' and the beginning of spec file:
41
+ #
42
+ # require 'spec_helper'
43
+ # describe User, type: :model do
44
+ # # Other code omitted
45
+ # end
46
+ #
47
+ # Each concrete instance is responsible to create the `body` of the spec file which includes
48
+ # its basic validations if any or any other info that we might get from ActiveRecord, ActionController
49
+ # etc.
50
+ #
51
+ # Finally this class is responsible to write the generated spec to the corresponsing
52
+ # path (ex spec/models/user_spec.rb)
53
+ #
54
+ module SpecProducer
55
+ module Producers
56
+ module Base
57
+ class Resource < Struct.new(:obj, :name, :type)
58
+ end
59
+
60
+ module ClassMethods
61
+ def call(args)
62
+ new(args).call
63
+ end
64
+ end
65
+
66
+ def self.prepended(base)
67
+ base.extend(ClassMethods)
68
+ end
69
+
70
+ attr_reader :type
71
+ attr_reader :builder
72
+
73
+ def initialize(type)
74
+ @type = type
75
+ @builder = RspecBuilders::Base.new
76
+ end
77
+
78
+ def call
79
+ raise(NotImplementedError.new('Abstract method.')) unless defined?(super)
80
+ warn "No resources available" if resources.empty?
81
+
82
+ resources.each do |resource|
83
+ builder.build do |b|
84
+ b.write(helper_spec_file)
85
+ b.spec resource.name, resource.type do |b|
86
+ super(resource)
87
+ end
88
+ end
89
+
90
+ # Footer / Fils Close etc
91
+ if resource.type == 'routing'
92
+ Utils::FileUtils.try_to_create_spec_file(resource.type, "#{resource.name}_routing", builder)
93
+ else
94
+ Utils::FileUtils.try_to_create_spec_file(resource.type.pluralize, resource.name.underscore, builder)
95
+ end
96
+
97
+ builder.flush!
98
+ end
99
+
100
+ self
101
+ rescue StandardError => e
102
+ handle_exception(e)
103
+ end
104
+
105
+ # @Abstract method
106
+ def resources
107
+ raise(NotImplementedError.new('Abstract method.')) unless defined?(super)
108
+ super
109
+ end
110
+
111
+ #######
112
+ private
113
+ #######
114
+
115
+ def handle_exception(e)
116
+ raise e if SpecProducer.configuration.raise_errors
117
+ puts "Exception '#{e}' was raised. Skipping model specs production.".colorize(:red)
118
+ end
119
+
120
+ def helper_spec_file
121
+ "require \'#{require_helper_string}\'\n"
122
+ end
123
+
124
+ def rspec_describe(klass, type)
125
+ "describe #{klass}, :type => :#{type} do"
126
+ end
127
+ end
128
+ end
129
+ end