Slide 1

Slide 1 text

Web & Rails 2016-08-12 ΫοΫύουαϚʔΠϯλʔϯ 2016 ٕज़෦ ։ൃج൫ @moro ॾڮګհ

Slide 2

Slide 2 text

Resources

Slide 3

Slide 3 text

IUUQTXXXSBJMTUVUPSJBMPSH

Slide 4

Slide 4 text

Slide 5

Slide 5 text

about Rails

Slide 6

Slide 6 text

‣ Railsͱ͸ɺRubyϓϩάϥϛϯάݴޠͰॻ͔ΕͨWebΞϓϦ έʔγϣϯϑϨʔϜϫʔΫͰ͢ɻRails͸ɺ͋ΒΏΔ։ൃऀ ͕WebΞϓϦέʔγϣϯͷ։ൃΛ࢝ΊΔ͏͑ͰඞཁͱͳΔ ࡞ۀ΍ϦιʔεΛࣄલʹԾఆͯ͠४උ͓ͯ͘͜͠ͱͰɺ WebΞϓϦέʔγϣϯΛΑΓ؆୯ʹϓϩάϥϛϯάͰ͖Δ Α͏ʹઃܭ͞Ε͍ͯ·͢ɻ ‣ -- http://railsguides.jp/getting_started.html

Slide 7

Slide 7 text

‣ Railsͱ͸ɺRubyϓϩάϥϛϯάݴޠͰॻ͔ΕͨWebΞϓϦ έʔγϣϯϑϨʔϜϫʔΫͰ͢ɻRails͸ɺ͋ΒΏΔ։ൃऀ ͕WebΞϓϦέʔγϣϯͷ։ൃΛ࢝ΊΔ͏͑ͰඞཁͱͳΔ ࡞ۀ΍ϦιʔεΛࣄલʹԾఆͯ͠४උ͓ͯ͘͜͠ͱͰɺ WebΞϓϦέʔγϣϯΛΑΓ؆୯ʹϓϩάϥϛϯάͰ͖Δ Α͏ʹઃܭ͞Ε͍ͯ·͢ɻ ‣ -- http://railsguides.jp/getting_started.html

Slide 8

Slide 8 text

WebΞϓϦέʔγϣϯͱ HTTP

Slide 9

Slide 9 text

‣ Hypertext Transfer Protocol ‣ ෼ࢄίϯϐϡʔλؒͰHTMLͳͲͷ৘ใΛ΍ΓͱΓ͢ΔͨΊͷϓϩτίϧɻ ‣ ΫϥΠΞϯτ(ϒϥ΢βɺεϚϗΞϓϦɺผͷαʔόΞϓϦͳͲ)͕ɺ αʔό্ͷʮԿ͔ͷ৘ใʯΛʮͲ͏ͯ͠΄͍͠ʯͱ ཁٻ(ϦΫΤετ)͠ɺαʔό͕Ԡ౴(Ϩεϙϯε)͢ Δɻ )551

Slide 10

Slide 10 text

‣ Կ͔ͷ৘ใ: ύεͱΫΤϦ ‣ Ͳ͏ͯ͠΄͍͠: HTTPϝιου ʮԿ͔ͷ৘ใʯΛʮͲ͏ͯ͠΄͍͠ʯ

Slide 11

Slide 11 text

$ telnet cookpad.com 80 Trying 54.178.224.205... Connected to cookpad.com. Escape character is '^]'. (下記から) GET / HTTP/1.1 Host: cookpad.com (空行) (すごいことになる) )551Λ஻ͬͯΈΑ͏

Slide 12

Slide 12 text

‣ GET : Ͳ͏͢Δ ‣ ʮHTTPϝιουʯ ‣ GET=औಘɺPOST=࡞੒ɺPATCH=(Ұ෦)ஔ͖׵͑)ɺDELETE=࡟আ ‣ / : ͳʹΛ ‣ ͜ͷ৔߹ɺhttp://cookpad.com ͷτοϓϖʔδ (&5

Slide 13

Slide 13 text

‣ HTTP/1.1 ‣ ௨৴͢ΔHTTPͷόʔδϣϯΛද͢ɻ޿͘࢖ΘΕ͍ͯΔͷ͸1.1 ‣ ݱ୅తͳ໰୊Λղܾ͢ΔͨΊHTTP/2΋ग़͖ͯͨ ‣ GET / HTTP/1.1 ͷߦΛʮϦΫΤετϥΠϯʯͱݺͿ ‣ Host: cookpad.com ‣ 1αʔόͰෳ਺αʔϏεΛӡ༻Ͱ͖ΔͨΊɺ࿦ཧతʹͲͷαʔϏε΁ͷϦΫΤετͰ͋Δ͔Λࣔ͢ඞཁ͕ ͋Δ ‣ ۭߦ ‣ ϦΫΤετ͕ऴΘͬͨ΋ͷͱͯ͠αʔόͷॲཧ͕࢝·Δ ͦͷଞͷՕॴ

Slide 14

Slide 14 text

‣ HTTPͰϦΫΤετ͏͚ɺαʔό্ͰͳΜΒ͔ͷॲཧ Λߦ͍ɺϨεϙϯεΛฦ͢ ‣ τοϓϖʔδ(/)͕΄͍͠(GET)ͱ͍͏ϦΫΤετʹͨ ͍ͯ͠ɺτοϓϖʔδͷHTMLΛੜ੒ͯ͠Ϩεϙϯε ͢Δͷ͕WebΞϓϦέʔγϣϯ 8FCΞϓϦέʔγϣϯ

Slide 15

Slide 15 text

‣ Railsͱ͸ɺRubyϓϩάϥϛϯάݴޠͰॻ͔ΕͨWebΞϓϦ έʔγϣϯϑϨʔϜϫʔΫͰ͢ɻRails͸ɺ͋ΒΏΔ։ൃऀ ͕WebΞϓϦέʔγϣϯͷ։ൃΛ࢝ΊΔ͏͑ͰඞཁͱͳΔ ࡞ۀ΍ϦιʔεΛࣄલʹԾఆͯ͠४උ͓ͯ͘͜͠ͱͰɺ WebΞϓϦέʔγϣϯΛΑΓ؆୯ʹϓϩάϥϛϯάͰ͖Δ Α͏ʹઃܭ͞Ε͍ͯ·͢ɻ ‣ -- http://railsguides.jp/getting_started.html

Slide 16

Slide 16 text

ඞཁͳϞϊΛԾఆͯ͠४උ
 ϑϧελοΫϑϨʔϜϫʔΫ

Slide 17

Slide 17 text

‣ Railsʹ͸ҰൠతͳWebΞϓϦέʔγϣϯʹඞཁͳػೳ͕
 Ұἧ͑ೖ͍ͬͯΔɻ ‣ HTTPϦΫΤετ͔ΒRubyͷॲཧΛݺͼग़͢͠ɺΫϥΠΞϯτʹ݁ՌΛ
 ฦͨ͢Ίͷ࢓૊Έ ‣ DBͷσʔλΛಡΈॻ͖͢Δ࢓૊Έ ‣ ॲཧ݁ՌΛHTML(ϒϥ΢β޲͚)΍JSON(ଞͷϓϩάϥϜ޲͚)Ͱ
 දݱ͢Δ࢓૊Έ ඞཁͳϞϊΛԾఆͯ͠४උϑϧελοΫ

Slide 18

Slide 18 text

‣ RailsΛ࢖ͬͯ؆୯ͳWebΞϓϦέʔγϣϯΛ࡞Δɻ ‣ ͦΕΛ௨ͯ͡ɺWebΞϓϦέʔγϣϯʹඞཁͳཁૉ ٕज़Λ֓؍͢Δɻ ࠓ೔ͷΰʔϧ

Slide 19

Slide 19 text

Controller routing View Model request response DBMS

Slide 20

Slide 20 text

Controller routing View Model request response DBMS ‣ Model: Ϟσϧ ‣ ΞϓϦέʔγϣϯͷॲཧͷຊମ ‣ σʔλΛಡΈॻ͖͠ɺॲཧΛ࣮ߦ͢Δ ‣ View: Ϗϡʔ ‣ Ϟσϧͷॲཧ݁ՌΛɺϢʔβΤʔδΣϯτ޲͚ʹඳը͢Δ ‣ Controller: ίϯτϩʔϥ ‣ ϦΫΤετΛड͚෇͚ͯΞΫγϣϯΛݺͼग़͢(ϧʔςΟϯά) ‣ ϞσϧͷॲཧΛݺͼग़͢ ‣ ద੾ͳϏϡʔΛϨϯμϦϯά͢Δ

Slide 21

Slide 21 text

Controller routing View Model request response DBMS ‣ ϑϨʔϜϫʔΫ: ͦΕҎ֎ͷશ෦ ‣ HTTPϦΫΤετΛղऍ͠ɺRubyΦϒδΣΫτʹͨ͠͏͑ͰϧʔςΟϯάॲཧΛݺͼग़͢ ‣ ϧʔςΟϯά͕ղܾͨ͠ΞΫγϣϯΛ࣋ͭίϯτϩʔϥΠϯελϯεΛॳظԽ͢Δ ‣ DB΁ͷ઀ଓΛཱ֬͠ɺదٓϓʔϧ͢Δ ‣ DB಺ͷϨίʔυΛRubyΦϒδΣΫτʹϚοϐϯά͠ɺಡΈॻ͖͠΍͍͢ΠϯλʔϑΣʔεΛఏڙ͢Δ ‣ ֎෦DSLͰॻ͔ΕͨϏϡʔΛςϯϓϨʔτΤϯδϯͰॲཧͰ͖ΔΑ͏ʹڮ౉͢ ‣ ϨϯμϦϯά͞Εͨॲཧ݁ՌΛHTTPϨεϙϯεͱͯ͠ϢʔβΤʔδΣϯτʹฦ͢ ‣ جຊతͳηΩϡϦςΟػߏΛఏڙ͢Δ (CSRFɺXSSɺSQLi) ‣ ....

Slide 22

Slide 22 text

Controller routing View Model request response DBMS

Slide 23

Slide 23 text

͓୊: ECαΠτΛ࡞Ζ͏

Slide 24

Slide 24 text

‣ ؆୯ͳRailsΞϓϦέʔγϣϯΛ࡞Δ ‣ ͦͷաఔͰWebΞϓϦʹඞཁͳॾ֓೦ʹ৮ΕΔ ‣ REST ‣ RDBMSઃܭ ‣ JSON APIԽ ‣ (ATDD) ͜ͷηογϣϯͰ΍Δ͜ͱ

Slide 25

Slide 25 text

‣ ඇಉظॲཧɺόονॲཧ ‣ JavaScript͘Θ͘͠ ‣ CSS͘Θ͘͠ ‣ RDBMSҎ֎ͷσʔλετΞ ‣ WebΞϓϦΛಈ͔͢Πϯϑϥ
 (Ϋϥ΢υɺεέʔϧΞ΢τɺίϯςφԽ) ‣ heroku ࢖͏͚Ͳਂ͘͸ผ్ ͜ͷηογϣϯͰ΍Βͳ͍͜ͱ ཁࣗशPS͋ͱͰ࣭໰ͯ͠

Slide 26

Slide 26 text

‣ ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ঎඼Λ஫จͰ͖Δ &$αΠτΛ࡞Ζ͏

Slide 27

Slide 27 text

঎඼ͷҰཡΛ
 දࣔͰ͖Δ

Slide 28

Slide 28 text

$ mkdir ~/work $ cd ~/work $ gem install rails $ rails new --skip-spring --skip-turbolinks --skip-test --database postgresql $ cd $ ls -F Gemfile Gemfile.lock README.md Rakefile app/ bin/ config/ config.ru db/ lib/ log/ public/ tmp/ vendor/ $ git init $ git add . $ git commit -m 'rails new ...' 3BJMTΞϓϦҰ൪͸͡Ί

Slide 29

Slide 29 text

$ vim Gemfile ``` --- a/Gemfile +++ b/Gemfile @@ -41,3 +41,7 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +group :test, :development do + gem 'rspec-rails', '>= 3.5.1' + gem 'capybara' + gem 'factory_girl_rails' +end ``` $ bundle install "5%% "DDFQUBODF5FTU%SJWFO%FWFMPQNFOU ͷ४උ

Slide 30

Slide 30 text

--- a/Gemfile +++ b/Gemfile @@ -45,5 +45,7 @@ end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] group :test do + gem 'capybara' + gem 'factory_girl_rails' gem 'rspec-rails', '>= 3.5.0' end "5%% "DDFQUBODF5FTU%SJWFO%FWFMPQNFOU ͷ४උ ‣ 3BJMTฤͰ΋5%%Λ͢Δɻͨͩ͠ɺτοϓμ΢ϯͷ5%% "5%% ‣ GFBUVSFTQFDΛॻͨ͘Ίʹ$BQZCBSB ‣ ςετσʔληοτΞοϓΛॻ͖΍͘͢͢ΔͨΊʹGBDUPSZ@HJSMΛೖΕΔ

Slide 31

Slide 31 text

$ rails generate rspec:install create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb 4FUVQ34QFD

Slide 32

Slide 32 text

$ vim Gemfile ``` --- a/Gemfile +++ b/Gemfile @@ -28,6 +28,8 @@ gem 'jbuilder', '~> 2.5' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'haml-rails' + group :development, :test do ``` $ bundle install *OTUBMM)BNM

Slide 33

Slide 33 text

$ rails generate haml:application_layout convert $ git rm app/views/layouts/application.html.erb $ git add app/views/layouts/application.html.haml $ git diff --cached ``` ... 略... diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 0000000..da94b80 --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,10 @@ +!!! +%html + %head + %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ + %title Utteiku + = csrf_meta_tags + = stylesheet_link_tag 'application', media: 'all' + = javascript_include_tag 'application' + %body + = yield ``` &3#ͱ)BNMͷҧ͍

Slide 34

Slide 34 text

Slide 35

Slide 35 text

--- /dev/null +++ b/spec/features/show_items_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +RSpec.feature "See shop items", type: :feature do + scenario 'A user can see vairous items' do + visit '/items' + + expect(page).to have_text('包丁') + expect(page).to have_text('フライパン') + end +end ঎඼ΛҰཡ͢Δ࠷௿ݶͷεςοϓ

Slide 36

Slide 36 text

http://localhost:3000/items -แஸ
 Α͘੾ΕΔแஸͰ͢ɻ1000ԁ -ϑϥΠύϯ
 Α͘ম͚ΔϑϥΠύϯͰ͢ɻ1000ԁ

Slide 37

Slide 37 text

$ rails db:create $ bundle exec rspec ./spec/features/show_items_spec.rb F Failures: 1) See shop items A user can see vairous items Failure/Error: visit '/items' ActionController::RoutingError: No route matches [GET] "/items" # ./spec/features/show_items_spec.rb:5:in `block (2 levels) in < ࣦഊ͢Δ

Slide 38

Slide 38 text

$ rails g scaffold item \ name:string description:string price:integer image_url:string invoke active_record create db/migrate/20160721092822_create_items.rb create app/models/item.rb invoke scaffold_controller ... δΣωϨʔλΛ׆༻͢Δ ‣ TDB⒎PMEδΣωϨʔλͰɺΞϓϦέʔγϣϯͷʮ଍৔ʯΛ࡞Δ ‣ OBNF EFTDSJQUJPO QSJDF JNBHF@VSMΛ࣋ͬͨJUFNͷ$36%

Slide 39

Slide 39 text

$ bundle exec rails db:migrate $ bundle exec rspec spec/features F Failures: 1) See shop items A user can see vairous items Failure/Error: expect(page).to have_text('包丁') expected to find text "包丁" in "Listing items Name Price Image url New I # ./spec/features/manage_items_spec.rb:7:in `block (2 levels) in

Slide 40

Slide 40 text

--- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,8 @@ # users commonly want. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +require 'factory_girl' + RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest @@ -96,4 +98,5 @@ RSpec.configure do |config| # as the one that triggered the failure. Kernel.srand config.seed =end + config.include FactoryGirl::Syntax::Methods end GBDUPSZ@HJSMͷಋೖ

Slide 41

Slide 41 text

--- /dev/null +++ b/spec/factories/item.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :item do + description { "素敵な#{name}です。" } + price 1_000 + image_url { "http://example.com/images/item/#{id}.png" } + end +end + --- a/spec/features/show_items_spec.rb +++ b/spec/features/show_items_spec.rb @@ -2,6 +2,9 @@ require 'rails_helper' RSpec.feature "See shop items", type: :feature do scenario 'A user can see vairous items' do + create(:item, name: '包丁') + create(:item, name: 'フライパン') + visit '/items' expect(page).to have_text('包丁') GHͰςετσʔλΛ࡞Δ

Slide 42

Slide 42 text

$ bundle exec rspec spec/features . Finished in 0.21943 seconds (files took 2.72 seconds to load) 1 example, 0 failures $ bundle exec rails s # 別ターミナルで $ open http://localhost:3000/items ࠷ॳͷςετ͕௨ͬͨ ‣ Ͳ͏ͳ͍ͬͯΔ͔ɺϒϥ΢βͰݟͯΈ·͠ΐ͏

Slide 43

Slide 43 text

Slide 44

Slide 44 text

scaffoldͷԼͰ
 ͳʹ͕ى͍ͬͯ͜Δ͔

Slide 45

Slide 45 text

--- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,4 @@ Rails.application.routes.draw do + resources :items # For details on the DSL available within this file, see http://guides.rubyon routing.html end 3BJMT͸3&45ͱ͍͏ߟ͑ํʹ΋ͱ͍͍ͮͯΔ ‣ 3&QSFTFOUBUJPOBM4UBUF5SBOTGFS ‣ )551ϝιου Ͳ͏͍ͨ͠ ͱύεΫΤϦ ͳʹΛ Λͱ͍͏૊Έ߹ΘͤΛɺ
 Ϧιʔε΁ͷૢ࡞ɺͱͯ͠ந৅Խ͠ɺΞʔΩςΫνϟͷத৺ʹஔ͘ߟ͑ํ ‣ ͦͷϦιʔε΁ͷૢ࡞Λ΋ͬͯɺΞϓϦέʔγϣϯͷॲཧΛ࣮ݱ͢Δɻ ‣ ࠓճ͸ɺѻ͏঎඼σʔλJUFNΛɺͦͷ··Ϧιʔεͱͨ͠ɻ

Slide 46

Slide 46 text

$ bundle exec rails routes
 Prefix Verb URI Pattern Controller#Action items GET /items(.:format) items#index POST /items(.:format) items#create new_item GET /items/new(.:format) items#new edit_item GET /items/:id/edit(.:format) items#edit item GET /items/:id(.:format) items#show PATCH /items/:id(.:format) items#update PUT /items/:id(.:format) items#update DELETE /items/:id(.:format) items#destroy SFTPVSDFTJUFNTͰఆٛ͞ΕΔૢ࡞ ‣ ෳ਺औಘɺ୯਺औಘɺ࡞੒ɺߋ৽ɺ࡟আ ‣ ࡞੒ɺߋ৽ͷͨΊͷϑΥʔϜΛؚΉը໘΋Ϧιʔεͱߟ͑ΒΕΔ ‣ JUFNT͕঎඼ JUFN ͷू߹Λද͠ɺͦͷू߹Λऔಘ (&5 ͢Δ͜ͱΛ΋ͬͯɺ ʮϒϥ΢β্ʹ঎඼Ұཡͷදࣔʯͱ͍͏ΞϓϦέʔγϣϯͷॲཧΛදݱͨ͠ɻ

Slide 47

Slide 47 text

‣ POST /groups/42/add_member?person_id=4 ‣ άϧʔϓͷ add_member ॲཧΛݺͼग़͢ ‣ POST /groups/42/memberships?person_id=4 ‣ "άϧʔϓʹࢀՃ͍ͯ͠Δ͜ͱ"(membership)Λ
 ࡞੒͢Δ ༨ஊ Ϧιʔε͸Ϟϊ͚ͩͰͳ͍

Slide 48

Slide 48 text

+++ b/app/controllers/items_controller.rb @@ -0,0 +1,74 @@ +class ItemsController < ApplicationController + before_action :set_item, only: [:show, :edit, :update, :destroy] + + # GET /items + # GET /items.json + def index + @items = Item.all + end + *UFNT$POUSPMMFSJOEFY ‣ ϧʔςΟϯά͔Βݺ͹ΕΔͱ͖͸AJUFNTJOEFYAදه ‣ *UFN͸3BJMTඪ४ͷ03.ɺ"DUJWF3FDPSE#BTFΛܧঝͨ͠Ϋϥεɻ ‣ *UFNBMMͰɺJUFNTςʔϒϧͷશϨίʔυΛಡΈࠐΈɺ!JUFNTʹೖΕ Δ

Slide 49

Slide 49 text

‣ ϑϨʔϜϫʔΫͷಈ͖Λ͢΂ͯઃఆ͢Δ(configuration)͢ΔͷͰͳ͘ɺ
 Rails͕૝ఆ͢Δن໿(convention)ʹԊͬͯಈ͘ɻ ‣ ARΫϥεItem͸ items ςʔϒϧͷϨίʔυΛѻ͏ ‣ GET /items͸ ItemsController#index ΛݺͼɺΞΫγϣϯॲཧޙ͸ app/views/ items/index.html.* ͷςϯϓϨʔτΛϨϯμϦϯά͢Δ ‣ ม਺໊ͰܕΛදݱ͢ΔจԽ΋͋Δɻ ‣ Railsʹ׳ΕΔͱitems͸ෳ਺ͷItemΛ͋ΒΘ͢ม਺໊ͱಡΈऔΔɻ $POWFOUJPOPWFS$POpHVSBUJPO

Slide 50

Slide 50 text

‣ 'New Item'ϦϯΫΛͨͲͬͯɺ͋ͨΒ͍͠ Item Λ
 ௥Ճ͠ͳ͍͞ ‣ 2ͭҎ্ͷItemΛ௥Ճ͠ͳ͍͞ ‣ image_url͸ۭͰΑ͍ ‣ indexͷςϯϓϨʔτΛฤूͨ͠͏͑Ͱϒϥ΢βΛϦϩʔυ ͠ɺද͕ࣔมΘΔ͜ͱΛ֬ೝ͠ͳ͍͞ ࣮श෼

Slide 51

Slide 51 text

%h1 Listing items %ul.items - @items.each do |item| %li.item %h2= link_to item.name, item %p= item.description = link_to 'New Item', new_item_path JOEFYIUNMIBNMͷॻ͖׵͑ ྫ

Slide 52

Slide 52 text

θϩσϓϩΠ

Slide 53

Slide 53 text

‣ https://www.heroku.com/about ‣ Heroku is a cloud application platform – a new way of building and deploying web apps. ‣ RailsΞϓϦέʔγϣϯͷίʔυΛૹΔͱɺࣗಈతʹDBͳͲ Λ༻ҙ͠ɺαʔϏεΛىಈͯ͘͠ΕΔɻ ‣ ίϯςφԽٕज़ͷҰछ ‣ ΫοΫύουͰ΋Docker + Amazon ECSͰ੔උ͍ͯ͠Δ IFSPLVʹ͍ͭͯ

Slide 54

Slide 54 text

$ heroku create $ git push heroku master $ heroku run rails db:migrate $ heroku open (のあとに、/items に移動) IFSPLVʹσϓϩΠ͢Δ ‣ σϓϩΠ͸೉͍͠ ‣ Ͱ͖Δ͚ͩૣ͍ஈ֊Ͱಈ͔࢝͠ΊΔ͜ͱ͕؊৺

Slide 55

Slide 55 text

‣ ࠓճͷείʔϓ(։ൃର৅ൣғ)Ͱ͸ɺ঎඼ࣗମͷ
 ௥Ճ࡟আ͸ؚ·Εͳ͍ɻ
 ͦ͏͍ͬͨෆཁίʔυΛফ͍ͯ͘͠ɻ TDBGGPMEੜ੒෺ͷΫϦʔϯΞοϓ

Slide 56

Slide 56 text

--- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,3 @@ Rails.application.routes.draw do - resources :items - # For details on the DSL available within this file, see http://guides.rubyo routing.html + resources :items, only: %i(index show) end ## 確認 $ bundle exec rails routes Prefix Verb URI Pattern Controller#Action items GET /items(.:format) items#index item GET /items/:id(.:format) items#show DPOpHSPVUFTSC

Slide 57

Slide 57 text

$ git rm \
 app/views/items/{_form.html.haml,edit.html.haml,new.html.haml} rm 'app/views/items/_form.html.haml' rm 'app/views/items/edit.html.haml' rm 'app/views/items/new.html.haml' ෆཁͳϑΝΠϧ΋࡟আ͢Δ

Slide 58

Slide 58 text

# 削除後 class ItemsController < ApplicationController before_action :set_item, only: [:show] # GET /items # GET /items.json def index @items = Item.all end # GET /items/1 # GET /items/1.json def show end private # Use callbacks to share common setup or constraints between actions. def set_item @item = Item.find(params[:id]) end end ίϯτϩʔϥͰ΋TIPXͱJOEFYҎ֎Λফ͢

Slide 59

Slide 59 text

--- a/app/views/items/index.html.haml +++ b/app/views/items/index.html.haml
 
 %h1 Listing items %ul.items - @items.each do |item| %li.item %h2= link_to item.name, item %p= item.description - - = link_to 'New Item', new_item_path ࡞੒ΞΫγϣϯ΁ͷಋઢ΋ফ͢

Slide 60

Slide 60 text

--- a/app/views/items/show.html.haml +++ b/app/views/items/show.html.haml @@ -4,9 +4,7 @@ %b Name: = @item.name %p - %b Image url: - = @item.image_url - -= link_to 'Edit', edit_item_path(@item) -\| -= link_to 'Back', items_path + = image_tag(@item.image_url) TIPXIUNMIBNM΋੔ཧ͢Δ ྫ

Slide 61

Slide 61 text

--- a/spec/features/show_items_spec.rb +++ b/spec/features/show_items_spec.rb @@ -5,7 +5,7 @@ RSpec.feature "See shop items", type: :feature do create(:item, name: '包丁') create(:item, name: 'フライパン') - visit '/items' + visit items_path expect(page).to have_text('包丁') expect(page).to have_text('フライパン') ςετίʔυ΋ϦϑΝΫλϦϯά͢Δ

Slide 62

Slide 62 text

‣ RailsΞϓϦέʔγϣϯͷجૅΛͭͬͨ͘ɻ ‣ feature specΛॻ͍ͯWebΞϓϦͰ΋TDDͨ͠ɻ ‣ scaffoldδΣωϨʔλͰ঎඼ͷCRUDΛͭͬͨ͘ɻ ‣ scaffoldͷ಺༰Λཧղ͠ɺෆཁͳίʔυΛ࡟আͨ͠ɻ ΍ͬͨ͜ͱ

Slide 63

Slide 63 text

‣ RailsΛ࢖͍͸͡Ίͨ ‣ ͍ΖΜͳgem͕͋Δ ‣ ATDD ‣ Convention over Configuration ‣ ʮδΣωϨʔλʯΛ࢖ͬͯ࠷ॳͷҰาΛૉૣ͘า͖ग़ͨ͠ ‣ herokuΛར༻ͨ͠θϩσϓϩΠ ͜ͷεςοϓͰֶΜͩ͜ͱ

Slide 64

Slide 64 text

‣ [done] ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ঎඼Λ஫จͰ͖Δ &$αΠτΛ࡞Ζ͏

Slide 65

Slide 65 text

Φεεϝ঎඼ͷ
 ҰཡΛදࣔͰ͖Δ

Slide 66

Slide 66 text

‣ ঎඼͕Φεεϝ͔Ͳ͏͔ΛͲͷΑ͏ʹ൑ผ͢Δ͔? ‣ recommended ͱ͍͏ϑϥάΛ௥Ճ͠Α͏ ‣ දࣔ͢ΔURL͸Ͳ͏͢Δ͔(Ͳ͏͍ͬͨϦιʔεͱߟ͑Δ͔) ‣ /items/recommended ʹԾஔ͖͠ɺrecommended_items_path Ͱ
 ࢀরͰ͖ΔΑ͏ʹ͢Δ ઃܭ͢Δ

Slide 67

Slide 67 text

‣ طଘͷςετΛࢀߟʹɺςετγφϦΦΛ௥Ճ͠ͳ͍͞ ‣ ςετσʔλ͸ʮแஸʯΛΦεεϝର৅ʹ͢Δ͜ͱ ‣ Φεεϝ͔Ͳ͏͔Λ੍ޚ͢ΔͨΊ recommended ΧϥϜ (boolean) ͕͋Δͱߟ͑Δ͜ͱ ‣ දࣔ͢ΔURL͸recommended_items_path ϝιουͰಋग़ ͢Δ͜ͱ ࣮श෼

Slide 68

Slide 68 text

--- a/spec/features/show_items_spec.rb +++ b/spec/features/show_items_spec.rb @@ -10,5 +10,15 @@ RSpec.feature "See shop items", type: :feature do expect(page).to have_text('包丁') expect(page).to have_text('フライパン') end + + scenario 'A user can see recommended items' do + create(:item, name: '包丁', recommended: true) + create(:item, name: 'フライパン') + + visit recommended_items_path + + expect(page).to have_text('包丁') + expect(page).not_to have_text('フライパン') + end end ղ౴ྫ

Slide 69

Slide 69 text

$ bundle exec rspec .F Failures: 1) See shop items A user can see recommended items Failure/Error: create(:item, name: '包丁', recommended: true) NoMethodError: undefined method `recommended=' for # # ./spec/features/manage_items_spec.rb:15:in `block (2 levels) SFDPNNFOEFEଐੑ͕ͳͯ͘ςετʹࣦഊ͢Δ

Slide 70

Slide 70 text

$ bundle exec rails g migration add_recommended_to_items recommended:boolean $ vim db/migrate/20160704015612_add_recommended_to_items.rb ``` --- a/db/migrate/20160704015612_add_recommended_to_items.rb +++ b/db/migrate/20160704015612_add_recommended_to_items.rb @@ -1,5 +1,5 @@ class AddRecommendedToItems < ActiveRecord::Migration[5.0] def change - add_column :items, :recommended, :boolean + add_column :items, :recommended, :boolean, null: false, default: false end end ``` $ bundle exec rails db:migrate *UFNͷଐੑΛ௥Ճ͢Δ ‣ ͸͖ͬΓͨ͠ཧ༝͕ͳ͍ݶΓɺOVMM͸ڐ༰͠ͳ͍΄͏͕Α͍Ͱ͢ɻ

Slide 71

Slide 71 text

$ bundle exec rspec .F Failures: 1) See shop items A user can see recommended items Failure/Error: visit recommended_items_path NameError: undefined local variable or method `recommended_items_path' for # # ./spec/features/manage_items_spec.rb:18:in `block (2 levels) in

Slide 72

Slide 72 text

‣ ͓͢͢Ί঎඼ͷҰཡ ‣ recommended ϑϥά͕trueͷItemͷҰཡ ‣ (recommendedϑϥά͕trueͷ)ItemͷҰཡ ‣ (৚݅ͰϑΟϧλ͞Εͨ)ϦιʔεͷίϨΫγϣϯ ‣ (৚݅ͰϑΟϧλ͞Εͨ͏͑Ͱɺ໊લΛ͚ͭΔՁ஋ͷ͋Δ)
 ϦιʔεͷίϨΫγϣϯ ϦιʔεΛઃܭ͢Δ͓͢͢Ί঎඼Ұཡͱ͸

Slide 73

Slide 73 text

$ vim config/routes.rb ``` --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,7 @@ Rails.application.routes.draw do - resources :items, only: %i(index show) + resources :items, only: %i(index show) do + collection do + get :recommended + end + end end ``` $ bundle exec rails routes | grep recommended recommended_items GET /items/recommended(.:format) items#recommended SFDPNNFOEFEJUFNT͸ίϨΫγϣϯϦιʔεͱͯ͠දݱ͢Δ

Slide 74

Slide 74 text

$ vim config/routes.rb ``` --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,7 @@ Rails.application.routes.draw do - resources :items, only: %i(index show) + resources :items, only: %i(index show) do + collection do + get :recommended + end + end end $ bundle exec rails routes | grep recommended recommended_items GET /items/recommended(.:format) items#recommended SFDPNNFOEFEJUFNT͸ίϨΫγϣϯϦιʔεͱͯ͠දݱ͢Δ ‣ 'JMUFSFETVCSFTPVSDFͰ͋Δͱղऍͯ͠DPOpHSPVUFTSCʹ௥Ճͨ͠ɻ ‣ SFDPNNFOEFE@JUFNTͱ͍͏໊લΛ͚ͭͨ ‣ (&5JUFNTSFDPNNFOEFEͱϦΫΤετ͞ΕΔ ‣ JUFNTSFDPNNFOEFEͰॲཧ͢Δ

Slide 75

Slide 75 text

‣ *UFNT$POUSPMMFSSFDPNNFOEFEΛ࣮૷͠ɺςετΛ ௨͠ͳ͍͞ ‣ ςϯϓϨʔτ͸JOEFYIUNMIBNMΛ࢖͏͜ͱ ‣ render action: :indexͰ ࣮श෼

Slide 76

Slide 76 text

--- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -12,6 +12,11 @@ class ItemsController < ApplicationController def show end + def recommended + @items = Item.where(recommended: true).all + render action: :index + end + private ղ౴ྫ

Slide 77

Slide 77 text

$ rails console irb(main):001:0> item = Item.first Item Load (0.2ms) SELECT `items`.* FROM `items` ORDER BY `items`.`id` ASC LIMIT 1 => # SBJMTDPOTPMF ‣ SBJMTDPOTPMFͰର࿩ίϯιʔϧʹೖΕΔ ‣ "3ϞσϧΦϒδΣΫτΛ௚઀ૢ࡞͠ɺ։ൃ΍σόοάʹ໾ཱͯΒΕΔ

Slide 78

Slide 78 text

‣ rails console্Ͱɺid͕࠷খͷitemͷrecommendedϑϥάΛtrueʹ͠ͳ͍͞ɻ ‣ id͕2൪໨ʹখ͍͞itemͷrecommedϑϥά͕falseͰ͋Δ͜ͱΛ֬ೝ͠ͳ͍͞ɻ ‣ app/views/items/index.html.hamlΛฤू͠ɺΦεεϝ͔Ͳ͏͔Λදࣔ͢ΔΑ͏ʹ ͠ͳ͍͞ɻ ‣ $ open http://localhost:3000/items/recommended Ͱϒϥ΢βදࣔՄೳɻ ‣ [advanced] ͸΍͘ऴΘͬͨΒ: "pry"ͱ͍͏ϥΠϒϥϦΛௐࠪ͠ɺ
 ศརͦ͏ͳΒಋೖ͠ͳ͍͞ɻ ࣮श෼

Slide 79

Slide 79 text

--- a/app/views/items/index.html.haml +++ b/app/views/items/index.html.haml @@ -3,5 +3,8 @@ %ul.items - @items.each do |item| %li.item - %h2= link_to item.name, item + %h2 + - if item.recommended? + %span.recommended オススメ + = link_to item.name, item %p= item.description ղ౴ྫ

Slide 80

Slide 80 text

‣ طଘͷRDBMSςʔϒϧʹɺΧϥϜΛ௥Ճͨ͠ ‣ RESTfulͳϦιʔεઃܭΛ࢝͠Ίͨɻ ‣ Φεεϝ঎඼ͷҰཡΛαϒϦιʔεͱͯ͠දݱ ‣ rails consoleΛ஌ͬͨ ͜ͷεςοϓͰֶΜͩ͜ͱ

Slide 81

Slide 81 text

‣ [done] ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ঎඼Λ஫จͰ͖Δ ΍Γ͍ͨ͜ͱϦετ

Slide 82

Slide 82 text

ΧςΰϦ෼͚͞Εͨ
 ঎඼ͷҰཡΛදࣔͰ͖Δ

Slide 83

Slide 83 text

‣ ڧ͍໨తΛ΋ͨͣʹ๚ΕͨϢʔβͱͯ͠ɺ
 τοϓϖʔδ͔ΒΧςΰϦʔΛͨͲΓͳ͕Β঎඼Λ୳͍ͨ͠ɻ
 ͳͥͳΒɺαΠτͰѻ͏঎඼શମΛோΊͳ͕ΒͿΒͬͱങ͍෺ͨ͠ ͍͔Βͩɻ ‣ τοϓϖʔδʹɺΧςΰϦͷҰཡΛදࣔͰ͖Δ ‣ ಛఆͷΧςΰϦʹଐ͢Δ঎඼ͷҰཡΛදࣔͰ͖Δ

Slide 84

Slide 84 text

--- /dev/null +++ b/spec/features/portal_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.feature "See shop categories", type: :feature do + scenario 'Listing categories' do + create(:category, name: '調理器具') + create(:category, name: '食器') + + visit root_path + + expect(page).to have_text('調理器具') + expect(page).to have_text('食器') + end +end τοϓϖʔδʹΧςΰϦͷҰཡΛදࣔͰ͖Δ

Slide 85

Slide 85 text

$ bundle exec rails g model category name:string image_url:string invoke active_record create db/migrate/20160722075755_create_categories.rb create app/models/category.rb $ vim db/migrate/20160722075755_create_categories.rb # nameを非nullに $ bundle exec rails db:migrate == 20160722075755 CreateCategories: migrating ======================= -- create_table(:categories) -> 0.0332s == 20160722075755 CreateCategories: migrated (0.0333s) ============== "3ϞσϧΛ࡞੒͢Δ

Slide 86

Slide 86 text

--- /dev/null +++ b/spec/factories/category.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :category do + name "雑貨" + image_url { "http://example.com/images/category/#{id}.png" } + end +end + ϑΝΫτϦΛ௥Ճ͢Δ

Slide 87

Slide 87 text

F.. Failures: 1) Show categories on root Listing categories Failure/Error: visit root_path NameError: undefined local variable or method `root_path' for # # ./spec/features/portal_spec.rb:8:in `block (2 levels) in

Slide 88

Slide 88 text

‣ config/routes.rb Ͱ root 'portal#show' ͱࢦఆ͢ΔͱΞϓϦέʔγϣϯϧʔτ('/') ΞΫγϣϯΛࢦఆͰ͖Δɻ ‣ ͜ͷ৔߹portal#show ‣ τοϓϖʔδ͸ɺখ͍͞ΞϓϦέʔγϣϯͰ͸ओཁΤϯςΟςΟ(ΫοΫύου Ͱ͍͏Ϩγϐ)ͷindexͰදݱ͞ΕΔ͜ͱ͕ଟ͍ɻ ‣ ͦͦ͜͜ͷن໛ʹͳΔͱɺʮಋઢͷ͋ͭ·ΓͰ͋Δτοϓϖʔδʯͱ͍͏Ϧιʔ εʹͳ͍ͬͯ͘ɻ ‣ ͦͷ৔߹ʮϙʔλϧϦιʔεʯͱͯ͠੾Γग़͢͜ͱ͕ଟ͍ [ཁग़య] ઃܭΛߟ͑ΔϙʔλϧϦιʔεͷಋೖ

Slide 89

Slide 89 text

--- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + root 'portal#show' + resources :items, only: %i(index show) do SPPU΁ͷϧʔςΟϯάΛఆٛ͢Δ

Slide 90

Slide 90 text

‣ ςετ͕௨ΔΑ͏ʹ࣮૷͠ͳ͍͞ ‣ app/controllers/portal_controller.rb (ͳͲ)௥Ճ ‣ app/views/portal/show.html.haml (ͳͲ)௥Ճ ‣ ҰཡͷϦετʹ͸ulΛ࢖͏͜ͱ ࣮श෼

Slide 91

Slide 91 text

‣ [advanced] 3BJMTͷόϦσʔγϣϯʹ͍ͭͯௐࠪ͠ɺ$BUFHPSZͷ OBNFͱ*UFNͷOBNFɺͦΕͧΕʹQSFTFODFόϦσʔγϣϯΛ௥ Ճ͠ͳ͍͞ ‣ [advanced]τοϓϖʔδʹɺΦεεϝ঎඼ͷҰཡ΋දࣔ͠ͳ͍͞ ‣ τοϓϖʔδͷ͓͢͢Ί঎඼Λ࠷େ݅ʹߜͬͨ͏͑Ͱɺ
 ͦΕҎ্͋Δ৔߹͸SFDPNNFOEFE@JUFNT΁ͷϦϯΫΛઃஔ͠ ͳ͍͞ ‣ σʔλ͸SBJMTDPOTPMF ࣮श෼

Slide 92

Slide 92 text

--- /dev/null +++ b/app/controllers/portal_controller.rb @@ -0,0 +1,5 @@ +class PortalController < ApplicationController + def show + @categories = Category.all + end +end ղ౴ྫ

Slide 93

Slide 93 text

--- /dev/null +++ b/app/views/portal/show.html.haml @@ -0,0 +1,3 @@ +%ul.categories + - @categories.each do |category| + %li= category.name ղ౴ྫ

Slide 94

Slide 94 text

‣ ڧ͍໨తΛ΋ͨͣʹ๚ΕͨϢʔβͱͯ͠ɺ
 τοϓϖʔδ͔ΒΧςΰϦʔΛͨͲΓͳ͕Β঎඼Λ୳͍ͨ͠ɻ
 ͳͥͳΒɺαΠτͰѻ͏঎඼શମΛோΊͳ͕ΒͿΒͬͱങ͍෺ͨ͠ ͍͔Βͩɻ ‣ τοϓϖʔδʹɺΧςΰϦͷҰཡΛදࣔͰ͖Δ ‣ ಛఆͷΧςΰϦʹଐ͢Δ঎඼ͷҰཡΛදࣔͰ͖Δ

Slide 95

Slide 95 text

--- a/spec/features/manage_items_spec.rb +++ b/spec/features/manage_items_spec.rb @@ -20,5 +20,21 @@ RSpec.feature "See shop items", type: :feature do expect(page).to have_text('包丁') expect(page).not_to have_text('フライパン') end + + scenario 'A user can see categorized items from portal', type: :feature do + kitchenware = create(:category, name: '調理器具') + kitchenware.items.create!(name: '包丁', recommended: true) + kitchenware.items.create!(name: 'フライパン') + + tableware = create(:category, name: '食器') + tableware.items.create!(name: '大皿') + + visit root_path + click_on '調理器具' + expect(page).to have_text('包丁') + expect(page).to have_text('フライパン') + + expect(page).not_to have_text('大皿') + end end τοϓϖʔδ͔ΒΧςΰϦΛͨͲͬͯݟʹདྷ͍ͯΔ༷ࢠͷड͚ೖΕςετ

Slide 96

Slide 96 text

--- a/spec/features/show_items_spec.rb +++ b/spec/features/show_items_spec.rb @@ -21,7 +21,7 @@ RSpec.feature "See shop items", type: :feature do expect(page).not_to have_text('フライパン') end - scenario 'A user can see categorized items from portal', type: :feature do + xscenario 'A user can see categorized items from portal', type: :feature do kitchenware = create(:category, name: '調理器具') kitchenware.items.create!(name: '包丁') 1SPUJQςετͷϖϯσΟϯά

Slide 97

Slide 97 text

$ bundle exec rspec spec/features .*.. Pending: (Failures listed here are expected and do not affect your su 1) See shop categories A user can see categorized items
 from portal # Temporarily disabled with xscenario # ./spec/features/portal_spec.rb:14 Finished in 0.21527 seconds (files took 2.25 seconds to load) 4 examples, 0 failures, 1 pending QFOEJOH

Slide 98

Slide 98 text

‣ ActiveRecord Ͱ͸ςʔϒϧؒ(ARΫϥεؒ)ͷؔ࿈Λ දݱͰ͖Δ ‣ ARͰͷݴ͍ճ͠͸ Association ߟ͑ΔΧςΰϦʹଐ͢Δɺͱ͸

Slide 99

Slide 99 text

‣ ʮ঎඼͸1ͷΧςΰϦʹଐ͢Δʯ ‣ Item belongs_to :category, optional: true ‣ item͕΋͍ͬͯΔcategory_idʹΑͬͯɺଐ͢Δ1ͭͷΧς ΰϦΛࢀর͢Δ ‣ ΧςΰϦΛࢀর͢ΔͨΊͷ#categoryͱ͍͏ϝιουͳͲ ͕࢖͑ΔΑ͏ʹͳΔ ‣ ΧςΰϦʹଐ͞ͳ͍͜ͱ΋Ͱ͖ΔͷͰɺoptional: trueΦϓ γϣϯΛ෇͚Δ ଟରPSରͷʮଐ͢Δʯؔ࿈ੑ

Slide 100

Slide 100 text

‣ ʮΧςΰϦ͸ɺෳ਺ͷ঎඼Λॴ༗͍ͯ͠Δʯ ‣ Category has_many :items ‣ item͕΋͍ͬͯΔcategory_idʹΑͬͯɺॴ༗͢Δn ݸͷ঎඼Λࢀর͢Δ ‣ ෳ਺঎඼Λࢀর͢ΔͨΊͷ#itemsͱ͍͏ϝιουͳ Ͳ͕࢖͑ΔΑ͏ʹͳΔ ରଟͷʮॴ༗͢Δʯؔ࿈ੑ

Slide 101

Slide 101 text

# spec/models/category_spec.rb require 'rails_helper' describe Category do describe 'has_many :item, learning test' do let(:category) { create(:category, name: '調理器具') } before do category.items.create!(name: '包丁', recommended: true) category.items.create!(name: 'フライパン') end specify { expect(category.items.map(&:name)).to match_array(['包丁', 'フライパン']) } specify { expect(category.items.first.category).to eq(category) } end end # ヒント: 下記のコマンドでマイグレーションファイルの雛形が生成できます $ bundle exec rails g migration add_category_id_to_items category_id:integer ࣮श෼ԼهͷςετΛ࡞੒͠ɺςετΛ௨͠ͳ͍͞ 3BJMT-FTTPO

Slide 102

Slide 102 text

--- /dev/null +++ b/db/migrate/20160705051719_add_category_reference_to_items.rb @@ -0,0 +1,7 @@ +class AddCategoryReferenceToItems < ActiveRecord::Migration[5.0] + def change + add_column :items, :category_id, :integer, null: true + + add_index :items, :category_id + end +end ղ౴ྫϚΠάϨʔγϣϯ

Slide 103

Slide 103 text

--- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,2 +1,3 @@ class Category < ApplicationRecord + has_many :items end --- a/app/models/item.rb +++ b/app/models/item.rb @@ -1,2 +1,3 @@ class Item < ApplicationRecord + belongs_to :category, optional: true end ղ౴ྫ֤Ϟσϧͷؔ࿈ͷఆٛ

Slide 104

Slide 104 text

‣ ͍͔ͭ͘બ୒ࢶ͕͋Δ͕ɺcategoryʹωετ͍ͤͨ͞ ‣ (͋ΔΧςΰϦʹଐ͢Δ)঎඼ͷҰཡɺͳͷͰindex ‣ /categories/:category_id/items ͱ͍͏URLʹͳΓ ‣ params[:category_id]ʹΧςΰϦID͕ೖͬͨঢ়ଶͰ items#indexΞΫγϣϯ͕࣮ߦ͞ΕΔ ߟ͑ΔΧςΰϦʹଐ͢Δ঎඼ͷϧʔςΟϯά

Slide 105

Slide 105 text

--- a/config/routes.rb +++ b/config/routes.rb @@ -6,4 +6,8 @@ Rails.application.routes.draw do get :recommended end end + + resources :categories, only: %i(index) do + resources :items, only: %w(index) + end end ωετͨ͠ϧʔςΟϯάΛఆٛ͢Δ

Slide 106

Slide 106 text

--- a/app/views/portal/show.html.haml +++ b/app/views/portal/show.html.haml @@ -1,3 +1,3 @@ %ul.categories - @categories.each do |category| - %li= category.name + %li= link_to category.name, category_items_path(category) ΧςΰϦʹଐ͢Δ঎඼΁ͷಋઢΛ࡞Δ

Slide 107

Slide 107 text

‣ Pendingͨ͠feature specΛ࣮૷͠ͳ͍͞ɻ ‣ (Rails/Lesson7) ‣ [advanced] ΧςΰϥΠζ͞Ε͍ͯͳ͍঎඼Λ GET / items/uncategorized ͰҰཡͰ͖ΔΑ͏ʹ͠ͳ͍͞ ࣮श෼

Slide 108

Slide 108 text

--- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -4,7 +4,7 @@ class ItemsController < ApplicationController # GET /items # GET /items.json def index - @items = Item.all + @items = collection_root end # GET /items/1 @@ -23,4 +23,12 @@ class ItemsController < ApplicationController @item = Item.find(params[:id]) end + def collection_root + if params[:category_id] + Category.find(params[:category_id]).items + else + Item.all + end + end + ղ౴ྫ

Slide 109

Slide 109 text

heroku΁σϓϩΠ#2 - αϯϓϧσʔλΛೖΕΑ͏

Slide 110

Slide 110 text

$ git clone [email protected]:moro/ckpd_intern.git vendor/ckpd_intern $ rm -rf vendor/ckpd_intern/.git $ vim Gemfile gem 'ckpd_intern', path: 'vendor/ckpd_intern' # これを追加 $ vim db/seed.rb --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,8 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) +require 'ckpd_intern/seed' + +ActiveRecord::Base.transaction do + CkpdIntern::Seed.create_all! +end db/seed.rb Λฤूͯ͠γʔυσʔλΛ࡞Δ

Slide 111

Slide 111 text

$ bundle exec rails db:seed (ブラウザで http://localhost:3000/items を見てみよう) $ git add vendor/ckpd_intern $ git commit -m '...' # heroku にデプロイ $ git push heroku master $ heroku run rails db:migrate $ heroku run rails db:seed db:seedΛͯ͠ΈΑ͏

Slide 112

Slide 112 text

‣ ςʔϒϧؒͷAssociationΛֶΜͩ ‣ 1ରଟͷؔ࿈ͷɺbelongs_toɺhas_many ‣ ࣗಈςετΛ࢖ͬͯϥΠϒϥϦػೳΛֶशͨ͠ɻ ‣ RESTfulͳϦιʔεઃܭͰɺ৽͍͠ύλʔϯΛֶΜͩ ‣ ωετͨ͠Ϧιʔε ‣ ϙʔλϧϦιʔε ͜ͷεςοϓͰֶΜͩ͜ͱ

Slide 113

Slide 113 text

‣ [done] ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ ঎඼Λ஫จͰ͖Δ ΍Γ͍ͨ͜ͱϦετ

Slide 114

Slide 114 text

঎඼Λ஫จͰ͖Δ

Slide 115

Slide 115 text

ߟ͑Δʮ஫จʯͷσʔλߏ଄ ඼໨ ਺ྔ ୯Ձ খܭ แஸ ು ߹ܭ ͝஫จ

Slide 116

Slide 116 text

ߟ͑Δͭͷදʹͯ͠ΈΔ ஫จ೔ ඼໨ ਺ྔ ୯Ձ খܭ แஸ ು

Slide 117

Slide 117 text

ߟ͑Δॏෳ͍ͯ͠Δ΋ͷΛ෼͚ͯΈΔ /' ඼໨ ਺ྔ ୯Ձ খܭ แஸ ು ஫จ೔

Slide 118

Slide 118 text

ߟ͑Δ෼͚ͨ΋ͷΛࢀরͰ͖ΔΑ͏ʹ /' ஫จJE ඼໨ ਺ྔ ୯Ձ খܭ แஸ ು JE ஫จ೔

Slide 119

Slide 119 text

ߟ͑Δಋग़Ͱ͖Δ΋ͷΛ෼͚ͯΈΔ /' ஫จJE ঎඼JE ਺ྔ খܭ JE ஫จ೔ JE ඼໨ ୯Ձ แஸ ು

Slide 120

Slide 120 text

είʔϓ֎ʮ஫จʯʹ഑ૹઌ΋͋Δ৔߹ ඼໨ ਺ྔ ୯Ձ খܭ แஸ ು ߹ܭ ͝஫จ ഑ૹઌܙൺणΨʔσϯϓϨΠε'NPSP༷

Slide 121

Slide 121 text

ߟ͑ΔJEҎ֎Ͱಋग़Ͱ͢΂͖΋ͷΛ෼͚Δ /' ஫จJE ঎඼JE ਺ྔ খܭ JE ސ٬JE ஫จ೔ JE ඼໨ ୯Ձ แஸ ು JE ஫จऀ ॅॴ NPSP ܙൺणΨʔσϯϓϨΠε

Slide 122

Slide 122 text

‣ ʮ׬ྃͨ͠஫จʯΛදݱ͢Δσʔλߏ଄͕Ͱ͖ͨɻ ‣ ͞Βʹɺར༻ऀ͸ɺαΠτΛݟͳ͕Β1঎඼ͣͭ
 ஫จ͢Δ͔Ͳ͏͔ܾΊ͍ͨͷͰ͸ͳ͍͔ɻ ‣ ʮ஫จத(=Χʔτ)ʯͱʮ஫จ֬ఆʯͨ͠஫จͷɺ গͳ͘ͱ΋2ͭͷঢ়ଶ͕ඞཁ ஫จͰ͖ΔΑ͏ʹ͢Δ

Slide 123

Slide 123 text

ςʔϒϧ໊ɺΧϥϜ໊ΛܾΊΔ line_items id:pk order_id
 :fk item_id
 :fk quantity
 :integer 5 100 201 2 6 100 202 1 orders id:pk ordered_at
 :timestamp status
 :enum 100 2016-08-11 ஫จ֬ఆ items id:pk name :string price
 :integer 201 แஸ 3,000 202 ು 2,500

Slide 124

Slide 124 text

$ bundle exec rails g model order status:integer ordered_at:datetime $ bundle exec rails g model line_item \ order:references item:references quantity:integer ‣ ઌ΄Ͳͷ࿦ཧઃܭΛ΋ͱʹϞσϧͱϚΠάϨʔγϣϯϑΝΠϧΛੜ੒͢Δɻ ‣ FOVNͷ࣮૷͸JOUFHFSɺGL΋࣮૷͸ϢχʔΫΠϯσοΫε੍໿෇͖ͷ JOUFHFSʹͳΔ

Slide 125

Slide 125 text

‣ τοϓ͔Βʮௐཧث۩ʯΧςΰϦͷ঎඼ΛҰཡ͢Δ ‣ ʮแஸʯͷϖʔδʹߦ͖ɺͭ஫จ͢Δ ‣ ஫จ͕֬ఆ͠ɺʮ͋Γ͕ͱ͏͍͟͝·ͨ͠ʯͱදࣔ ͞ΕΔ ஫จͷγφϦΦΛߟ͑Δ

Slide 126

Slide 126 text

# spec/features/orders_spec.rb require 'rails_helper' RSpec.feature "Order items", type: :feature do scenario 'A user can order 2 hochyo' do kitchenware = create(:category, name: '調理器具') create(:item, category: kitchenware, name: '包丁', price: 3_000) visit root_path click_on '調理器具' click_on '包丁' fill_in '数量', with: '2' click_on 'カートに追加' expect(page).to have_content('ご注文ありがとうございました') expect(page).to have_content(%r!注文日時: \d{4}/\d{2}/\d{2} \d{2}:\d{2}!) expect(page).to have_css('.line_items td', text: '包丁') end end ஫จͷγφϦΦΛߟ͑Δ

Slide 127

Slide 127 text

‣ items#show ʹɺline_items#create͢ΔͨΊͷϑΥʔϜ͕͋Δ ‣ ਺ྔΛࢦఆͰ͖ΔɻʮΧʔτʹೖΕΔʯΠϝʔδ ‣ line_items#createͰ஫จΛ௥Ճ͠ɺ஫จΛ֬ఆ͢Δɻ ‣ ஫จΛ֬ఆ͢ΔͱɺOrder#ordered_atʹͦͷ࣌ࠁ͕ηοτ͞Εɺεςʔλε͕ checked_outʹͳΓɺ஫จৄࡉ(orders#show)ʹભҠ͢Δɻ ‣ orders#showͰ͸஫จͷ͓ྱΛදࣔ͢Δɻ ࣍ͷ໨ඪΛߟ͑Δ

Slide 128

Slide 128 text

‣ items#show ʹɺline_items#create͢ΔͨΊͷϑΥʔϜ͕͋Δ ‣ ਺ྔΛࢦఆͰ͖ΔɻʮΧʔτʹೖΕΔʯΠϝʔδ ‣ line_items#createͰ஫จΛ௥Ճ͠ɺ஫จΛ֬ఆ͢Δɻ ‣ ஫จΛ֬ఆ͢ΔͱɺOrder#ordered_atʹͦͷ࣌ࠁ͕ηοτ͞Εɺεςʔλε͕ checked_outʹͳΓɺ஫จৄࡉ(orders#show)ʹભҠ͢Δɻ ‣ orders#showͰ͸஫จͷ͓ྱΛදࣔ͢Δɻ ࣍ͷ໨ඪΛߟ͑Δ

Slide 129

Slide 129 text

%p#notice= notice .item %h2= @item.name = image_tag @item.image_url, size: '400x300', alt: "#{@item.name}のイラストです %p.description= @item.description %p.price #{@item.price}円 .line_item = form_for LineItem.new(item: @item) do |f| = f.hidden_field :item_id = f.label :quantity, '数量' = f.number_field :quantity, size: 5, required: true = submit_tag 'カートに追加' = link_to 'Back', items_path ஫จϑΥʔϜͷྫ঎඼ৄࡉ JUFNTTIPX ʹߪೖϑΥʔϜΛ଍͢

Slide 130

Slide 130 text

# spec/features/orders_spec.rb require 'rails_helper' RSpec.feature "Order items", type: :feature do scenario 'A user can order 2 hochyo' do kitchenware = create(:category, name: '調理器具') create(:item, category: kitchenware, name: '包丁', price: 3_000) visit root_path click_on '調理器具' click_on '包丁' fill_in '数量', with: '2' click_on 'カートに追加' expect(page).to have_content('ご注文ありがとうございました') expect(page).to have_content(%r!注文日時: \d{4}/\d{2}/\d{2} \d{2}:\d{2}!) expect(page).to have_css('.line_items td', text: '包丁') end end ࣮श8: 20෼ ஫จॲཧΛ࣮૷͠ͳ͍͞ (Rails/Lesson8)

Slide 131

Slide 131 text

--- a/config/routes.rb +++ b/config/routes.rb @@ -10,4 +10,7 @@ Rails.application.routes.draw do resources :categories, only: %i(index) do resources :items, only: %w(index) end + + resources :line_items + resources :orders ղ౴ྫ

Slide 132

Slide 132 text

--- /dev/null +++ b/app/controllers/line_items_controller.rb @@ -0,0 +1,10 @@ +class LineItemsController < ApplicationController + def create + order = Order.create! + line_item = order.line_items.create! (params.require(:line_item).permit(:item_id, :quantity)) + order.checkout!(Time.now) + + redirect_to order + end +end ղ౴ྫ

Slide 133

Slide 133 text

--- /dev/null +++ b/app/models/order.rb +class Order < ApplicationRecord + has_many :line_items + enum status: { + cart: 0, + checked_out: 1, + } + + def checkout!(at) + update!(status: :checked_out, ordered_at: at) + end +end ղ౴ྫ

Slide 134

Slide 134 text

--- /dev/null +++ b/app/models/line_item.rb +class LineItem < ApplicationRecord + belongs_to :order + belongs_to :item + + def subtotal + item.price * quantity + end +end ղ౴ྫ

Slide 135

Slide 135 text

--- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,5 @@ +class OrdersController < ApplicationController + def show + @order = Order.find(params[:id]) + end +end ղ౴ྫ

Slide 136

Slide 136 text

--- /dev/null +++ b/app/views/orders/show.html.haml @@ -0,0 +1,24 @@ +.order + %h2 ご注文ありがとうございました + .ordered_at + 注文日時: + %span=l @order.ordered_at, format: '%Y/%m/%d %H:%M' + + - if @order.line_items.present? + %table.line_items + %thead + %tr + %th 商品 + %th 単価 + %th 数量 + %th 小計 + %tbody + - @order.line_items.order(:id).each do |li| + %tr + %td= li.item.name + %td= li.item.price + %td= li.quantity + %td= li.subtotal + - else + %p.empty 商品はありません。 + = link_to '買い物を続ける', root_path ղ౴ྫ

Slide 137

Slide 137 text

‣ τοϓ͔Βʮௐཧث۩ʯΧςΰϦͷ঎඼ΛҰཡ͢Δ ‣ ʮแஸʯͷϖʔδʹߦ͖ɺͭ஫จ͢Δ ‣ ങ͍෺Λଓ͚ɺ͜ΜͲ͸ʮುʯΛͭ஫จ͢Δ ‣ ஫จΛ֬ఆ͢Δ ‣ ஫จ͕֬ఆ͠ɺʮ͋Γ͕ͱ͏͍͟͝·ͨ͠ʯͱදࣔ ͞ΕΔ ෳ਺ͷ঎඼ΛҰؾʹങ͍͍ͨ

Slide 138

Slide 138 text

‣ items#show ʹɺline_items#create͢ΔͨΊͷϑΥʔϜ͕͋Δ ‣ ਺ྔΛࢦఆͰ͖ΔɻʮΧʔτʹೖΕΔʯΠϝʔδ ‣ line_items#createͰ஫จΛ௥Ճ͢Δ ‣ ࠷ॳͷ஫จͷ৔߹ɺOrderΛ௥Ճ͠ɺIDΛηογϣϯʹೖΕΔ ‣ cartεςʔλεͱͯ͠௥Ճ͢Δ ‣ ηογϣϯ͔Βʮcartεςʔλεʯͷ஫จΛ෮ݩ͠ɺͦ͜ʹline_itemΛ௥Ճ͢Δ ‣ ஫จΛ௥Ճͨ͋͠ͱ͸ɺorders#showʹϦμΠϨΫτ͢Δ ‣ ͦ͜Ͱɺങ͍෺Λଓ͚Δ(root_path΁ͷϦϯΫ)͔ɺ஫จΛ֬ఆ͢Δ(orders#update΁ͷϑΥʔϜ)ͷͲͪΒ ͔ʹભҠ͢Δ ‣ ஫จΛ֬ఆ͢ΔͱɺOrder#ordered_atʹͦͷ࣌ࠁ͕ηοτ͞Εɺεςʔλε͕checked_outʹͳΓɺ஫จ ৄࡉʹભҠ͢Δ ࣍ͷ໨ඪΛߟ͑Δ

Slide 139

Slide 139 text

‣ items#show ʹɺline_items#create͢ΔͨΊͷϑΥʔϜ͕͋Δ ‣ ਺ྔΛࢦఆͰ͖ΔɻʮΧʔτʹೖΕΔʯΠϝʔδ ‣ line_items#createͰ஫จΛ௥Ճ͢Δ ‣ ࠷ॳͷ஫จͷ৔߹ɺOrderΛ௥Ճ͠ɺIDΛηογϣϯʹೖΕΔ ‣ cartεςʔλεͱͯ͠௥Ճ͢Δ ‣ ηογϣϯ͔Βʮcartεςʔλεʯͷ஫จΛ෮ݩ͠ɺͦ͜ʹline_itemΛ௥Ճ͢Δ ‣ ஫จΛ௥Ճͨ͋͠ͱ͸ɺorders#showʹϦμΠϨΫτ͢Δ ‣ ͦ͜Ͱɺങ͍෺Λଓ͚Δ(root_path΁ͷϦϯΫ)͔ɺ஫จΛ֬ఆ͢Δ(orders#update΁ͷϑΥʔϜ)ͷͲͪΒ ͔ʹભҠ͢Δ ‣ ஫จΛ֬ఆ͢ΔͱɺOrder#ordered_atʹͦͷ࣌ࠁ͕ηοτ͞Εɺεςʔλε͕checked_outʹͳΓɺ஫จ ৄࡉʹભҠ͢Δ ࣍ͷ໨ඪΛߟ͑Δ

Slide 140

Slide 140 text

‣ େલఏ: HTTP͸εςʔτϨεͳϓϩτίϧͰ͋Δɻ ‣ αʔόଆʹɺΫϥΠΞϯτঢ়ଶΛอ࣋Ͱ͖ͳ͍ɻ ‣ ෳ਺ฒΜͩAPαʔόͷͲΕʹϦΫΤετ͕ߦ͔ܾ͘ఆͰ͖ͳ͍ɻ ‣ ΫϥΠΞϯτ(ϒϥ΢β)͕ৗʹૹग़͢ΔσʔλʹࣝผࢠΛ ೖΕɺαʔόଆͰΫϥΠΞϯτঢ়ଶΛอଘ͢Δ͜ͱͰ
 ʮηογϣϯΛʯදݱ͍ͯ͠Δɻ ‣ CookieΛར༻͢Δ͜ͱ͕ଟ͍ɻ ‣ WebΞϓϦϑϨʔϜϫʔΫ͕ػೳΛఏڙͯ͘͠ΕΔɻ ηογϣϯͱ͸

Slide 141

Slide 141 text

‣ ίϯτϩʔϥͷ #session ϝιουͰΞΫηεͰ͖Δ ‣ ෳ਺ϦΫΤετΛލ͍ͰσʔλΛอ࣋Ͱ͖Δ ‣ ͋·Γͨ͘͞ΜͷσʔλΛೖΕͳ͍ͷ͕Rails way ‣ ϩάΠϯϢʔβͷid΍ɺ஫จͷidͳͲ ‣ idͰDB͔ΒҾ͖௚ͯ͠࢖͏ 3BJMTͰͷηογϣϯ࣮૷

Slide 142

Slide 142 text

# セッション中に「いまのカートとなっている注文」のidがあれば # DBから検索する。なければ新しくつくってセッションに入れる def current_cart_order if session[:current_order_id] Order.where(status: :cart).find(session[:current_order_id]) else order = Order.create! session[:current_order_id] = order.id order end end ࣮૷ྫ

Slide 143

Slide 143 text

require 'rails_helper' scenario 'A user can see categorized items from portal' do kitchenware = create(:category, name: '調理器具') create(:item, category: kitchenware, name: '包丁', price: 3_000) create(:item, category: kitchenware, name: '鍋', price: 2_500) visit root_path click_on '調理器具' click_on '包丁' fill_in '数量', with: '2' click_on 'カートに追加' click_on '買い物を続ける' expect(page.current_path).to eq('/') click_on '調理器具' click_on '鍋' fill_in '数量', with: '1' click_on 'カートに追加' click_on '注文を確定する' expect(page).to have_content('ご注文ありがとうございました') expect(page).to have_content(%r!注文日時: \d{4}/\d{2}/\d{2} \d{2}:\d{2}!) expect(page).to have_css('.line_items td', text: '包丁') expect(page).to have_css('.line_items td', text: '鍋') end ࣮श9: 30෼ ԼهςετΛ௨͠ͳ͍͞ (Rails/Lesson9)

Slide 144

Slide 144 text

--- a/app/controllers/line_items_controller.rb +++ b/app/controllers/line_items_controller.rb @@ -1,10 +1,21 @@ class LineItemsController < ApplicationController def create - order = Order.create! + order = current_cart_order line_item = order.line_items.create!(params.require(:line_item).permit(:item_id, :quantity)) - order.checkout!(Time.now) redirect_to order end + + private + + def current_cart_order + if session[:current_order_id] + Order.where(status: :cart).find(session[:current_order_id]) + else + Order.create!.tap do |order| + session[:current_order_id] = order.id + end + end + end end ղ౴ྫ

Slide 145

Slide 145 text

--- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -2,4 +2,16 @@ class OrdersController < ApplicationController def show @order = Order.find(params[:id]) end + + def update + @order = Order.find(params[:id]) + if params.dig(:order, :status) == 'checked_out' + @order.checkout!(Time.now) + session.delete(:current_order_id) + + redirect_to @order + else + head(:bad_request) + end + end end ղ౴ྫ

Slide 146

Slide 146 text

--- a/app/views/orders/show.html.haml +++ b/app/views/orders/show.html.haml @@ -1,8 +1,11 @@ .order - %h2 ご注文ありがとうございました - .ordered_at - 注文日時: - %span=l @order.ordered_at, format: '%Y/%m/%d %H:%M' + - if @order.cart? + %h2 カートの中のアイテム + - elsif @order.checked_out? + %h2 ご注文ありがとうございました + .ordered_at + 注文日時: + %span=l @order.ordered_at, format: '%Y/%m/%d %H:%M' - if @order.line_items.present? %table.line_items @@ -19,6 +22,13 @@ %td= li.item.price %td= li.quantity %td= li.subtotal + + - unless @order.checked_out? + = link_to '買い物を続ける', root_path + = form_for @order do |f| + = f.hidden_field :status, value: :checked_out + = submit_tag '注文を確定する' + - else %p.empty 商品はありません。 = link_to '買い物を続ける', root_path

Slide 147

Slide 147 text

‣ RDBMSͷςʔϒϧઃܭͱਖ਼نԽΛֶΜͩɻ ‣ RailsͰσʔλΛ࡞੒(POST)͢Δํ๏ΛֶΜͩɻ ‣ HTTP্ͰηογϣϯΛදݱ͢Δํ๏ΛֶΜͩɻ ͜ͷεςοϓͰֶΜͩ͜ͱ

Slide 148

Slide 148 text

‣ [done] ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] ঎඼Λ஫จͰ͖Δ ΍Γ͍ͨ͜ͱϦετ

Slide 149

Slide 149 text

‣ [done] ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] Φεεϝ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] ΧςΰϦ෼͚͞Εͨ঎඼ͷҰཡΛදࣔͰ͖Δ ‣ [done] ঎඼Λ஫จͰ͖Δ ‣ ֤ػೳΛ+40/"1*Ͱఏڙ͢Δ ΍Γ͍ͨ͜ͱϦετ: one more thing

Slide 150

Slide 150 text

֤ػೳΛJSON APIͰ
 ఏڙ͢Δ

Slide 151

Slide 151 text

‣ RESTfulͳWebαʔϏεͷϢʔβΤʔδΣϯτ͸ɺϒϥ΢β ͚ͩͰ͸ͳ͍ ‣ εϚϗΞϓϦͱ͔ ‣ ϑΥʔϚοτ(representation)ͱ௨৴ܦ࿏΋ଟछଟ༷ɻ͍· ͸JSON over HTTPS͕೼ݖΛͱͬͨײ͡ɻ ‣ HTML͔JSON͔͸representationͷ໰୊ͳͷͰɺRailsͰ͸ ϏϡʔΛ௥Ճ͢Ε͹ྑ͍ɻ 8FC"1*ͱ͸

Slide 152

Slide 152 text

‣ curl -s http://localhost:3000/items/1.json ݱঢ়Λ֬ೝͯ͠ΈΑ͏

Slide 153

Slide 153 text

$ curl -s https://localhost:3000/items/1.json | jq . { "id": 1, "name": "料理包丁", "description": "材料を切るときに使う、料理包丁です。", "price": 1000, "image_url": "ckpd_intern/item/cooking_ryouri_houchou.png", "created_at": "2016-08-02T04:33:21.796Z", "updated_at": "2016-08-02T04:33:21.796Z", "url": "https://localhost:3000/items/1.json" } TDBGGPMEͰ͍͍ͩͨͰ͖ͯͨ

Slide 154

Slide 154 text

--- a/app/views/items/_item.json.jbuilder +++ b/app/views/items/_item.json.jbuilder @@ -1,2 +1,3 @@ -json.extract! item, :id, :name, :description, :price, \ :image_url, :created_at, :updated_at -json.url item_url(item, format: :json) \ No newline at end of file +json.extract! item, :id, :name, :description, :price, \ :created_at, :updated_at +json.image_url image_url(item.image_url) +json.url item_url(item, format: :json) TIPXKTPOKCVJMEFS͔Βͷ@JUFNKTPOKCVJMEFSΛमਖ਼

Slide 155

Slide 155 text

$ curl -s http://localhost:3000/items/1.json | jq . { "id": 1, "name": "料理包丁", "description": "材料を切るときに使う、料理包丁です。", "price": 1000, "created_at": "2016-08-02T04:28:57.000Z", "updated_at": "2016-08-02T04:28:57.000Z", "image_url": "http://localhost:3000/assets/ckpd_intern/item/ cooking_ryouri_houchou-35bc5437f2f2c2dd6f32f9d6b4248ce3272ff9998349fd .png", "url": "http://localhost:3000/items/1.json" } JNBHF@VSM΋௥ՃͰ͖ͯͦ͏

Slide 156

Slide 156 text

‣ categories#index ʹ͓͍ͯ΋ɺΧςΰϦҰཡͷJSONϨεϙϯεΛฦͤΔΑ͏ʹ ίϯτϩʔϥͱϏϡʔΛ௥Ճ͠ͳ͍͞ɻ ‣ ֤ΧςΰϦ͸ɺid, name, image_url, category_items_urlΛ࣋ͭ͜ͱ ‣ λΠϜελϯϓ͸ෆཁ: ΧςΰϦΛͭͬͨ͘λΠϜελϯϓ͸͋·Γҙຯͳ ͍? ‣ url΋ෆཁ: ΧςΰϦৄࡉͱ͍͏Ϧιʔε΋ΞϓϦ͔Β࢖͍Έͪͳͦ͞͏ ࣮श෼

Slide 157

Slide 157 text

--- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,5 @@ +class CategoriesController < ApplicationController + def index + @categories = Category.all + end +end --- /dev/null +++ b/app/views/categories/index.json.jbuilder @@ -0,0 +1,5 @@ +json.array!(@categories) do |category| + json.extract! category, :id, :name + json.image_url image_url(category.image_url) + json.category_items_url category_items_url(category, format: :json) +end ղ౴ྫ

Slide 158

Slide 158 text

‣ APIܦ༝Ͱ஫จ͍ͨ͠ ‣ APIΫϥΠΞϯτ͸े෼ʹݡ͍͏͑ɺ௨৴ྉ΋࡟ݮ͍ͨͨ͠ ΊɺΫϥΠΞϯτεςʔτͷ؅ཧΛΫϥΠΞϯτଆͰ΍Δɻ ‣ Χʔτঢ়ଶΛ؅ཧ͢Δඞཁ͕ͳ͍ ‣ Ұ౓ͷ஫จʹඞཁͳ line_items ͢΂͕ͯಉ࣌ʹ
 ૹ৴͞ΕΔ ࣍ͷ໨ඪ஫จ"1*ͷ࡞੒

Slide 159

Slide 159 text

{ "line_items": [ { "item_id": 8, "quantity": 1 }, { "item_id": 15, "quantity": 2 } ] } ஫จϦΫΤετͷྫ

Slide 160

Slide 160 text

# spec/requests/order_api_spec.rb require 'rails_helper' RSpec.describe "Order items via API", type: :request do let(:kitchenware) { create(:category, name: '調理器具') } let(:hocho) { create(:item, category: kitchenware, name: '包丁', price: 3_000) } let(:pot) { create(:item, category: kitchenware, name: '鍋', price: 2_500) } specify 'A client can POST whole order in one request' do post( orders_path, params: { line_items: [ {item_id: hocho.id, quantity: 2}, {item_id: pot.id, quantity: 1}, ] }, as: :json ) expect(response.status).to eq(201) expect(JSON.parse(response.body)['status']).to eq('checked_out') end end "1*ͷ&&ςετ 3BJMT-FTTPO

Slide 161

Slide 161 text

{ "id": 5, "ordered_at": "2016-07-11T07:41:04.781Z", "total": 3000, "status": "checked_out", "created_at": "2016-07-11T07:41:04.782Z", "updated_at": "2016-07-11T07:41:04.802Z", "url": "https://ancient-bastion-22709.herokuapp.com/orders/5", "line_items": [ { "quantity": 1, "subtotal": 1000, "item": { "id": 8, "name": "寸胴", "price": 1000, "url": "https://ancient-bastion-22709.herokuapp.com/items/8" } }, { "quantity": 2, "subtotal": 2000, "item": { "id": 15, "name": "お皿", "price": 1000, "url": "https://ancient-bastion-22709.herokuapp.com/items/15" } } ] } Ϩεϙϯεͷαϯϓϧ

Slide 162

Slide 162 text

--- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,10 @@ class ApplicationController < ActionController::Base - protect_from_forgery with: :exception + protect_from_forgery with: :exception, unless: :valid_api_request? + + private + + def valid_api_request? + # FIXME need authentication for realworld application + request.format.json? + end end FIXME: ࠓճAPIͰPOST͢Δ৔߹

Slide 163

Slide 163 text

‣ ϦΫΤετڧཁʢCSRFɿCross-site Request Forgeryʣͱ͸ɺผͷαΠτʹ༻ҙ ͨ͠ίϯςϯπ্ͷ᠘ͷϦϯΫΛ౿·ͤΔ͜ͱ౳Λ͖͔͚ͬͱͯ͠ɺΠϯλʔ ωοτγϣοϐϯάͷ࠷ऴܾࡁ΍ୀձ౳WebΞϓϦέʔγϣϯͷॏཁͳॲཧΛ ݺͼग़͢Α͏ϢʔβΛ༠ಋ͢Δ߈ܸͰ͋Δɻ ‣ https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/ 301.html ‣ GETͰഁյతॲཧΛͨ͠ΓɺPOST࣌ʹϦΫΤετ΋ͱΛݕূ͠ͳ͔ͬͨΓ CSRF੬ऑੑͱ͸

Slide 164

Slide 164 text

‣ RailsͰ͸ϑΥʔϜύϥϝʔλ΍HTTPϔομʹ༧ଌෆೳτʔΫϯΛ෇༩͠ɺ
 ॲཧલʹݕূ͢Δ͜ͱͰ๷͍Ͱ͍Δɻ ‣ ͦΕΛͯ͘͠ΕΔͷ͕ `protect_from_forgery` ‣ ࠓճɺAPIϦΫΤετΛड͚Δࡍʹ͸͍ͬͨΜແޮʹ͢Δ͕ɺͦͷ͏͑ͰAPI ϦΫΤετݩΛݕূ͢ΔखஈΛ *ඞͣ* ಋೖ͢Δ͜ͱɻ CSRF੬ऑੑͱRailsͰͷ๷ޚ

Slide 165

Slide 165 text

--- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,10 @@ class ApplicationController < ActionController::Base - protect_from_forgery with: :exception + protect_from_forgery with: :exception, unless: :valid_api_request? + + private + + def valid_api_request? + # FIXME need authentication for realworld application + request.format.json? + end end (࠶ܝ) Real WordͰ΍Δͱ͖͸ϦΫΤετݩݕূΛͪΌΜͱ΍Ζ͏

Slide 166

Slide 166 text

‣ RailsͰJSON APIΛ࣮૷͢Δํ๏ΛֶΜͩ ‣ HTMLΛฦ͔͢JSONΛฦ͔͢͸දݱͷҧ͍ ‣ ͨͩ͠ɺΫϥΠΞϯτͷϦον͞ʹΑͬͯ
 ΤϯυϙΠϯτཻ౓͕ҧ͍͏Δ ‣ APIͷEnd-to-Endςετͷॻ͖ํΛֶΜͩ ‣ CSRF੬ऑੑͷଘࡏͱɺRailsͰͷͦͷରࡦΛֶΜͩ ͜ͷεςοϓͰֶΜͩ͜ͱ

Slide 167

Slide 167 text

‣ ্هςετ͕௨ΔΑ͏ʹɺ࣮૷͠ͳ͍͞ɻ ‣ [advanced] 201͕ฦΔ৔߹ɺ࡞੒͞ΕͨϨεϙϯε͸ Locationϔομͷࢦ͢ઌΛಡΈͳ͓͢΄͏͕ΑΓద੾Ͱ͢ɻ ςετ͕ͦͷΑ͏ʹͳΔΑ͏ʹɺमਖ਼͠ͳ͍͞ɻ ‣ [advanced] rspec-json_matcherΛ࢖ͬͯɺϨεϙϯεͷ JSONΛݕূ͠ͳ͍͞ɻ ࣮श޷͖ͳ͚ͩ