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
@@ -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