katapult 0.1.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 (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +122 -0
  9. data/Rakefile +14 -0
  10. data/bin/katapult +44 -0
  11. data/features/binary.feature +48 -0
  12. data/features/configuration.feature +24 -0
  13. data/features/katapult.feature +201 -0
  14. data/features/model.feature +203 -0
  15. data/features/navigation.feature +80 -0
  16. data/features/step_definitions/db_steps.rb +8 -0
  17. data/features/step_definitions/file_steps.rb +14 -0
  18. data/features/step_definitions/katapult_steps.rb +14 -0
  19. data/features/step_definitions/rails_steps.rb +44 -0
  20. data/features/step_definitions/test_steps.rb +7 -0
  21. data/features/support/env.rb +16 -0
  22. data/features/wui.feature +319 -0
  23. data/katapult.gemspec +35 -0
  24. data/katapult.png +0 -0
  25. data/lib/generators/katapult/basics/basics_generator.rb +95 -0
  26. data/lib/generators/katapult/basics/templates/Gemfile +76 -0
  27. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application.css.sass +6 -0
  28. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_all.css.sass +4 -0
  29. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_items.css.sass +11 -0
  30. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_layout.css.sass +26 -0
  31. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_navigation.css.sass +11 -0
  32. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_tools.css.sass +12 -0
  33. data/lib/generators/katapult/basics/templates/config/database.sample.yml +16 -0
  34. data/lib/generators/katapult/basics/templates/config/database.yml +13 -0
  35. data/lib/generators/katapult/basics/templates/config/spring.rb +3 -0
  36. data/lib/generators/katapult/basics/templates/features/support/env-custom.rb +3 -0
  37. data/lib/generators/katapult/basics/templates/features/support/paths.rb +47 -0
  38. data/lib/generators/katapult/cucumber_features/cucumber_features_generator.rb +23 -0
  39. data/lib/generators/katapult/cucumber_features/templates/feature.feature +59 -0
  40. data/lib/generators/katapult/haml/haml_generator.rb +90 -0
  41. data/lib/generators/katapult/haml/templates/_form.html.haml +38 -0
  42. data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +25 -0
  43. data/lib/generators/katapult/haml/templates/custom_action.html.haml +5 -0
  44. data/lib/generators/katapult/haml/templates/edit.html.haml +4 -0
  45. data/lib/generators/katapult/haml/templates/index.html.haml +29 -0
  46. data/lib/generators/katapult/haml/templates/new.html.haml +4 -0
  47. data/lib/generators/katapult/haml/templates/show.html.haml +41 -0
  48. data/lib/generators/katapult/install/install_generator.rb +14 -0
  49. data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +18 -0
  50. data/lib/generators/katapult/model/model_generator.rb +59 -0
  51. data/lib/generators/katapult/model/templates/app/models/shared/does_flag.rb +32 -0
  52. data/lib/generators/katapult/model/templates/model.rb +21 -0
  53. data/lib/generators/katapult/model_specs/model_specs_generator.rb +51 -0
  54. data/lib/generators/katapult/model_specs/templates/model_spec.rb +34 -0
  55. data/lib/generators/katapult/navigation/navigation_generator.rb +25 -0
  56. data/lib/generators/katapult/navigation/templates/app/models/navigation.rb +12 -0
  57. data/lib/generators/katapult/transform/transform_generator.rb +47 -0
  58. data/lib/generators/katapult/w_u_i/templates/_route.rb +13 -0
  59. data/lib/generators/katapult/w_u_i/templates/controller.rb +106 -0
  60. data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +57 -0
  61. data/lib/katapult.rb +5 -0
  62. data/lib/katapult/action.rb +44 -0
  63. data/lib/katapult/application_model.rb +45 -0
  64. data/lib/katapult/attribute.rb +83 -0
  65. data/lib/katapult/element.rb +72 -0
  66. data/lib/katapult/generator.rb +28 -0
  67. data/lib/katapult/model.rb +33 -0
  68. data/lib/katapult/navigation.rb +22 -0
  69. data/lib/katapult/parser.rb +39 -0
  70. data/lib/katapult/util.rb +16 -0
  71. data/lib/katapult/version.rb +3 -0
  72. data/lib/katapult/wui.rb +77 -0
  73. data/script/console +16 -0
  74. data/spec/action_spec.rb +44 -0
  75. data/spec/attribute_spec.rb +48 -0
  76. data/spec/model_spec.rb +18 -0
  77. data/spec/spec_helper.rb +19 -0
  78. data/spec/util_spec.rb +23 -0
  79. data/spec/wui_spec.rb +49 -0
  80. metadata +253 -0
@@ -0,0 +1,203 @@
1
+ Feature: Generate Models
2
+
3
+ Background:
4
+ Given a pristine Rails application
5
+ And I install katapult
6
+ And I generate katapult basics
7
+
8
+
9
+ Scenario: Generate ActiveRecord Model
10
+ When I overwrite "lib/katapult/application_model.rb" with:
11
+ """
12
+ model 'Car'
13
+ """
14
+ And I successfully transform the application model
15
+ Then the file "app/models/car.rb" should contain exactly:
16
+ """
17
+ class Car < ActiveRecord::Base
18
+
19
+ def to_s
20
+ "Car##{id}"
21
+ end
22
+ end
23
+
24
+ """
25
+ And there should be a migration with:
26
+ """
27
+ class CreateCars < ActiveRecord::Migration
28
+ def change
29
+ create_table :cars do |t|
30
+
31
+ t.timestamps
32
+ end
33
+ end
34
+ end
35
+
36
+ """
37
+ And the file "spec/models/car_spec.rb" should contain exactly:
38
+ """
39
+ require 'rails_helper'
40
+
41
+ describe Car do
42
+
43
+ describe '#to_s' do
44
+ it 'returns its class name with its id' do
45
+ subject.id = 17
46
+ expect(subject.to_s).to eql("Car#17")
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ """
53
+
54
+
55
+ Scenario: Generate ActiveRecord Model with attributes
56
+ When I overwrite "lib/katapult/application_model.rb" with:
57
+ """
58
+ model 'Person' do |person|
59
+
60
+ # Basic types
61
+ person.attr :age, type: :integer
62
+ person.attr :nick, type: :string
63
+ person.attr :hobby # string is default
64
+
65
+ # Special types
66
+ person.attr :email # type is detected as email
67
+ person.attr :income, type: :money
68
+ person.attr :homepage, type: :url, default: 'http://www.makandra.de'
69
+ person.attr :locked, type: :flag, default: false
70
+ end
71
+ """
72
+ And I successfully transform the application model
73
+ Then the file "app/models/person.rb" should contain exactly:
74
+ """
75
+ class Person < ActiveRecord::Base
76
+ include DoesFlag[:locked, default: false]
77
+ has_defaults({:homepage=>"http://www.makandra.de"})
78
+
79
+ def to_s
80
+ age.to_s
81
+ end
82
+ end
83
+
84
+ """
85
+ And there should be a migration with:
86
+ """
87
+ class CreatePeople < ActiveRecord::Migration
88
+ def change
89
+ create_table :people do |t|
90
+ t.integer :age
91
+ t.string :nick
92
+ t.string :hobby
93
+ t.string :email
94
+ t.decimal :income, precision: 10, scale: 2
95
+ t.string :homepage
96
+ t.boolean :locked
97
+
98
+ t.timestamps
99
+ end
100
+ end
101
+ end
102
+
103
+ """
104
+ And the file "spec/models/person_spec.rb" should contain exactly:
105
+ """
106
+ require 'rails_helper'
107
+
108
+ describe Person do
109
+
110
+ describe '#to_s' do
111
+ it 'returns the #age attribute' do
112
+ subject.age = 778
113
+ expect(subject.to_s).to eql("778")
114
+ end
115
+ end
116
+
117
+ describe '#homepage' do
118
+
119
+ it 'has a default' do
120
+ expect( subject.homepage ).to eql("http://www.makandra.de")
121
+ end
122
+ end
123
+
124
+ describe '#locked' do
125
+
126
+ it 'has a default' do
127
+ expect( subject.locked ).to eql(false)
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ """
134
+ And the specs should pass
135
+
136
+
137
+ Scenario: Get a helpful error message when an attribute has an unknown option
138
+ When I overwrite "lib/katapult/application_model.rb" with:
139
+ """
140
+ model 'Person' do |person|
141
+ person.attr :x, invalid_option: 'here'
142
+ end
143
+ """
144
+ And I transform the application model
145
+ Then the output should contain "Katapult::Attribute does not support option :invalid_option. (Katapult::Element::UnknownOptionError)"
146
+
147
+
148
+ Scenario: Specify assignable values
149
+ When I overwrite "lib/katapult/application_model.rb" with:
150
+ """
151
+ model 'Person' do |person|
152
+ person.attr :age, type: :integer, assignable_values: 9..99
153
+ person.attr :hobby, assignable_values: %w[soccer baseball], default: 'soccer', allow_blank: true
154
+ end
155
+ """
156
+ And I successfully transform the application model
157
+ Then the file "app/models/person.rb" should contain exactly:
158
+ """
159
+ class Person < ActiveRecord::Base
160
+ assignable_values_for :age, {} do
161
+ 9..99
162
+ end
163
+ assignable_values_for :hobby, {:allow_blank=>true, :default=>"soccer"} do
164
+ ["soccer", "baseball"]
165
+ end
166
+
167
+ def to_s
168
+ age.to_s
169
+ end
170
+ end
171
+
172
+ """
173
+ And the file "spec/models/person_spec.rb" should contain exactly:
174
+ """
175
+ require 'rails_helper'
176
+
177
+ describe Person do
178
+
179
+ describe '#to_s' do
180
+ it 'returns the #age attribute' do
181
+ subject.age = 9
182
+ expect(subject.to_s).to eql("9")
183
+ end
184
+ end
185
+
186
+ describe '#age' do
187
+ it { is_expected.to allow_value(99).for(:age) }
188
+ it { is_expected.to_not allow_value(100).for(:age) }
189
+ end
190
+
191
+ describe '#hobby' do
192
+ it { is_expected.to allow_value("baseball").for(:hobby) }
193
+ it { is_expected.to_not allow_value("baseball-unassignable").for(:hobby) }
194
+
195
+ it 'has a default' do
196
+ expect( subject.hobby ).to eql("soccer")
197
+ end
198
+ end
199
+
200
+ end
201
+
202
+ """
203
+ And the specs should pass
@@ -0,0 +1,80 @@
1
+ Feature: Navigation
2
+
3
+ Background:
4
+ Given a pristine Rails application
5
+ And I install katapult
6
+ And I generate katapult basics
7
+
8
+
9
+ Scenario: Generate navigation
10
+
11
+ The navigation is rendered from all WUIs in the application model. It
12
+ consists of links to their index pages.
13
+
14
+ When I overwrite "lib/katapult/application_model.rb" with:
15
+ """
16
+ model 'Customer' do |customer|
17
+ customer.attr :name
18
+ end
19
+
20
+ wui 'Customer', model: 'Customer' do |wui|
21
+ wui.action :index
22
+ wui.action :show
23
+ wui.action :create
24
+ wui.action :update
25
+ wui.action :destroy
26
+ end
27
+
28
+ navigation 'main'
29
+ """
30
+ And I successfully transform the application model
31
+ Then the file "app/models/navigation.rb" should contain exactly:
32
+ """
33
+ class Navigation
34
+ include Navy::Description
35
+
36
+ navigation :main do
37
+ section :customers, "Customers", customers_path
38
+ end
39
+
40
+ end
41
+
42
+ """
43
+ And the file "app/views/layouts/application.html.haml" should contain:
44
+ """
45
+ = render_navigation Navigation.main
46
+ """
47
+ And the file "app/controllers/customers_controller.rb" should contain:
48
+ """
49
+ before_filter :set_section
50
+ """
51
+ And the file "app/controllers/customers_controller.rb" should contain:
52
+ """
53
+ def set_section
54
+ in_sections :customers
55
+ end
56
+ """
57
+
58
+
59
+ Scenario: Homepage (aka root route) is set automatically
60
+
61
+ The first WUI with an index action is set as home page
62
+
63
+ When I overwrite "lib/katapult/application_model.rb" with:
64
+ """
65
+ model 'Customer'
66
+ model 'Elephant'
67
+
68
+ wui 'Elephant' do |wui|
69
+ wui.action :trumpet, scope: :member, method: :post
70
+ end
71
+
72
+ wui 'Customer' do |wui|
73
+ wui.crud
74
+ end
75
+ """
76
+ And I successfully transform the application model
77
+ Then the file "config/routes.rb" should contain:
78
+ """
79
+ root 'customers#index'
80
+ """
@@ -0,0 +1,8 @@
1
+ Then(/^a db table named "(.*?)" should exist$/) do |table|
2
+ require 'sequel'
3
+ db_path = "sqlite://#{current_dir}/db/development.sqlite3"
4
+
5
+ Sequel.connect(db_path) { |db|
6
+ db.table_exists?(table)
7
+ }.should == true
8
+ end
@@ -0,0 +1,14 @@
1
+ When /^I replace "(.*?)" with "(.*?)" inside "(.*?)"$/ do |a, b, path|
2
+ in_current_dir do
3
+ content = File.read(path)
4
+ content.gsub! a, b
5
+ File.open(path, 'w') { |file| file.puts(content) }
6
+ end
7
+ end
8
+
9
+ Then /^there should be a migration with:$/ do |migration|
10
+ migrations_path = File.join(current_dir, 'db', 'migrate', '*.rb')
11
+
12
+ all_migrations = Dir.glob(migrations_path).map(&File.method(:read)).join
13
+ all_migrations.should include(migration)
14
+ end
@@ -0,0 +1,14 @@
1
+ When /^I install katapult$/ do
2
+ append_to_file 'Gemfile', "gem 'katapult', path: '../../..'"
3
+ run_simple('bin/rails generate katapult:install')
4
+ end
5
+
6
+ # This step is required for any feature because it generates config/database.yml
7
+ When /^I generate katapult basics$/ do
8
+ run_simple('bin/rails generate katapult:basics')
9
+ end
10
+
11
+ When /^I( successfully)? transform the application model$/ do |require_success|
12
+ # the second parameter would be true when not specified
13
+ run_simple('bin/rails generate katapult:transform lib/katapult/application_model.rb', !!require_success)
14
+ end
@@ -0,0 +1,44 @@
1
+ module KatapultRailsHelper
2
+
3
+ def with_aruba_timeout(timeout, &block)
4
+ original_aruba_timeout = @aruba_timeout_seconds
5
+ @aruba_timeout_seconds = timeout
6
+
7
+ block.call
8
+ ensure
9
+ @aruba_timeout_seconds = original_aruba_timeout
10
+ end
11
+
12
+ def create_cached_app(name)
13
+ job = 'Cached Rails app generation'
14
+ rails_new_command = "bundle exec rails new #{name} --skip-test-unit --skip-bundle --database mysql"
15
+
16
+ puts "#{job} started (in #{Dir.pwd})"
17
+ system(rails_new_command) or raise "#{job} failed"
18
+ puts "#{job} done."
19
+ end
20
+
21
+ def ensure_bundled(path)
22
+ Dir.chdir(path) do
23
+ Bundler.with_clean_env do
24
+ system('bundle check &> /dev/null') or system('bundle install')
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ World(KatapultRailsHelper)
31
+
32
+
33
+ Given /^a pristine Rails application$/ do
34
+ with_aruba_timeout(120) do
35
+ Dir.chdir('tmp') do
36
+ create_cached_app('cached_test_app') unless File.directory?('cached_test_app')
37
+ ensure_bundled('cached_test_app')
38
+ end
39
+
40
+ # copy cached app to aruba directory
41
+ FileUtils.cp_r('tmp/cached_test_app', File.join(current_dir, 'katapult_test_app'))
42
+ cd 'katapult_test_app' # Aruba::Api method
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ Then /^the specs should pass$/ do
2
+ run_simple('bin/rspec')
3
+ end
4
+
5
+ Then /^the features should pass$/ do
6
+ run_simple('bin/cucumber')
7
+ end
@@ -0,0 +1,16 @@
1
+ require 'aruba/cucumber'
2
+ require 'pry'
3
+
4
+ # Make sure tests use the correct katapult gem
5
+ ENV['KATAPULT_GEMFILE_OPTIONS'] = ", path: '../../..'"
6
+
7
+ Before do
8
+ @aruba_timeout_seconds = 30
9
+
10
+ unset_bundler_env_vars
11
+ run_simple 'spring stop # Clean up in case the After hook did not run'
12
+ end
13
+
14
+ After do
15
+ run_simple 'spring stop'
16
+ end
@@ -0,0 +1,319 @@
1
+ Feature: Web User Interface
2
+
3
+ Background:
4
+ Given a pristine Rails application
5
+ And I install katapult
6
+ And I generate katapult basics
7
+
8
+
9
+ Scenario: Generate a Web User Interface
10
+ When I overwrite "lib/katapult/application_model.rb" with:
11
+ """
12
+ model 'Customer' do |customer|
13
+ customer.attr :name
14
+ customer.attr :age, type: :integer
15
+
16
+ customer.attr :email
17
+ customer.attr :revenue, type: :money
18
+ customer.attr :homepage, type: :url, default: 'http://www.makandra.de'
19
+ customer.attr :locked, type: :flag, default: false
20
+ end
21
+
22
+ wui 'Customer', model: 'Customer' do |wui|
23
+ wui.crud
24
+ wui.action :get_member, method: :get, scope: :member
25
+ wui.action :post_member, method: :post, scope: :member
26
+ wui.action :get_collection, method: :get, scope: :collection
27
+ wui.action :put_collection, method: :put, scope: :collection
28
+ end
29
+ """
30
+ And I successfully transform the application model
31
+ Then the file "app/controllers/customers_controller.rb" should contain exactly:
32
+ """
33
+ class CustomersController < ApplicationController
34
+
35
+ def index
36
+ load_customers
37
+ end
38
+
39
+ def show
40
+ load_customer
41
+ end
42
+
43
+ def new
44
+ build_customer
45
+ end
46
+
47
+ def create
48
+ build_customer
49
+ save_customer or render :new
50
+ end
51
+
52
+ def edit
53
+ load_customer
54
+ build_customer
55
+ end
56
+
57
+ def update
58
+ load_customer
59
+ build_customer
60
+ save_customer or render :edit
61
+ end
62
+
63
+ def destroy
64
+ load_customer
65
+ @customer.destroy
66
+ redirect_to customers_path
67
+ end
68
+
69
+ def get_member
70
+ load_customer
71
+ end
72
+
73
+ def post_member
74
+ load_customer
75
+ redirect_to @customer
76
+ end
77
+
78
+ def get_collection
79
+ load_customers
80
+ end
81
+
82
+ def put_collection
83
+ load_customers
84
+ redirect_to customers_path
85
+ end
86
+
87
+ private
88
+
89
+ def load_customers
90
+ @customers ||= customer_scope.to_a
91
+ end
92
+
93
+ def load_customer
94
+ @customer ||= customer_scope.find(params[:id])
95
+ end
96
+
97
+ def build_customer
98
+ @customer ||= customer_scope.build
99
+ @customer.attributes = customer_params
100
+ end
101
+
102
+ def save_customer
103
+ if @customer.save
104
+ redirect_to @customer
105
+ end
106
+ end
107
+
108
+ def customer_params
109
+ customer_params = params[:customer]
110
+ customer_params ? customer_params.permit([:name, :age, :email, :revenue, :homepage, :locked]) : {}
111
+ end
112
+
113
+ def customer_scope
114
+ Customer.scoped
115
+ end
116
+
117
+ end
118
+
119
+ """
120
+ And the file "config/routes.rb" should contain:
121
+ """
122
+ resources :customers, only: [:index, :show, :new, :create, :edit, :update, :destroy] do
123
+ member do
124
+ get 'get_member'
125
+ post 'post_member'
126
+ end
127
+ collection do
128
+ get 'get_collection'
129
+ put 'put_collection'
130
+ end
131
+ end
132
+ """
133
+ And the file "app/views/customers/index.html.haml" should contain exactly:
134
+ """
135
+ %h1
136
+ Customers
137
+
138
+ .tools
139
+ = link_to 'Add customer', new_customer_path, class: 'tools__button is_primary'
140
+ = link_to 'Get Collection', get_collection_customers_path, class: 'tools__button'
141
+ = link_to 'Put Collection', put_collection_customers_path, class: 'tools__button'
142
+
143
+ - if @customers.any?
144
+ %table.items
145
+ - @customers.each do |customer|
146
+ %tr
147
+ %td
148
+ = link_to customer.to_s, customer, class: 'hyperlink'
149
+ %td
150
+ .items__actions
151
+ = link_to 'Edit', edit_customer_path(customer), class: 'items__action'
152
+ = link_to 'Destroy', customer_path(customer), method: :delete, class: 'items__action', confirm: 'Really destroy?'
153
+ = link_to 'Get Member', get_member_customer_path(customer), class: 'items__action'
154
+ = link_to 'Post Member', post_member_customer_path(customer), class: 'items__action', method: :post
155
+
156
+ - else
157
+ %p.help-block
158
+ There are no customers yet.
159
+
160
+ """
161
+ And the file "app/views/customers/show.html.haml" should contain exactly:
162
+ """
163
+ %h1
164
+ = @customer.to_s
165
+
166
+ .tools
167
+ = link_to 'All customers', customers_path, class: 'tools__button'
168
+ = link_to 'Edit', edit_customer_path(@customer), class: 'tools__button is_primary'
169
+ = link_to 'Destroy', customer_path(@customer), method: :delete, class: 'tools__button', confirm: 'Really destroy?'
170
+ = link_to 'Get Member', get_member_customer_path(@customer), class: 'tools__button'
171
+ = link_to 'Post Member', post_member_customer_path(@customer), class: 'tools__button', method: :post
172
+
173
+ %dl.values
174
+ %dt
175
+ = Customer.human_attribute_name(:name)
176
+ %dd
177
+ = @customer.name
178
+ %dt
179
+ = Customer.human_attribute_name(:age)
180
+ %dd
181
+ = @customer.age
182
+ %dt
183
+ = Customer.human_attribute_name(:email)
184
+ %dd
185
+ = mail_to @customer.email, nil, class: 'hyperlink'
186
+ %dt
187
+ = Customer.human_attribute_name(:revenue)
188
+ %dd
189
+ = @customer.revenue
190
+
191
+ %dt
192
+ = Customer.human_attribute_name(:homepage)
193
+ %dd
194
+ = link_to @customer.homepage, @customer.homepage, class: 'hyperlink'
195
+ %dt
196
+ = Customer.human_attribute_name(:locked)
197
+ %dd
198
+ = @customer.locked ? 'Yes' : 'No'
199
+
200
+ """
201
+ And the file "app/views/customers/new.html.haml" should contain exactly:
202
+ """
203
+ %h1
204
+ Add customer
205
+
206
+ = render 'form'
207
+
208
+ """
209
+ And the file "app/views/customers/edit.html.haml" should contain exactly:
210
+ """
211
+ %h1
212
+ = @customer.to_s
213
+
214
+ = render 'form'
215
+
216
+ """
217
+ And the file "app/views/customers/_form.html.haml" should contain exactly:
218
+ """
219
+ = form_for @customer do |form|
220
+
221
+ %dl.controls
222
+ %dt
223
+ = form.label :name
224
+ %dd
225
+ = form.text_field :name
226
+ %dt
227
+ = form.label :age
228
+ %dd
229
+ = form.number_field :age
230
+ %dt
231
+ = form.label :email
232
+ %dd
233
+ = form.email_field :email
234
+ %dt
235
+ = form.label :revenue
236
+ %dd
237
+ = form.number_field :revenue
238
+
239
+ %dt
240
+ = form.label :homepage
241
+ %dd
242
+ = form.url_field :homepage
243
+ %dt
244
+ = form.label :locked
245
+ %dd
246
+ = form.check_box :locked
247
+
248
+ .tools
249
+ = form.submit 'Save', class: 'tools__button is_primary'
250
+ - cancel_path = @customer.new_record? ? customers_path : customer_path(@customer)
251
+ = link_to 'Cancel', cancel_path, class: 'tools__button'
252
+
253
+ """
254
+ And the file "app/views/customers/get_member.html.haml" should contain exactly:
255
+ """
256
+ %h1
257
+ Get Member
258
+
259
+ .tools
260
+ = link_to 'All customers', customers_path, class: 'tools__button'
261
+
262
+ """
263
+ But a file named "app/views/customers/post_member.html.haml" should not exist
264
+ And the file "app/views/customers/get_collection.html.haml" should contain exactly:
265
+ """
266
+ %h1
267
+ Get Collection
268
+
269
+ .tools
270
+ = link_to 'All customers', customers_path, class: 'tools__button'
271
+
272
+ """
273
+ But a file named "app/views/customers/put_collection.html.haml" should not exist
274
+ And the file "features/customers.feature" should contain exactly:
275
+ """
276
+ Feature: Customers
277
+
278
+ Scenario: CRUD customers
279
+ Given I am on the list of customers
280
+
281
+ # create
282
+ When I follow "Add customer"
283
+ And I fill in "Name" with "name-string"
284
+ And I fill in "Age" with "778"
285
+ And I fill in "Email" with "email@example.com"
286
+ And I fill in "Revenue" with "2.21"
287
+ And I fill in "Homepage" with "homepage.example.com"
288
+ And I check "Locked"
289
+ And I press "Save"
290
+
291
+ # read
292
+ Then I should be on the page for the customer above
293
+ And I should see "name-string"
294
+ And I should see "778"
295
+ And I should see "email@example.com"
296
+ And I should see "2.21"
297
+ And I should see "homepage.example.com"
298
+ And I should see "Locked Yes"
299
+
300
+ # update
301
+ When I follow "Edit"
302
+ Then I should be on the form for the customer above
303
+ And the "Name" field should contain "name-string"
304
+ And the "Age" field should contain "778"
305
+ And the "Email" field should contain "email@example.com"
306
+ And the "Revenue" field should contain "2.21"
307
+ And the "Homepage" field should contain "homepage.example.com"
308
+ And the "Locked" checkbox should be checked
309
+
310
+ # destroy
311
+ When I go to the list of customers
312
+ Then I should see "name-string"
313
+
314
+ When I follow "Destroy"
315
+ Then I should be on the list of customers
316
+ But I should not see "name-string"
317
+
318
+ """
319
+ And the features should pass