spec_producer 0.11.0 → 0.13.0

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.
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
@@ -0,0 +1,220 @@
1
+ module SpecProducer
2
+ module RspecBuilders
3
+ module Builder
4
+ # == RspecBuilder \Builder
5
+ #
6
+ # Provides the building blocks to create an rspec text using
7
+ # <tt>RspecProducer::RspecBuilders::Base</tt> class. Currently
8
+ # it supports the following building spec blocks:
9
+ #
10
+
11
+
12
+ # Call build on an <tt>RspecBuilder::Base instance</tt>
13
+ #
14
+ # Example
15
+ #
16
+ # builder = RspecBuilders::Base.new('Init Text')
17
+ # builder.build do |b|
18
+ # # Other method calls here
19
+ # end
20
+ def build(&block)
21
+ block.call(self)
22
+ self
23
+ end
24
+
25
+ # Call build on a <tt>RspecBuilders::Base</tt> class
26
+ #
27
+ # Example
28
+ #
29
+ # builder = RspecBuilders::Base.build do |b|
30
+ # # ...
31
+ # b.context('my awsome spec') do
32
+ # be.it { should_be(:valid) }
33
+ # end
34
+ # end
35
+ #
36
+ def self.build(&block)
37
+ instance = new
38
+ block.call(instance)
39
+ instance
40
+ end
41
+
42
+ # Creates a new spec. This is the header block required by all spec files
43
+ #
44
+ # Example
45
+ #
46
+ # builder = RspecBuilders::Base.build do |b|
47
+ # b.spec 'User', 'models'
48
+ # end
49
+ #
50
+ # Calling
51
+ # puts builder
52
+ #
53
+ # Returns the following string:
54
+ #
55
+ # describe User, type: :model do
56
+ # end
57
+ #
58
+ def spec(klass, type, &block)
59
+ new_line
60
+
61
+ if type == 'routing'
62
+ add "describe '#{klass} routes', type: :#{type} do"
63
+ elsif type == 'view'
64
+ add "describe '#{klass}', type: :#{type} do"
65
+ else
66
+ add "describe #{klass}, type: :#{type} do"
67
+ end
68
+
69
+ increase_intent
70
+ new_line
71
+
72
+ block.call(self)
73
+
74
+ decrease_intent
75
+ add 'end'
76
+ end
77
+
78
+ # Adds a subject { 'some' } block to buffer
79
+ # it also adds a new line for the next spec to be added to the new line
80
+ #
81
+ # Example
82
+ #
83
+ # RspecBuilders::Based.build do |b|
84
+ # b.subject { 'User.new' }
85
+ # end
86
+ #
87
+ # Produces:
88
+ # subject { User.new }
89
+ #
90
+ def subject(s)
91
+ add "subject { #{s} }"
92
+ new_line
93
+ end
94
+
95
+ # Adds a pending 'some' block to buffer
96
+ # it also adds a new line for the next spec to be added to the new line
97
+ #
98
+ # Example
99
+ #
100
+ # RspecBuilders::Based.build do |b|
101
+ # b.pending { 'some missing spec' }
102
+ # end
103
+ #
104
+ # Produces:
105
+ # pending 'some missing spec'
106
+ #
107
+ def pending(s)
108
+ add "pending '#{s}'"
109
+ new_line
110
+ end
111
+
112
+ # Adds a before { render } block to buffer
113
+ #
114
+ # Example
115
+ #
116
+ # RspecBuilders::Based.build do |b|
117
+ # b.before_render
118
+ # end
119
+ #
120
+ # Produces:
121
+ # before do
122
+ # render
123
+ # end
124
+ #
125
+ def before_render
126
+ add "before do"
127
+ increase_intent
128
+ new_line
129
+
130
+ add "render"
131
+ new_line
132
+
133
+ decrease_intent
134
+ add 'end'
135
+ new_line
136
+ new_line
137
+ end
138
+
139
+ # Adds a context or describe (alias) block for the spec.
140
+ # the method is responsible to handle the intentation management
141
+ # (increase / decrease).
142
+ #
143
+ # Usage
144
+ #
145
+ # RspecBuilders::Base.build do |b|
146
+ # b.context 'first context' do
147
+ # b.context 'first nested' do
148
+ # end
149
+ # end
150
+ #
151
+ # b.context 'second context' do
152
+ # b.context 'second nested' {}
153
+ # end
154
+ # end
155
+ #
156
+ # Which produces the following spec:
157
+ #
158
+ # context 'first context' do
159
+ # context 'first nested' do
160
+ # end
161
+ # end
162
+ #
163
+ # context 'second context' do
164
+ # context 'second nested do
165
+ #
166
+ # end
167
+ # end
168
+ #
169
+ def context(name, &block)
170
+ new_line
171
+ add "context \"#{name}\" do"
172
+ increase_intent
173
+ new_line
174
+
175
+ block.call
176
+
177
+ decrease_intent
178
+ add 'end'
179
+ new_line
180
+ end
181
+ alias_method :describe, :context
182
+
183
+ # Provides an it expectation.
184
+ #
185
+ # Usage example
186
+ #
187
+ # user = User.new(name: 'alex', email: 'some@some.com')
188
+ #
189
+ # RspecBuilders::Base.build do |b|
190
+ # b.subject { "build(:user, name: 'Alex', email: 'some@some.com')" }
191
+ #
192
+ # # Matchers provided from RspecBuilders::Matchers
193
+ # b.it { validates_presence_of(:name) }
194
+ # b.it { expect('subject.name').to eq user.name }
195
+ # b.it { expect('subject.email').to eq 'name' }
196
+ #
197
+ # # Or provide the expecation as a string
198
+ # b.it('expect(subject).to be_a User')
199
+ # end
200
+ #
201
+ # produces the following spec
202
+ #
203
+ # subject { 'builder(:user, anme: 'Alex', email: 'some@some.com')' }
204
+ # it { is_expected.to validate_presence_of(:name) }
205
+ # it { expect(subject.name).to eq 'Alex' }
206
+ # it { expect(subject.email).to eq 'some@some.com' }
207
+ # it { expect(subject).to be_a User }
208
+ #
209
+ def it(*args, &block)
210
+ expectation = args.shift
211
+ if expectation
212
+ add "it { #{expectation} }"
213
+ new_line
214
+ else
215
+ instance_eval(&block)
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,256 @@
1
+ module SpecProducer
2
+ module RspecBuilders
3
+ module Matchers
4
+ # \RspecBuilders \Matchers
5
+ # Provides some helper methods to build Rspec matchers.
6
+
7
+ # AR reflections to RSpec mathcers
8
+ REFLECTION_MACRO_MAPPINGS = {
9
+ belongs_to: 'belong_to',
10
+ has_one: 'have_one',
11
+ has_many: 'have_many',
12
+ has_and_belongs_to_many: 'have_and_belongs_to_many',
13
+ inverse_of: 'inverse_of',
14
+ autosave: 'autosave',
15
+ through: 'through',
16
+ class_name: 'class_name',
17
+ foreign_key: 'with_foreign_key',
18
+ primary_key: 'with_primary_key',
19
+ source: 'source',
20
+ dependent: 'dependent'
21
+ }
22
+
23
+ MATCHERS = {
24
+ presence: -> (name) {"is_expected.to validate_presence_of(:#{name})" },
25
+ belong_to: ->(name) { "is_expected.to belong_to(:#{name})" },
26
+ have_one: ->(name) { "is_expected.to have_one(:#{name})" },
27
+ has_many: ->(name) { "is_expected.to have_many(:#{name})" },
28
+ db_column: ->(name) { "is_expected.to have_db_column(:#{name})" },
29
+ }
30
+
31
+ def M(type)
32
+ MATCHERS[type]
33
+ end
34
+
35
+ # Writes an expectation for an AR association given an AR reflection.
36
+ # Example
37
+ #
38
+ # class User < ActiveRecord::Base
39
+ # has_many :posts, class_name: 'Post', dependent: :destroy
40
+ # end
41
+ #
42
+ # user_reflection = User.reflections.first
43
+ # puts has_assosication(user_reflection)
44
+ # => it { is_expected.to have_many(:posts).class_name(:Post).dependent(:destroy) }
45
+ #
46
+ # RspecBuilders::Base.build do |b|
47
+ # b.it { has_associations(user_reflection) }
48
+ # end
49
+ def has_association(reflection)
50
+ matcher = "#{REFLECTION_MACRO_MAPPINGS[reflection.macro.to_sym]}(:#{reflection.name})"
51
+ options = association_options_for(reflection)
52
+ matcher += options if options.present?
53
+ expectation = "it { is_expected.to #{matcher} }"
54
+ write expectation
55
+ new_line
56
+ end
57
+
58
+ def json_parse_for_serialized_object(object)
59
+ "JSON.parse(#{object.name}.new(#{factory_build_for_object(object)}).to_json)"
60
+ end
61
+
62
+ def initialize_serializer_for_object(object)
63
+ "#{object.name}.new(#{factory_build_for_object(object)})"
64
+ end
65
+
66
+ def factory_build_for_object(object)
67
+ "FactoryGirl.build(:#{object.name.underscore.gsub('_serializer', '')})"
68
+ end
69
+
70
+ def association_options_for(reflection)
71
+ return if reflection.options.empty?
72
+ options = []
73
+
74
+ reflection.options.each_pair do |key, value|
75
+ options << (SpecProducer::RspecBuilders::Matchers::REFLECTION_MACRO_MAPPINGS[key] || key.to_s) + "(:#{value})"
76
+ end
77
+ options.reject(&:nil?).join('.').prepend('.')
78
+ end
79
+ private :association_options_for
80
+
81
+ # Writes all responds_to expectations for the fiven arguments.
82
+ #
83
+ # Example:
84
+ #
85
+ # RspecBuilders::Base.build do |b|
86
+ # b.it { responds_to(:name, :age) }
87
+ # # or
88
+ # b.responds_to(:name, :age)
89
+ # end
90
+ #
91
+ # will produce
92
+ # it { is_expected.to respond_to(:name, :age) }
93
+ #
94
+ def responds_to(*args)
95
+ args = args.map!{ |arg| ":#{arg.gsub(":", '')}" }.split.join(',')
96
+ it "is_expected.to respond_to(#{args})"
97
+ new_line
98
+ end
99
+
100
+ # provides a should_be_* matchers
101
+ #
102
+ # Example
103
+ # RspecBuilders::Base.build do |b|
104
+ # b.should_be(:present)
105
+ # b.it { should_be(:valid) }
106
+ # end
107
+ #
108
+ # will produce:
109
+ # it { is_expected.to be_present }
110
+ # it { is_expected.to be_valid }
111
+ #
112
+ def should_be(type)
113
+ it "is_expected.to be_#{type}"
114
+ new_line
115
+ end
116
+
117
+ # Given a validator naeme (ex. present) and the attribute
118
+ # validate it will produce the corresponding shoulda matcher
119
+ #
120
+ # Example
121
+ #
122
+ # RspecBuilders::Base.build do |b|
123
+ # b.validates_with(:presence, :name)
124
+ # b.it { vlaidates_with(:presence, :age) }
125
+ # end
126
+ #
127
+ # Will produce
128
+ # it { is_expected.to validate_presence_of(:name) }
129
+ # it { is_expected.to validate_presence_of(:age) }
130
+ #
131
+ # Currently no validation options are supported.
132
+ # We should probably change the method to accept an
133
+ # <tt>ActiveModel</tt> validator instead like we do
134
+ # with association expectation where we accept an
135
+ # <tt>ActiveRecord::Reflection</tt>
136
+ #
137
+ def validates_with(kind, attr)
138
+ it "is_expected.to validate_#{kind}_of(:#{attr})"
139
+ new_line
140
+ end
141
+
142
+ # Combine the methods below to add an Rspec like
143
+ # expectation.
144
+ #
145
+ # Example
146
+ #
147
+ # var = 10
148
+ # RspecBuidlers::Base.build do
149
+ # it { expect('subject.age').to eq var }
150
+ # it { expect('subject.name').to eq 'me' }
151
+ # end
152
+ #
153
+ #############################################
154
+ # it { expect(some).to eq instance.age }
155
+ def expect(attr)
156
+ @_expected = attr
157
+ self
158
+ end
159
+ def to actual
160
+ it "expect(#{@_expected}).to eq(#{actual})"
161
+ new_line
162
+ end
163
+ def eq(value)
164
+ value
165
+ end
166
+ #############################################
167
+
168
+ # Given an name atrribute it writes the:
169
+ # Example
170
+ # builder.should_validate_presence_of('age')
171
+ #
172
+ # Produes
173
+ # it { is_expeted.to validate_presnce_of(:age) }
174
+ #
175
+ def should_validate_presence_of(name)
176
+ expectation = M(:presence)[name]
177
+ it(expectation)
178
+ new_line
179
+ end
180
+ alias_method :validates_presence_of, :should_validate_presence_of
181
+
182
+ # Given a :user attribute it writes the belongs to expectation
183
+ #
184
+ # Example
185
+ # builder.should_belong_to(:user)
186
+ #
187
+ # Produces
188
+ # it { is_expected.to belong_to(:user) }
189
+ #
190
+ def should_belong_to(name)
191
+ expectation = M(:belong_to)[name]
192
+ it(expectation)
193
+ new_line
194
+ end
195
+
196
+ # Should have one shoulda matcher producer
197
+ #
198
+ # Example
199
+ # builder.should_have_one(:profile)
200
+ #
201
+ # Produces
202
+ # it { is_expected.to have_one(:profile) }
203
+ #
204
+ def should_have_one(name)
205
+ expectation = M(:have_one)[name]
206
+ it(expectation)
207
+ new_line
208
+ end
209
+
210
+ # have_many shoulda matcher expectation
211
+ #
212
+ # Example
213
+ # builder.should_have_many(:posts)
214
+ #
215
+ # Produces
216
+ # it { is_expected.to have_many(:posts) }
217
+ #
218
+ def should_have_many(name)
219
+ expectation = M(:has_many)[name]
220
+ it(expectation)
221
+ new_line
222
+ end
223
+ alias_method :has_many, :should_have_many
224
+
225
+ # have_db_column spec producer
226
+ #
227
+ # example
228
+ # builder.has_db_column(:user_id)
229
+ #
230
+ # produces
231
+ # it { is_expected.to have_db_column(:user_id) }
232
+ #
233
+ def has_db_column(name)
234
+ expectation = M(:db_column)[name]
235
+ it(expectation)
236
+ new_line
237
+ end
238
+
239
+ # Factories spec producer
240
+ # Given the resource name of the factory to build
241
+ # it checks to see if a valid factory exists for the resource name
242
+ #
243
+ # example
244
+ #
245
+ # build.has_valid_factory(:user)
246
+ #
247
+ # produces
248
+ # it { expect(FactoryGirl.build(:user)).to be_valid }
249
+ #
250
+ def has_valid_factory(name)
251
+ it "expect(FactoryGirl.build(:#{name})).to be_valid"
252
+ new_line
253
+ end
254
+ end
255
+ end
256
+ end