Удаление местоположения.
В первой части мы создали приложение, в котором перечислены местоположения на английском и испанском языках.
Во второй части мы добавили возможность создавать локации на английском и испанском языках.
В третьей части мы добавили возможность редактировать локации на английском и испанском языках.
В этой части мы удалим местоположение, показывая пути локализации. Помните, что мы используем Cucumber и BDD для запуска нашего приложения.
Почему бы нам не настроить четыре местоположения, а затем удалить третье местоположение?
Как бы вы написали сценарий для этого? Мы можем написать сценарий как:
@wip | |
Scenario Outline: Delete a location | |
Given there are 4 locations | |
And I am on the <language> site | |
And I «<action>» the 3rd location | |
Then I should see 3 locations | |
Examples: | |
| language | action | | |
| en | Delete | |
Это даст нам четыре местоположения, убедитесь, что мы находимся на английском сайте, удалите третье местоположение и оставим три местоположения. Также обратите внимание, что мы снова используем тег @wip, чтобы сосредоточиться на этом тесте.
Теперь давайте запустим Cucumber и посмотрим, что получится.
cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/managing_locations.feature:43 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I «<action>« the 3rd location # features/managing_locations.feature:45 | |
Then I should see 3 locations # features/managing_locations.feature:46 | |
Examples: | |
| language | action | | |
| en | Delete | | |
1 scenario (1 undefined) | |
4 steps (1 skipped, 3 undefined) | |
0m1.299s | |
You can implement step definitions for undefined steps with these snippets: | |
Given /^there are (\d+) locations$/ do |arg1| | |
pending # express the regexp above with the code you wish you had | |
end | |
Given /^I «([^«]*)« the (\d+)rd location$/ do |arg1, arg2| | |
pending # express the regexp above with the code you wish you had | |
end | |
Then /^I should see (\d+) locations$/ do |arg1| | |
pending # express the regexp above with the code you wish you had | |
end | |
The —wip switch was used, so the failures were expected. All is good. |
По крайней мере один из предыдущих шагов, которые мы написали, используется. Давайте реализуем определения шагов для неопределенных. Откройте файл features / step_definitions / location_step.rb и скопируйте в ожидающие шаги.
Given /^there are (\d+) locations$/ do |arg1| | |
pending # express the regexp above with the code you wish you had | |
end | |
Given /^I «([^»]*)» the (\d+)rd location$/ do |arg1, arg2| | |
pending # express the regexp above with the code you wish you had | |
end | |
Then /^I should see (\d+) locations$/ do |arg1| | |
pending # express the regexp above with the code you wish you had | |
end |
На первом этапе нам нужно, чтобы заводская девушка создала все локации. Этот шаг будет повторяться, сколько бы мы ни говорили, создавая местоположение на каждой итерации. Почему бы нам не назвать их «Местоположение 1», «Место 2» и так далее.
Каков был бы способ сделать это? Вот как я это сделал.
Given /^there are (\d+) locations$/ do |number| | |
number.to_i.times { |x| Factory(:location, :name => x+1) } | |
end |
Вероятно, не самый шикарный, но вы можете изменить его после того, как он пройдет. Давайте запустим огурец и посмотрим, что получится.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:40 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I «<action>« the 3rd location # features/step_definitions/location_steps.rb:44 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:48 | |
Examples: | |
| language | action | | |
| en | Delete | | |
TODO (Cucumber::Pending) | |
./features/step_definitions/location_steps.rb:45:in `/^I «([^«]*)« the (\d+)rd location$/’ | |
features/managing_locations.feature:45:in `And I «<action>« the 3rd location‘ | |
1 scenario (1 pending) | |
4 steps (1 skipped, 1 pending, 2 passed) | |
0m1.410s | |
The —wip switch was used, so the failures were expected. All is good. |
Мы создаем места, которые никому не нужны.
Время реализовать шаг удаления местоположения :
- перейти на страницу индекса местоположений
- найти третье место в списке
- нажмите на ссылку, чтобы удалить ее
Как вы находите третье место в списке?
В Капибаре вы можете использовать обзор. Например, мы можем заглянуть в список, чтобы найти n-го ребенка.
Given /^I (.*) the (\d+)rd location$/ do |action, pos| | |
visit(locations_path) | |
within(«ul li:nth-child(#{pos.to_i})») do | |
click_link(action) | |
end | |
end |
Сначала мы идем на страницу индекса для определения местоположения. Так как мы ищем 3-е место, мы ищем его в списке. Как только ссылка найдена, clink_link
Давайте запустим огурец и посмотрим, что получится.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I «<action>« the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
no link with title, id or text ‘«Delete»‘ found (Capybara::ElementNotFound) | |
(eval):2:in `click_link‘ | |
./features/step_definitions/location_steps.rb:40:in `block (2 levels) in <top (required)>‘ | |
./features/step_definitions/location_steps.rb:39:in `/^I (.*) the (\d+)rd location$/‘ | |
features/managing_locations.feature:45:in `And I «<action>» the 3rd location‘ | |
Failing Scenarios: | |
cucumber -p wip features/managing_locations.feature:42 # Scenario: Delete a location | |
1 scenario (1 failed) | |
4 steps (1 failed, 1 skipped, 2 passed) | |
0m2.200s | |
The —wip switch was used, so the failures were expected. All is good. |
Ссылка на уничтожение пока не существует. Откройте файл app / views / location / index.html.erb и добавьте ссылку уничтожения.
<%= link_to «Delete», location, confirm: ‘Are you sure?’, method: :delete %> |
Сохраните файл и запустите Cucumber.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
No route matches [DELETE] «/en/locations/3« (ActionController::RoutingError) | |
(eval):2:in `click_link‘ | |
./features/step_definitions/location_steps.rb:40:in `block (2 levels) in <top (required)>‘ | |
./features/step_definitions/location_steps.rb:39:in `/^I (.*) the (\d+)rd location$/‘ | |
features/managing_locations.feature:45:in `And I <action> the 3rd location‘ | |
Failing Scenarios: | |
cucumber -p wip features/managing_locations.feature:42 # Scenario: Delete a location | |
1 scenario (1 failed) | |
4 steps (1 failed, 1 skipped, 2 passed) | |
0m2.194s | |
The —wip switch was used, so the failures were expected. All is good. |
Огурец жалуется на отсутствие удаляемого маршрута. Мы были здесь раньше с шоу и обновлением. Вот файл /config/route.rb
International::Application.routes.draw do | |
scope ‘(:locale)’ do | |
match ‘locations/’ => ‘locations#index’, :as => :locations, :via => [:get] | |
match ‘locations/new’ => ‘locations#new’, :as => :new_location | |
match «locations» => ‘locations#create’, :as => :locations, :via => [:post] | |
match «locations/(:id)» => ‘locations#show’, :as => :location, :via => [:get] | |
match ‘locations/:id/edit’ => ‘locations#edit’, :as => :edit_location | |
match «locations/(:id)» => ‘locations#update’, :as => :location, :via => [:put] | |
match «locations/(:id)» => ‘locations#destroy’, :as => :location, :via => [:delete] | |
root :to => ‘locations#index’ | |
end | |
end |
Сохраните файл и запустите Cucumber.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
The action ‘destroy‘ could not be found for LocationsController (AbstractController::ActionNotFound) | |
(eval):2:in `click_link‘ | |
./features/step_definitions/location_steps.rb:40:in `block (2 levels) in <top (required)>‘ | |
./features/step_definitions/location_steps.rb:39:in `/^I (.*) the (\d+)rd location$/‘ | |
features/managing_locations.feature:45:in `And I <action> the 3rd location‘ | |
Failing Scenarios: | |
cucumber -p wip features/managing_locations.feature:42 # Scenario: Delete a location | |
1 scenario (1 failed) | |
4 steps (1 failed, 1 skipped, 2 passed) | |
0m2.465s | |
The —wip switch was used, so the failures were expected. All is good. |
Никаких действий для «уничтожить». Откройте файл /app/controllers/locations_controller.rb и добавьте этот метод.
# DELETE /locations/1 | |
# DELETE /locations/1.json | |
def destroy | |
@location = Location.find(params[:id]) | |
@location.destroy | |
respond_to do |format| | |
format.html { redirect_to locations_url } | |
format.json { head :ok } | |
end | |
end |
Сохраните файл и запустите Cucumber.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
TODO (Cucumber::Pending) | |
./features/step_definitions/location_steps.rb:45:in `/^I should see (\d+) locations$/‘ | |
features/managing_locations.feature:46:in `Then I should see 3 locations‘ | |
1 scenario (1 pending) | |
4 steps (1 pending, 3 passed) | |
0m2.315s | |
The —wip switch was used, so the failures were expected. All is good. |
Теперь для реализации следующего шага. Если у нас было четыре местоположения, и мы удаляем одно, то у нас должно быть три (Math is FUN). Итак, количество мест должно равняться трем? Ответ в вопросе
Then /^I should see (\d+) locations$/ do |number| | |
Location.count.should == number.to_i | |
end |
Я чувствую, что это сработает. О, Огурец, пожалуйста, дайте нам знать, если мы сдадимся.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
1 scenario (1 passed) | |
4 steps (4 passed) | |
0m2.295s | |
The —wip switch was used, so I didn‘t expect anything to pass. These scenarios passed: | |
(::) passed scenarios (::) | |
features/managing_locations.feature:50:in `| en | Delete |‘ |
GREEN! Перерыв.
Боррар Ubicación
Настало время сделать то же самое на испанском. Помните, что делать? Бинго. Вы добавляете новую строку в таблицу примера.
@wip | |
Scenario Outline: Delete a location | |
Given there are 4 locations | |
And I am on the <language> site | |
And I <action> the 3rd location | |
Then I should see 3 locations | |
Examples: | |
| language | action | | |
| en | Delete | | |
| es | Borrar | |
Как вы думаете, это будет зеленым? Посмотрим.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
| es | Borrar | | |
no link with title, id or text ‘Borrar‘ found (Capybara::ElementNotFound) | |
(eval):2:in `click_link‘ | |
./features/step_definitions/location_steps.rb:40:in `block (2 levels) in <top (required)>‘ | |
./features/step_definitions/location_steps.rb:39:in `/^I (.*) the (\d+)rd location$/‘ | |
features/managing_locations.feature:45:in `And I <action> the 3rd location‘ | |
Failing Scenarios: | |
cucumber -p wip features/managing_locations.feature:42 # Scenario: Delete a location | |
2 scenarios (1 failed, 1 passed) | |
8 steps (1 failed, 1 skipped, 6 passed) | |
0m2.436s | |
The —wip switch was used, so I didn‘t expect anything to pass. These scenarios passed: | |
(::) passed scenarios (::) | |
features/managing_locations.feature:50:in `| en | Delete |‘ |
D’о! Мы еще даже не смотрели на локали. Нам нужно добавить перевод для destroy — на английском и испанском языках. Вы помните, где они и что должно произойти?
конфиг / локали / en.yml
# Sample localization file for English. Add more files in this directory for other locales. | |
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. | |
en: | |
helpers: | |
submit: | |
create: «Create %{model}« | |
update: «Update %{model}« | |
locations: | |
index: | |
title_html: «Locations« | |
destroy_html: «Delete« | |
form: | |
name_html: «Name« |
confif / локали / es.yml
# Sample localization file for English. Add more files in this directory for other locales. | |
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. | |
es: | |
helpers: | |
submit: | |
create: «crear %{model}« | |
update: «Actualizar %{model}« | |
locations: | |
index: | |
title_html: «Locaciones« | |
destroy_html: «Borrar« | |
form: | |
name_html: «Nombre« |
Что еще нам нужно сделать? Ты понял. Измените ссылку уничтожения, чтобы показать перевод.
<%= link_to t(‘.destroy_html’), location, confirm: ‘Are you sure?’, method: :delete %> |
Все зеленые, пожалуйста.
$ cucumber —profile wip | |
Using the wip profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
@wip | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:33 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:37 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:44 | |
Examples: | |
| language | action | | |
| en | Delete | | |
| es | Borrar | | |
2 scenarios (2 passed) | |
8 steps (8 passed) | |
0m2.288s | |
The —wip switch was used, so I didn‘t expect anything to pass. These scenarios passed: | |
(::) passed scenarios (::) | |
features/managing_locations.feature:50:in `| en | Delete |‘ | |
features/managing_locations.feature:51:in `| es | Borrar |‘ |
Ладно, удалите тег wip и бросьте кости …
$ cucumber | |
Using the default profile… | |
Feature: Manage locations | |
In order to manage locations | |
As a user | |
I want to create and edit my locations. | |
Scenario Outline: List locations # features/managing_locations.feature:6 | |
Given there is a location named «<location>« # features/step_definitions/location_steps.rb:1 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
When I am on the locations page # features/step_definitions/location_steps.rb:9 | |
Then I should see «<title>« # features/step_definitions/location_steps.rb:13 | |
And I should see «<location>« # features/step_definitions/location_steps.rb:13 | |
Examples: | |
| location | language | title | | |
| location 1 | en | Locations | | |
| location 2 | es | Locaciones | | |
Scenario Outline: : Create a new location # features/managing_locations.feature:18 | |
Given I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I am on new location page # features/step_definitions/location_steps.rb:21 | |
And I fill in «<name>« with «<location>« # features/step_definitions/location_steps.rb:25 | |
And press «<button>« # features/step_definitions/location_steps.rb:29 | |
Then I should see «<location>« # features/step_definitions/location_steps.rb:13 | |
Examples: | |
| language | name | location | button | | |
| en | Name | location 1 | Create | | |
| es | Nombre | location 1 | crear | | |
Scenario Outline: Edit a location # features/managing_locations.feature:30 | |
Given I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And there is a location named «<location>« # features/step_definitions/location_steps.rb:1 | |
When I «<action>« the location «<field>« to «<new_name>« # features/step_definitions/location_steps.rb:33 | |
Then I should see «<new_name>« # features/step_definitions/location_steps.rb:13 | |
Examples: | |
| language | location | action | field | new_name | | |
| en | location 1 | Update | Name | location has changed | | |
| es | location 1 | Actualizar | Nombre | location has changed | | |
Scenario Outline: Delete a location # features/managing_locations.feature:42 | |
Given there are 4 locations # features/step_definitions/location_steps.rb:40 | |
And I am on the <language> site # features/step_definitions/location_steps.rb:5 | |
And I <action> the 3rd location # features/step_definitions/location_steps.rb:44 | |
Then I should see 3 locations # features/step_definitions/location_steps.rb:51 | |
Examples: | |
| language | action | | |
| en | Delete | | |
| es | Borrar | | |
8 scenarios (8 passed) | |
36 steps (36 passed) | |
0m2.658s |
Отправим его! Ок, может и нет. Я уверен, что может произойти еще какой-нибудь рефакторинг и сушка. Взять, к примеру, маршруты.
International::Application.routes.draw do | |
scope ‘(:locale)’ do | |
match ‘locations/’ => ‘locations#index’, :as => :locations, :via => [:get] | |
match «locations» => ‘locations#create’, :via => [:post] | |
match ‘locations/new’ => ‘locations#new’, :as => :new_location | |
match ‘locations/:id/edit’ => ‘locations#edit’, :as => :edit_location | |
match «locations/(:id)» => ‘locations#show’, :as => :location, :via => [:get] | |
match «locations/(:id)» => ‘locations#update’, :via => [:put] | |
match «locations/(:id)» => ‘locations#destroy’, :via => [:delete] | |
root :to => ‘locations#index’ | |
end | |
end |
Запустите грабли в терминале, и вы должны увидеть что-то вроде этого.
$ rake routes | |
locations GET (/:locale)/locations(.:format) locations#index | |
POST (/:locale)/locations(.:format) locations#create | |
new_location (/:locale)/locations/new(.:format) locations#new | |
edit_location (/:locale)/locations/:id/edit(.:format) locations#edit | |
location GET (/:locale)/locations(/:id)(.:format) locations#show | |
PUT (/:locale)/locations(/:id)(.:format) locations#update | |
DELETE (/:locale)/locations(/:id)(.:format) locations#destroy | |
root /(:locale)(.:format) locations#index |
Мы можем перефакторизовать расположение маршрутов одной линией.
International::Application.routes.draw do | |
scope ‘(:locale)’ do | |
resources :locations | |
root :to => ‘locations#index’ | |
end | |
end |
Теперь перезапускаем рейковые маршруты
$ rake routes | |
locations GET (/:locale)/locations(.:format) locations#index | |
POST (/:locale)/locations(.:format) locations#create | |
new_location GET (/:locale)/locations/new(.:format) locations#new | |
edit_location GET (/:locale)/locations/:id/edit(.:format) locations#edit | |
location GET (/:locale)/locations/:id(.:format) locations#show | |
PUT (/:locale)/locations/:id(.:format) locations#update | |
DELETE (/:locale)/locations/:id(.:format) locations#destroy | |
root /(:locale)(.:format) locations#index |
Я знаю. Почему мы не сделали это в первую очередь? Две причины:
1) Мы просто писали код для прохождения тестов. Только позже мы поняли, что нам нужны они все.
2) Посмотрите, как много вы узнали о магии маршрутов. Часть тайны была раскрыта.
Теперь перезапустите огурец и убедитесь, что все тесты все еще проходят. Твой?
Я знаю, что во время этой серии мы проделали долгий путь. Это вывело тайну из интернационализации? Теперь пришло время сделать ваши приложения глобальными.
Ура,
Джон