katapult 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +34 -12
  4. data/README.md +154 -114
  5. data/Rakefile +3 -3
  6. data/bin/katapult +34 -8
  7. data/features/application_model.feature +8 -44
  8. data/features/authenticate.feature +21 -4
  9. data/features/basics.feature +16 -89
  10. data/features/binary.feature +37 -4
  11. data/features/model.feature +40 -24
  12. data/features/navigation.feature +2 -0
  13. data/features/step_definitions/katapult_steps.rb +48 -16
  14. data/features/step_definitions/test_steps.rb +2 -0
  15. data/features/templates.feature +74 -0
  16. data/lib/generators/katapult/app_model/templates/lib/katapult/application_model.rb +2 -2
  17. data/lib/generators/katapult/basics/basics_generator.rb +12 -1
  18. data/lib/generators/katapult/basics/templates/Capfile +5 -0
  19. data/lib/generators/katapult/basics/templates/Gemfile +3 -1
  20. data/lib/generators/katapult/basics/templates/Gemfile.lock +376 -0
  21. data/lib/generators/katapult/basics/templates/app/models/application_record.rb +28 -0
  22. data/lib/generators/katapult/basics/templates/app/webpack/assets/javascripts/bootstrap.js +2 -2
  23. data/lib/generators/katapult/basics/templates/config/deploy.rb +2 -3
  24. data/lib/generators/katapult/basics/templates/config/deploy/production.rb +2 -2
  25. data/lib/generators/katapult/basics/templates/config/deploy/staging.rb +1 -1
  26. data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/deploy.rake +1 -4
  27. data/lib/generators/katapult/clearance/clearance_generator.rb +13 -4
  28. data/lib/generators/katapult/clearance/templates/features/authentication.feature +3 -3
  29. data/lib/generators/katapult/model/model_generator.rb +14 -4
  30. data/lib/generators/katapult/templates/templates_generator.rb +35 -0
  31. data/lib/generators/katapult/transform/transform_generator.rb +3 -3
  32. data/lib/generators/katapult/views/views_generator.rb +1 -1
  33. data/lib/generators/katapult/web_ui/web_ui_generator.rb +1 -1
  34. data/lib/katapult/application_model.rb +7 -7
  35. data/lib/katapult/elements/attribute.rb +16 -0
  36. data/lib/katapult/elements/authentication.rb +9 -6
  37. data/lib/katapult/elements/model.rb +8 -4
  38. data/lib/katapult/elements/navigation.rb +2 -2
  39. data/lib/katapult/elements/web_ui.rb +2 -2
  40. data/lib/katapult/generator.rb +12 -2
  41. data/lib/katapult/support/generator_goodies.rb +14 -0
  42. data/lib/katapult/version.rb +1 -1
  43. data/script/update +5 -1
  44. metadata +8 -8
  45. data/features/configuration.feature +0 -24
  46. data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/passenger.rake +0 -8
  47. data/lib/generators/katapult/basics/templates/lib/ext/active_record/find_by_anything.rb +0 -20
  48. data/lib/generators/katapult/basics/templates/lib/ext/active_record/these.rb +0 -7
data/Rakefile CHANGED
@@ -16,11 +16,11 @@ RSpec::Core::RakeTask.new(:spec)
16
16
  task :update_readme do
17
17
  require_relative 'lib/katapult/version'
18
18
  readme_path = 'README.md'
19
+
19
20
  readme = File.read readme_path
21
+ readme.sub! /\A# Katapult.*\nGenerating.*\n/, '# ' + `bin/katapult version`
22
+ readme.sub! /(required .Ruby. version is )[\d\.]+\d/, "\\1#{Katapult::RUBY_VERSION}"
20
23
 
21
- # Using \s+ to support line breaks
22
- readme.sub! /(currently\s+it's\s+Rails\s+)[\d\.]+/, "\\1#{Katapult::RAILS_VERSION}"
23
- readme.sub! /(and\s+Ruby\s+)[\d\.]+\d/, "\\1#{Katapult::RUBY_VERSION}"
24
24
  File.open readme_path, 'w' do |f|
25
25
  f.write readme
26
26
  end
data/bin/katapult CHANGED
@@ -3,8 +3,6 @@
3
3
  # This script simplifies the usage of `katapult` by grouping relevant actions
4
4
  # that the user else had to perform manually.
5
5
 
6
- usage = 'Usage: katapult new APP_NAME | fire [path/to/model] | version'
7
-
8
6
  require_relative '../lib/katapult/version'
9
7
  require_relative '../lib/katapult/support/binary_util'
10
8
  util = Katapult::BinaryUtil
@@ -35,6 +33,25 @@ is not supported by this version of katapult.
35
33
  Please switch to Ruby #{supported_ruby} and run again.
36
34
  MSG
37
35
 
36
+ # Prepare output ###############################################################
37
+
38
+ # This is also used as README heading
39
+ version = <<-VERSION
40
+ Katapult #{Katapult::VERSION}
41
+ Generating a Rails #{Katapult::RAILS_VERSION} app on Ruby #{Katapult::RUBY_VERSION}.
42
+ VERSION
43
+
44
+ usage = <<-USAGE
45
+ Usage: katapult <command>
46
+
47
+ Commands:
48
+ new APP_NAME Generate a configured Rails application
49
+ fire [PATH] Transform application model into code
50
+ Default path: lib/katapult/application_model.rb
51
+ templates Copy templates to lib/templates/katapult
52
+ version Print version
53
+ USAGE
54
+
38
55
 
39
56
  case ARGV.shift
40
57
  when 'new'
@@ -78,13 +95,16 @@ when 'new'
78
95
  Application initialization done.
79
96
 
80
97
  Next steps:
81
-
98
+ - \`cd #{app_name}\`
99
+ - Customize Katapult's template files in lib/templates/katapult
82
100
  - Model your application in lib/katapult/application_model.rb and transform it
83
- into code by running `katapult fire`
101
+ into code by running \`katapult fire\`
102
+ - Run \`bundle update\` if you want to use the latest versions of all gems
84
103
  - Configure public/robots.txt
85
104
  - Write a README
86
105
  INSTRUCTIONS
87
106
 
107
+
88
108
  when 'fire'
89
109
  app_model_path = ARGV.shift || 'lib/katapult/application_model.rb'
90
110
  transform_command = 'bin/rails generate katapult:transform ' + app_model_path
@@ -102,12 +122,18 @@ Now boot up your development server (e.g. with `rails server`) and try your
102
122
  kickstarted application in the browser!
103
123
  INSTRUCTIONS
104
124
 
125
+
126
+ when 'templates'
127
+ util.run 'bundle exec rails generate katapult:templates'
128
+ util.pink 'Templates copied to lib/templates/katapult.'
129
+
130
+
105
131
  when 'version'
106
- puts <<-VERSION
107
- Katapult #{Katapult::VERSION}
108
- Generating a Rails #{Katapult::RAILS_VERSION} app on Ruby #{Katapult::RUBY_VERSION}.
109
- VERSION
132
+ puts version
133
+
110
134
 
111
135
  else
136
+ puts version
137
+ puts
112
138
  puts usage
113
139
  end
@@ -5,50 +5,14 @@ Feature: The default application model prepared by Katapult
5
5
  Given a new Rails application with Katapult basics installed
6
6
 
7
7
  When I generate the application model
8
- Then the file "lib/katapult/application_model.rb" should contain:
9
- """
10
- # Define a model + generate views with CRUD actions
11
- # Since this generates the first web UI with an index action, the products list
12
- # will become the home page of the generated app
13
- crud 'product' do |product|
14
- # The first attribute of each model is taken as a human identifier/label
15
- product.attr :title # Default type "string"
16
-
17
- # The order of attributes is respected when generating the form for that model
18
- product.attr :price, type: :money
19
- product.attr :mode, assignable_values: %w[public private]
20
- product.attr :provider, type: :url
21
- product.attr :import_data, type: :json
22
-
23
- # Reference other models just like you called them
24
- product.belongs_to 'user'
25
- end
26
-
27
- # Define a model
28
- model 'user' do |user|
29
- user.attr :email # Type "email" derived from attribute name
30
-
31
- user.attr :name
32
- user.attr :last_visit, type: :datetime
33
- user.attr :locked, type: :flag, default: false
34
- end
35
-
36
- # Add a web user interface for the 'user' model
37
- web_ui 'user' do |web_ui|
38
- # All CRUD actions: index, show, new, create, edit, update, destroy
39
- web_ui.crud
40
-
41
- # Custom action
42
- web_ui.action :lock, scope: :member, method: :post
43
- end
44
-
45
- # Have a main menu
46
- navigation
47
-
48
- # Add authentication
49
- authenticate 'user', system_email: 'system@example.com'
50
-
51
- """
8
+ Then the file "lib/katapult/application_model.rb" should contain "crud 'product'"
9
+ And the file "lib/katapult/application_model.rb" should contain "product.attr :price, type: :money"
10
+ And the file "lib/katapult/application_model.rb" should contain "product.belongs_to 'user'"
11
+
12
+ And the file "lib/katapult/application_model.rb" should contain "model 'user'"
13
+ And the file "lib/katapult/application_model.rb" should contain "web_ui 'user'"
14
+ And the file "lib/katapult/application_model.rb" should contain "navigation"
15
+ And the file "lib/katapult/application_model.rb" should contain "authenticate 'user'"
52
16
 
53
17
  When I successfully transform the application model including migrations
54
18
  And I run cucumber
@@ -7,10 +7,12 @@ Feature: Add authentication to an application
7
7
 
8
8
 
9
9
  Scenario: Authenticate with the user model
10
+ # Have a custom "label attr" (:name)
10
11
  When I write to "lib/katapult/application_model.rb" with:
11
12
  """
12
- model 'user'
13
- web_ui('user', &:crud)
13
+ crud 'user' do |user|
14
+ user.attr :name
15
+ end
14
16
  authenticate 'user', system_email: 'system@example.com'
15
17
  """
16
18
  And I successfully transform the application model including migrations
@@ -267,11 +269,11 @@ Feature: Add authentication to an application
267
269
 
268
270
 
269
271
  Scenario: Reset password as a signed-in user
270
- Given there is a user with the email "henry@example.com"
272
+ Given there is a user with the email "henry@example.com" and the name "name-string"
271
273
  And I sign in as the user above
272
274
 
273
275
  When I go to the homepage
274
- And I follow "henry@example.com" within the navbar
276
+ And I follow "name-string" within the navbar
275
277
  Then I should be on the form for the user above
276
278
 
277
279
  When I fill in "Password" with "new-password"
@@ -351,3 +353,18 @@ Feature: Add authentication to an application
351
353
 
352
354
  When I run cucumber
353
355
  Then the features should pass
356
+
357
+
358
+ Scenario: Generate a model with authentication twice
359
+ Given I write to "lib/katapult/application_model.rb" with:
360
+ """
361
+ crud 'user'
362
+ authenticate 'user', system_email: 'system@example.com'
363
+ """
364
+
365
+ When I successfully transform the application model
366
+ And I successfully transform the application model, ignoring conflicts
367
+ Then the file named "spec/factories/factories.rb" should contain "factory :user" exactly once
368
+
369
+ When I run cucumber
370
+ Then the features should pass
@@ -25,6 +25,7 @@ Feature: Preparation of a new Rails app (basics generator)
25
25
 
26
26
  And the configured Rails version should be listed in the Gemfile.lock
27
27
  And the file "Gemfile" should contain "gem 'katapult', path: '../../..'"
28
+ And a file named "Gemfile.lock" should exist
28
29
 
29
30
  And the file "app/controllers/application_controller.rb" should contain:
30
31
  """
@@ -63,6 +64,9 @@ Feature: Preparation of a new Rails app (basics generator)
63
64
  And binstubs should be set up
64
65
 
65
66
 
67
+ And the file "app/models/application_record.rb" should contain "def these"
68
+ And the file "app/models/application_record.rb" should contain "def find_by_anything"
69
+
66
70
  # Config
67
71
  And the file "config/application.rb" should contain "config.time_zone = 'Berlin'"
68
72
  And the file "config/application.rb" should contain "config.system_email = 'system@katapult_test_app.com'"
@@ -132,6 +136,7 @@ Feature: Preparation of a new Rails app (basics generator)
132
136
  require filename
133
137
  end
134
138
  """
139
+ And a file "config/schedule.rb" should exist
135
140
  And the file "config/secrets.yml" should contain:
136
141
  """
137
142
  staging:
@@ -141,95 +146,17 @@ Feature: Preparation of a new Rails app (basics generator)
141
146
 
142
147
 
143
148
  # Lib
144
- And the file "lib/ext/active_record/find_by_anything.rb" should contain:
145
- """
146
- ActiveRecord::Base.class_eval do
147
-
148
- def self.find_by_anything(identifier)
149
- """
150
- And the file "lib/ext/action_view/spec_label.rb" should contain:
151
- """
152
- ActionView::Helpers::FormBuilder.class_eval do
153
-
154
- def spec_label(field, text = nil, options = {})
155
- """
156
- And the file "lib/ext/action_view/form_for_with_development_errors.rb" should contain:
157
- """
158
- if Rails.env.development?
159
-
160
- ActionView::Helpers::FormHelper.module_eval do
161
- def form_for_with_development_errors
162
- """
163
- And the file "lib/ext/active_record/these.rb" should contain:
164
- """
165
- ActiveRecord::Base.class_eval do
166
-
167
- def self.these(arg)
168
- where(id: arg.collect_ids)
169
- end
170
- """
171
- And the file "lib/ext/array/xss_aware_join.rb" should contain:
172
- """
173
- Array.class_eval do
174
- def xss_aware_join(delimiter = '')
175
- ''.html_safe.tap do |str|
176
- each_with_index do |element, i|
177
- str << delimiter if i > 0
178
- str << element
179
- end
180
- end
181
- end
182
- end
183
- """
184
- And the file "lib/ext/enumerable/natural_sort.rb" should contain:
185
- """
186
- module Enumerable
187
-
188
- def natural_sort
189
- """
190
- And the file "lib/ext/hash/infinite.rb" should contain:
191
- """
192
- class Hash
193
-
194
- def self.infinite
195
- new { |h, k| h[k] = new(&h.default_proc) }
196
- end
197
-
198
- end
199
- """
200
- And the file "lib/ext/string/html_entities.rb" should contain:
201
- """
202
- class String
203
-
204
- def self.nbsp
205
- ' '
206
- end
207
-
208
- def self.ndash
209
- '–'
210
- end
211
-
212
- end
213
- """
214
- And the file "lib/ext/string/to_sort_atoms.rb" should contain:
215
- """
216
- String.class_eval do
217
-
218
- def to_sort_atoms
219
- SmartSortAtom.parse(self)
220
- end
221
-
222
- end
223
- """
224
- And the file "lib/tasks/pending_migrations.rake" should contain:
225
- """
226
- pending_migrations = ActiveRecord::Migrator.new(:up, all_migrations).pending_migrations
227
-
228
- if pending_migrations.any?
229
- puts ''
230
- puts '======================================================='
231
- puts "You have #{ pending_migrations.size } pending migrations:"
232
- """
149
+ And Katapult templates should have been copied to the application
150
+
151
+ And a file "lib/ext/action_view/spec_label.rb" should exist
152
+ And a file "lib/ext/action_view/form_for_with_development_errors.rb" should exist
153
+ And a file "lib/ext/array/xss_aware_join.rb" should exist
154
+ And a file "lib/ext/enumerable/natural_sort.rb" should exist
155
+ And a file "lib/ext/hash/infinite.rb" should exist
156
+ And the file "lib/ext/string/html_entities.rb" should contain "def self.nbsp"
157
+ And the file "lib/ext/string/html_entities.rb" should contain "def self.ndash"
158
+ And a file "lib/ext/string/to_sort_atoms.rb" should exist
159
+ And a file "lib/tasks/pending_migrations.rake" should exist
233
160
 
234
161
 
235
162
  # Tests
@@ -4,7 +4,17 @@ Feature: Katapult binary `katapult`
4
4
 
5
5
  Scenario: Run without arguments
6
6
  When I run `katapult`
7
- Then the output should contain "Usage: katapult new APP_NAME | fire [path/to/model] | version"
7
+ Then the output should contain:
8
+ """
9
+ Usage: katapult <command>
10
+
11
+ Commands:
12
+ new APP_NAME Generate a configured Rails application
13
+ fire [PATH] Transform application model into code
14
+ Default path: lib/katapult/application_model.rb
15
+ templates Copy templates to lib/templates/katapult
16
+ version Print version
17
+ """
8
18
 
9
19
 
10
20
  Scenario: Print versions
@@ -13,6 +23,11 @@ Feature: Katapult binary `katapult`
13
23
  And the output should contain "Generating a Rails 5."
14
24
  And the output should contain " app on Ruby 2."
15
25
 
26
+ When I run `katapult -v`
27
+ Then the output should contain "Katapult"
28
+ And the output should contain "Generating a Rails 5."
29
+ And the output should contain " app on Ruby 2."
30
+
16
31
 
17
32
  Scenario: Missing options are asked for
18
33
  When I run `katapult new` interactively
@@ -36,6 +51,10 @@ Feature: Katapult binary `katapult`
36
51
 
37
52
  # @announce-output
38
53
  Scenario: Start new Rails application
54
+
55
+ This scenario is particularly prone to "Spring zombies". If it fails, make
56
+ sure there are no broken Spring instances (see README).
57
+
39
58
  # Rails new including yarn install + installing basics takes quite some time
40
59
  Given the aruba exit timeout is 90 seconds
41
60
 
@@ -45,9 +64,12 @@ Feature: Katapult binary `katapult`
45
64
  And the output should contain "Generating katapult basics"
46
65
 
47
66
  And the output should contain "Application initialization done."
67
+ And the output should contain "cd binary_test"
48
68
  And the output should contain "Model your application in lib/katapult/application_model.rb"
49
69
  And the output should contain "Configure public/robots.txt"
50
70
  And the output should contain "Write a README"
71
+ And the output should contain "Run `bundle update`"
72
+ And the output should contain "Customize Katapult's template files"
51
73
 
52
74
  When I cd to "binary_test"
53
75
  Then the file "Gemfile" should contain "gem 'katapult'"
@@ -97,10 +119,10 @@ Feature: Katapult binary `katapult`
97
119
  """
98
120
  model 'custom'
99
121
  """
100
- And I run `katapult fire lib/katapult/custom_model.rb`
122
+ And I run `katapult fire lib/katapult/custom_model.rb`
101
123
  Then the output should contain "Loading katapult"
102
- And the output should contain "parse lib/katapult/custom_model"
103
- And a file named "app/models/custom.rb" should exist
124
+ And the output should contain "parse lib/katapult/custom_model"
125
+ And a file named "app/models/custom.rb" should exist
104
126
 
105
127
 
106
128
  Scenario: When the transformation fails, an error message is printed
@@ -115,3 +137,14 @@ Feature: Katapult binary `katapult`
115
137
  And I run `katapult fire`
116
138
  Then the output should not contain "Model transformation done"
117
139
  But the output should contain "x Something went wrong"
140
+
141
+
142
+ Scenario: Copying templates to the application
143
+ Given a pristine Rails application
144
+
145
+ When I install katapult
146
+ Then a directory named "lib/templates/katapult" should not exist
147
+
148
+ When I run `katapult templates`
149
+ Then a directory named "lib/templates/katapult" should exist
150
+ And Katapult templates should have been copied to the application
@@ -188,19 +188,21 @@ Feature: Generate Models
188
188
  """
189
189
  model 'Person' do |person|
190
190
  person.attr :age, type: :integer, assignable_values: 9..99
191
+ person.attr :mood, assignable_values: %w[happy pensive]
191
192
  person.attr :hobby, assignable_values: %w[soccer baseball], default: 'soccer', allow_blank: true
192
193
  end
193
194
  """
194
195
  And I successfully transform the application model including migrations
195
- Then the file "app/models/person.rb" should contain exactly:
196
+ Then the file "app/models/person.rb" should contain:
196
197
  """
197
- class Person < ApplicationRecord
198
-
199
-
200
198
  assignable_values_for :age, {} do
201
199
  9..99
202
200
  end
203
201
 
202
+ assignable_values_for :mood, {} do
203
+ ["happy", "pensive"]
204
+ end
205
+
204
206
  assignable_values_for :hobby, {:allow_blank=>true, :default=>"soccer"} do
205
207
  ["soccer", "baseball"]
206
208
  end
@@ -208,26 +210,19 @@ Feature: Generate Models
208
210
  def to_s
209
211
  age.to_s
210
212
  end
211
-
212
- end
213
-
214
213
  """
215
- And the file "spec/models/person_spec.rb" should contain exactly:
214
+ And the file "spec/models/person_spec.rb" should contain:
216
215
  """
217
- describe Person do
218
-
219
- describe '#to_s' do
220
- it 'returns the #age attribute' do
221
- subject.age = 9
222
- expect(subject.to_s).to eq("9")
223
- end
224
- end
225
-
226
216
  describe '#age' do
227
217
  it { is_expected.to allow_value(99).for(:age) }
228
218
  it { is_expected.to_not allow_value(100).for(:age) }
229
219
  end
230
220
 
221
+ describe '#mood' do
222
+ it { is_expected.to allow_value("pensive").for(:mood) }
223
+ it { is_expected.to_not allow_value("pensive-unassignable").for(:mood) }
224
+ end
225
+
231
226
  describe '#hobby' do
232
227
  it { is_expected.to allow_value("baseball").for(:hobby) }
233
228
  it { is_expected.to_not allow_value("baseball-unassignable").for(:hobby) }
@@ -236,23 +231,41 @@ Feature: Generate Models
236
231
  expect(subject.hobby).to eq("soccer")
237
232
  end
238
233
  end
234
+ """
235
+ And the file "spec/factories/factories.rb" should contain:
236
+ """
237
+ factory :person do
238
+ age 9
239
+ mood "happy"
240
+ end
241
+ """
239
242
 
240
- end
241
-
243
+ When I write to "spec/models/factory_spec.rb" with:
242
244
  """
245
+ describe Person do
243
246
 
244
- When I run rspec
247
+ describe 'person factory' do
248
+ subject { create :person }
249
+
250
+ it 'generates a valid record' do
251
+ expect(subject).to be_valid
252
+ end
253
+ end
254
+
255
+ end
256
+ """
257
+ And I run rspec
245
258
  Then the specs should pass
246
259
 
247
260
 
248
261
  Scenario: Transform the application model multiple times
249
262
 
250
- Do not add routes twice.
263
+ Some generators inject code snippets into files. They should not do this
264
+ if their snippet is already present.
251
265
 
252
266
  When I write to "lib/katapult/application_model.rb" with:
253
267
  """
254
- model('Car') { |c| c.attr :model }
255
- web_ui 'Car', &:crud
268
+ crud('Car') { |c| c.attr :model }
256
269
  """
257
270
  And I successfully transform the application model
258
271
  Then the file named "config/routes.rb" should contain:
@@ -261,7 +274,8 @@ Feature: Generate Models
261
274
  root 'cars#index'
262
275
  resources :cars
263
276
  """
264
- And I successfully transform the application model
277
+
278
+ When I successfully transform the application model
265
279
  Then the file named "config/routes.rb" should contain:
266
280
  """
267
281
  Rails.application.routes.draw do
@@ -271,3 +285,5 @@ Feature: Generate Models
271
285
  And the file named "config/routes.rb" should contain "root 'cars#index'" exactly once
272
286
  And the file named "config/routes.rb" should contain "resources :cars" exactly once
273
287
  And the output should contain "Routes for :cars already exist! Not updated."
288
+
289
+ And the file named "spec/factories/factories.rb" should contain "factory :car" exactly once