Статьи

Loccasions: наем мастера, наследование ресурсов и случаи

В этом посте я хочу наконец закончить последовательность Occasions MVC. Это седьмой пост в серии , и я думал, что мы будем дальше. Те, кто несет ответственность за наш менее ожидаемый прогресс, были уволены. Однако сначала давайте немного упростим запуск среды разработки. Может быть, это положит начало нашей производительности …

Наем бригадира

Каждый раз, когда я хочу взломать Loccasions, я должен запускать охрану, веб-сервер (пока rails s ) и mongodb вместе с моей сессией vim. В обязательном порядке я забываю запустить mongodb, поэтому повсюду взрывается охрана. Это надоедливая трата времени, и в начале моего хакерского сеанса у меня складывается плохое настроение. Я хотел бы немного разобраться с этим, поэтому я привожу Формана . Foreman — это «менеджер приложений на основе Procfile», который, по словам Google, означает, что вы можете создать Procfile (мы поместим наш в корень приложения) и перечислить процессы, которые мы хотим запустить Foreman.

Это звучит положительно для меня, так что я добавляю gem foreman, "~> 0.24.0" в группы :development и :test в моем Gemfile, быструю bundle install и foreman официально находится в ведомости.

У меня есть три процесса, которые я хочу запустить в разработке: mongod , guard и rails s , поэтому мой Procfile выглядит следующим образом:

 web: rails s test: guard db: mongod --dbpath=/Users/ggoodrich/db/data 

Теперь я могу напечатать foreman start в своем каталоге приложений, и Forman запустит эти три процесса.

Здесь нет каски

Мне нравится представлять неопрятного парня в каске, кричащего на процессы («ВСЕ ПРАВО, база данных! Сними свой ленивый осколок и готовься к данным!») Хотя, если честно, я думаю, что лучшим названием Formean было бы Procadile. Я уже вижу логотип… возможно, мне нужно заняться непрограммированием…

ОК, может быть, логотип будет лучше, чем этот …

Случаев

Наконец-то мы подошли к тому моменту, когда сможем спроектировать, как мы будем добавлять события. Случаи, как вы помните, принадлежат Событию. Повод — это отдельное происшествие этого события. Таким образом, если ваше событие «Продажа печенья для девочек-скаутов», то случаем для этого события может быть «2 февраля 2010 года» с широтой / долготой 35,223 / -85,443 (дом моего соседа) и примечанием «2 ящика». Самоа ». Таким образом, еще один повод для этого события может иметь дату 10 февраля 2010 года с лат / лонг (лат / лонг для школы моего ребенка) и записку с надписью «Миссис. Whatsherface купил 1 коробку тонких монетных дворов ».

Давайте напишем несколько юнит-тестов вокруг этой идеи. Поместите это в spec / models / reason_spec.rb

 require 'spec_helper' describe 'Occasion' do before do @event = Factory.build(:event) @occasion = @event.occasions.build end it "should belong to an event" do @occasion.event.should_not be_nil end it "should have a time and date of occurrence" do dt = Time.now @occasion.occurred_at = dt @occasion.occurred_at.to_s.should == dt.to_s end it "should have a latitude and longitude" do @occasion.latitude = -85.000 @occasion.longitude = 35.3232 @occasion.latitude.should == -85.000 @occasion.longitude.should == 35.3232 end it "should have a note" do @occasion.note = "This thang went down" @occasion.note.should == "This thang went down" end end 

Эти тесты не пройдены, потому что мы не создали модель Occasion, а у Event нет метода occasions . Быстрые rails g model Occasion occurred_at:datetime latitude:float longitude:float note:text -s позаботится об этом. (Примечание: -s пропускает существующие файлы, которые являются нашими спецификациями, которые мы уже создали). Мы должны изменить сгенерированный файл модели, чтобы сообщить ему, что он живет в Events. Наш файл app / models / случай.rb выглядит следующим образом: (Я пошел дальше и добавил проверки и средства доступа)

 class Occasion include Mongoid::Document field :occurred_at, :type => Time field :latitude, :type => Float field :longitude, :type => Float field :note, :type => String embedded_in :event, :inverse_of => :occasions validates :occurred_at, :latitude, :longitude, :presence => true attr_accessible :occurred_at, :latitude, :longitude, :note end 

Кроме того, откройте models / event.rb и добавьте embeds_many :occasions под embedded_in :user строкой embedded_in :user . Я снова посмотрел на этот файл и понял, какие атрибуты в Event должны быть доступны. Это плохое мохо, поэтому я добавил attr_accessible :name, :description в модель Event.

Изменение конфигурации Spork

В процессе написания спецификации модели Occasion я добавил новую фабрику для создания Occasion в spec / factories.rb.

 factory :occasion do latitude 35.1234 longitude -80.1234 occurred_at DateTime.now note "Test Occasion" event end 

С моей новой фабрикой я изменил блок before в спецификации случая, чтобы использовать его. Это привело к тому, что мои спецификации взорвались повсюду с ошибками вроде:

Донде эст ми ми завод?

Итак, моя новая фантастическая среда Spork / Guard не перезаряжала фабрики. Я лихорадочно повернулся к Google и спросил: «ЧТО СЕЙЧАС ??!?» Google спокойно ответил: «Поместите это в блок Spork.each_run в вашем файле spec / spec_helper.rb , мой человек».

 # Reload our factories FactoryGirl.factories.clear Dir[Rails.root.join("spec/factories.rb")].each{|f| load f} 

Guard знает, что нужно перезагрузить среду RSpec, когда вы возитесь с spec_helper.rb, поэтому мои тесты снова были счастливы. Пока мы там, давайте добавим кое-что для перезагрузки маршрутов:

 # Reload routes Loccasions::Application.reload_routes! 

Теперь, когда у нас есть модель, нам нужен способ ее создания.

Вы говорите Картофель « поторопись », а я говорю Потахтое « Поводок контролера»

На этом этапе мы все должны быть олимпийскими золотыми медалистами при создании ванильного контроллера Rails для ресурса. В этом случае наши ресурсы случаются. Идите и попробуйте получить работающий (и специальный) контроллер для Occasions. Вы можете проверить, что я сделал с этим гистом и посмотреть, как оно вышло.

Унаследованные ресурсы

Вау! Что с этим? Это не похоже на то, что мы сделали для контроллера событий. Ты прав, это не похоже на это. Я обманул тебя. Хосе Валим (Jose Valim) из Plataformatec (и Crafting Rails Applications ) прославился созданием драгоценного камнятека для того, чтобы 95% всех контроллеров RESTful в Rails делали то же самое. Используя жемчужину Хосе, мы можем получить наш OccasionsController, наследующий от InheritedResources::Base и мы получим 7 ~~ Deadly ~~ общих действий контроллера бесплатно. Я сердце этого сообщества. (Между прочим, сейчас самое время добавить gem "inherited_resources", "~> 1.3.0" gem "inherited_resources", "~> 1.3.0" в ваш Gemfile и bundle install этого ребенка.)

В этом случае, однако, это не совсем бесплатно, так как мы должны сделать некоторую настройку, чтобы справиться с нашими «особыми» обстоятельствами.
Эти обстоятельства связаны главным образом с тем, что мы используем MongoDB и тот факт, что случаи встраиваются в иерархию документов (пользователь ==> события ==> случаи). Если вы попытаетесь сделать что-то вроде Occasion.where(:event_id => @event.id) или что-то еще, вы получите следующую ошибку, которая пугает вас до чертиков, когда вы впервые видите это:

 Mongoid::Errors::InvalidCollection: Access to the collection for Occasion is not allowed since it is an embedded document, please access a collection from the root document. 

Когда вы успокаиваетесь, вы понимаете, что это имеет смысл. Поскольку мы используем базу данных документов, события встроены в события, а события — в пользователей. Таким образом, вместо того, чтобы использовать обычные методы класса ActiveModel для доступа к коллекциям, вы должны пройтись по иерархии документов. Нам нужен пользователь ( current_user , который мы уже используем для определения событий) и событие. Где мы берем событие?

У параметров маршрута есть запись :event_id поэтому, если бы мы делали это сами, мы бы взяли это и :event_id коллекцию current_user.events . Это довольно распространенный сценарий, и драгоценность унаследованных ресурсов безумно умна в распространенных сценариях. Давайте посмотрим на эту конфигурацию в файле app / controllers / occasions contoller.rb:

 belongs_to :event actions :all, :except => [:show, :index] def begin_of_association_chain current_user end 

Но ждать! Есть еще кое-что!! Вы видите этот метод действия там? Это сообщает втекает_ресурсам, какие действия мы хотим (или не хотим, в данном случае) для нашего контроллера. Случаи будут видны только через Событие, поэтому нет смысла создавать действия show и index (мы передумаем, когда перейдем к Loccasions API) прямо сейчас. По-настоящему проницательный среди вас сейчас задается вопросом «Но как насчет перенаправлений?», И это отличный вопрос. Распространенная идиома для контроллеров Rails RESTful — это перенаправление на индекс или показ страницы после создания ресурса. Опять же, мы не собираемся делать это здесь, мы хотим перейти к events#show action. Драгоценный камень наследованный_ресурс имеет функцию «Умные перенаправления», которая (со страницы github 🙂

Перенаправления в действиях создания и обновления вычисляются в следующем порядке: URL ресурса , коллекция URL, родительский URL (который мы увидим позже), корневой URL. Переадресация при разрушении действия рассчитывается в следующем порядке: сбор URL, родительский URL, root_url.

Другими словами, он выясняет, чего мы хотим. Я визжал, как маленькая девочка, когда нашел эту особенность. (Честно говоря, я много визжаю.)

Довольно просто, и мы сократили объем кода, который нам нужно написать. Случаи могут быть добавлены к событию. Я написал спецификации / accept / add_occasions_spec.rb и delete_occasions_spec.rb . В настоящее время я не буду беспокоиться об обновлении, потому что у меня проблемы с просмотром варианта использования. Я уверен, что мы вернемся к обновлению позже, но сейчас я хочу попасть на карту.

Обновление: Alert Reader Николас Генри отмечает в комментариях ниже, что вам нужно:

  • Внесите изменения в events / show.html.haml с помощью github формы Occasion.
  • Добавить случаи / _occasion.html.haml github
  • Добавить маршрут для случаев GitHub

Loccasions.map do {| its | о времени()}

Ну, почти … карта будет следующим постом.