Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails3 Recipe Book Gaiden

Akira Matsuda
September 16, 2012

Rails3 Recipe Book Gaiden

札幌Ruby会議2012の発表スライド with @moro, @takahashim, and @tenderlove

Akira Matsuda

September 16, 2012
Tweet

More Decks by Akira Matsuda

Other Decks in Programming

Transcript

  1. ֎఻
    ???
    Sapporo RubyKaigi 2012
    @tenderlove
    @takahashim
    @moro
    @a_matsuda

    View full-size slide

  2. % whoami
    Twitter: @a_matsuda
    GitHub: amatsuda
    (ʮ͋·ͭͩʯ͡Όͳͯ͘ “A. Matsuda” Ͱ͢)

    View full-size slide

  3. ٻਓαΠτ࡞ͬͯ·͢
    ϑϦʔϥϯεͷඇৗۈऔక໾ (Ṗ)
    http://forkwell.com/

    View full-size slide

  4. Rails3ϨγϐϒοΫ
    Λߪೖ͢Δ
    օ༷͓͔࣋ͪͱ͸ࢥ͍·͕͋͢ΒͨΊͯɻ
    000

    View full-size slide

  5. ઈࢍൃചத
    શࠃͷॻళͰ
    ΦϯϥΠϯͰ

    View full-size slide

  6. ిࢠॻ੶൛΋͋ΔΑʂ
    http://bookpub.jp/books/bp/198

    View full-size slide

  7. “Entry”ͱ͍͏
    Ϟσϧ໊Λආ͚Δ
    ॻ੶ʹܝࡌ͍ͯͨ͠αϯϓϧίʔυʹ࢖༻͞Εͯ
    ͍ͨϞσϧ໊ʹҰ෦ෆద੾ͳ΋ͷ͕͋Γ·ͨ͠ɻ
    ۘΜͰ͓࿳ͼਃ্͛͠·͢ɻ
    001

    View full-size slide

  8. % rails g model blog name
    % rails g model entry blog:references title
    body:text
    Blog.has_many :entries
    ॻ੶தͷαϯϓϧΞϓϦ

    View full-size slide

  9. % rails c
    > Blog.scoped.entries
    => [#,
    #, ...]
    > Blog.scoped.entries.entries.entries.entries
    => [#,
    #, ...]
    Ṗͷݱ৅
    શͯͷRelationʹ࠷ॳ͔Β#entries͕ੜ͑ͯΔʂʁ
    ԿճͰ΋܁Γฦ͠ݺ΂Δʂʁ

    View full-size slide

  10. Blog. rst.entries.class #=> Array
    (Arrayʹݟ͑Δ͚Ͳ࣮ࡍ͸”Association”)
    Blog. rst.entries.order(‘title’) #=> order by͞ΕΔ
    (Association Proxyతͳ΋ͷͷಇ͖)
    Blog#entries

    View full-size slide

  11. > [1, 2, 3].entries #=> [1, 2, 3]
    Enumerable#entries
    entries -> [object]
    શͯͷཁૉΛؚΉ഑ྻΛฦ͠·͢ɻ
    Relation#entries ??

    View full-size slide

  12. ࣮͸ɺࣥචதʹʮEntry͸Ϛζ͍ʂʯͬͯؾ෇͍
    ͨ΋ͷͷɺӨڹൣғ͕σΧ͗͢ΔͷͰमਖ਼Λஅ
    ೦…
    ͍͓ͪ͏αϯϓϧίʔυ͸શ෦ͪΌΜͱಈ͘͠ɻ
    ʰRails4ϨγϐϒοΫʱͰ͸௚Γ·͢(ग़Ε͹
    Ͷ…)
    Rails3ϨγϐϒοΫ

    View full-size slide

  13. “Entry”ͱ͍͏Ϟσϧ໊͸࢖Θͳ͍Α͏ʹʂ
    PROTIP

    View full-size slide

  14. “type”͍ͬͯ͏ΧϥϜ໊
    => polymorphicؔ࿈ͱΈͳ͞Εͯڍಈෆ৹ʹͳΔ
    “Task”͍ͬͯ͏Ϟσϧ໊
    => Rake಺ͰRake::TaskͱέϯΧ͢ΔՄೳੑ
    “Application”͍ͬͯ͏Ϧιʔε໊
    => ApplicationϞσϧͱ͔ApplicationsControllerͱ͔…
    RailsΞϓϦࣗମͷ໊લʹকདྷϞσϧʹͳΓͦ͏ͳ໊લΛ͚ͭΔ
    => ϞσϧΛ࡞Εͳͯ͘ϋϚΔ
    ϓϥάΠϯ(gem)ͷ໊લʹ୭͔͕Ϟσϧʹͦ͠͏ͳ໊લΛ͚ͭΔ
    => Ϟσϧ͕ݟ͑ͳͯ͘ࠔΔ
    => gem໊͸ΩϥΩϥωʔϜਪ঑ (ʁ)
    ·ͩ·ͩ͋Δͧɺةͳ໊͍લ

    View full-size slide

  15. AssociationProxy
    Λ࢖͏
    AssociationProxyͱݺ͹ΕΔ(ݺ͹Ε͍ͯͨ)΋ͷʹ
    ͍ͭͯɺͬ͘͟Γ֓ཁΛޠΓ·͢ɻ
    002

    View full-size slide

  16. ARͷ”association”
    Ϟσϧؒͷʮؔ࿈ʯΛந৅Խͨ͠΋ͷ
    #owner
    #target
    #loaded?

    View full-size slide

  17. has direction
    ͋ΔUserͷΠϯελϯε͕ෳ਺ͷEmailΛ࣋ͬͯ
    ͍Δͱ͍͏ؔ࿈Λ͋ΒΘ͢ΦϒδΣΫτ
    <#User> <#Email>
    has_many
    <#User>#association(:emails)
    owner target
    by @moro

    View full-size slide

  18. Ұ౓ಡΜͩࢠڙΛΩϟογϡ
    > user.emails.loaded? #=> false
    > user.emails
    SELECT "emails".* FROM "emails" WHERE "emails"."user_id" = 1
    > user.emails.loaded? #=> true
    > user.emails
    ࠓ౓͸SQL͕࣮ߦ͞Εͳ͍ʂ

    View full-size slide

  19. Ͳ͜ʹΩϟογϡͯ͠Δͷʁ
    > user.emails.class #=> Array
    > user.emails.instance_variables #=> []
    > user.emails.proxy_association #=>
    #0x007f8dcf1c6df8
    @loaded=true,
    @owner= #,
    ...
    @target= [#...],
    ...
    (AR 3.2ͷ৔߹)

    View full-size slide

  20. ϝιουΛੜ΍ͨ͠ΓͰ͖Δ
    class User < ActiveRecord::Base
    has_many :emails do
    def latest_verified
    if loaded?
    target.select(&:verified?).
    sort_by(:updated_at).last
    else
    where('verified_at IS NOT NULL').
    order('updated_at DESC').first
    end
    end
    end
    end
    by @moro

    View full-size slide

  21. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺෳ਺छྨ
    ͷΤϯςΟςΟ΁ͷؔ࿈Λɺ1ͭͷؔ࿈Ͱ͋Δ͔ͷ
    Α͏ʹѻ͏ͨΊͷΞιγΤʔγϣϯͰ͢ɻ
    ৄ͘͠͸[078] ϙϦϞʔϑΟοΫؔ࿈Λར༻͢Δ
    Λݟ͍ͯͩ͘͞ɻ
    (@moro)
    ϙϦϞϧϑΟοΫΞιγΤʔ
    γϣϯͷN+1໰୊Λճආ͢Δ
    003

    View full-size slide

  22. ͱͯ΋ศར͕ͩ...
    N+1໰୊͕ൃੜ͠΍͍͢ͱ͍͏ऑ఺͕ɻ
    RDBMSతʹJOINͯ͠Ұؾʹऔͬͯ͘Δͱ͍͏͜ͱ
    ͕ग़དྷͳ͍ͨΊɺEager loading࢖͑ͳ͍ɻ

    View full-size slide

  23. =begin
    PR about us

    View full-size slide

  24. About me
    •MOROHASHI Kyosuke
    •@moro on Twitter and GitHub
    •works at @esminc
    •One of the authors of
    Rails3 Recipe Book

    View full-size slide

  25. Web built a nice "confidential" paste
    service. Available in beta.
    https://www.copi.pe/

    View full-size slide

  26. You can paste both text snippet and picture.

    View full-size slide

  27. Core models of copipedential
    pastes
    item_id:int
    item_type:string
    Snippet
    body:text
    letype_id:fk
    Picture
    storage_key:string
    :item

    View full-size slide

  28. AssociationProxyΛ࢖͏ͱɺ͜ΕΛO(2+1)ʹͰ͖·͢ɻ
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 25 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 24 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 23 LIMIT 1
    SELECT "pictures".* FROM "pictures" WHERE "pictures"."id" = 37 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 22 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 21 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 19 LIMIT 1
    SELECT "snippets".* FROM "snippets" WHERE "snippets"."id" = 18 LIMIT 1
    SELECT "pictures".* FROM "pictures" WHERE "pictures"."id" = 38 LIMIT 1

    View full-size slide

  29. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺnݸͷ௨ৗ
    ͷؔ࿈ʹ෼ղͰ͖Δɻ
    item_typeͰߜΓࠐΜͰ͔Βɺitem_idͰJOIN͢
    Ε͹Α͍ɻ
    ௨ৗͷؔ࿈Ͱ͋Ε͹eager loadͰ͖Δɻ
    ͦΕΛ࢖͑͹͍͍Μ͡Όͳ͍͔?
    جຊํ਑

    View full-size slide

  30. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺnݸͷ௨ৗ
    ͷؔ࿈ʹ෼ղͰ͖Δɻ
    class Paste < ActiveRecord::Base
    belongs_to :snippet,
    readonly: true,
    foreign_key: :item_id,
    conditions: ['EXISTS(SELECT 1 FROM snippets s WHERE s.id = ?
    AND pastes.item_id = s.id)', 'Snippet']
    belongs_to :pictures,
    readonly: true,
    foreign_key: :item_id,
    conditions: ['EXISTS(SELECT 1 FROM pictures s WHERE s.id = ?
    AND pastes.item_id = s.id)', 'Picture']
    def item
    (...)
    end
    end

    View full-size slide

  31. eager loadͨ͠௨ৗͷؔ࿈ͷΦϒδΣΫτΛ࢖͏
    class Paste < ActiveRecord::Base
    belongs_to :snippet, (...)
    belongs_to :pictures, (...)
    def item
    actual_item = association(item_type.underscore.to_sym)
    polymorphic_item = association(:item)
    if actual_item.loaded? && !polymorphic_item.loaded?
    polymorphic_item.target = actual_item.target
    end
    super
    end
    end

    View full-size slide

  32. ͍͍ͩͨͰ͖ͨ
    > Paste.limit(10).includes(:snippet, :pictures).map(&:item)
    # => SELECT "pastes".* FROM "pastes" LIMIT 10
    # => SELECT "snippets".* FROM "snippets" WHERE
    "snippets"."id" IN (1, 2, 3, 4, 5, 6) AND (EXISTS(SELECT 1
    FROM "pastes" WHERE "pastes".item_type = 'Snippet' AND
    "pastes".item_id = "snippets".id))
    # => SELECT "pictures".* FROM "pictures" WHERE
    "pictures"."id" IN (1, 2, 3, 4, 5, 6) AND (EXISTS(SELECT 1
    FROM "pastes" WHERE "pastes".item_type = 'Picture' AND
    "pastes".item_id = "pictures".id))

    View full-size slide

  33. ΋ͬͱίϯύΫτʹॻ͖͍ͨ!
    class Paste < ActiveRecord::Base
    extend EagerLoadablePolymorph
    belongs_to :item, polymorphic: true
    eager_loadable_polymorphic_association \
    :item, [:snippet, :picture]
    ...
    end

    View full-size slide

  34. ΋ͬͱίϯύΫτʹॻ͖͍ͨ!
    module EagerLoadablePolymorph
    class AssociationWriter
    def initialize(association, types)
    raise ArgumentError, "#{association} is not polymorphic association" unless association.options[:polymorphic]
    @association = association
    @types = types
    end
    def belong_to_them(ref_from)
    @types.each do |t|
    ref_from.belongs_to t, readonly: true, foreign_key: fk, conditions: condition_sql(t.to_s.classify.constantize)
    end
    end
    def define_scope(ref_from)
    ref_from.scope "with_#{@association.name}", ref_from.includes(*@types)
    end
    def override_accessor(ref_from)
    ref_from.class_eval <<-RUBY.strip_heredoc
    def #{@association.name}
    acutual_item = association(self[#{ft.dump}].underscore.to_sym)
    polymorphic_item = association(:#{@association.name})
    if acutual_item.loaded? && !polymorphic_item.loaded?
    polymorphic_item.target = acutual_item.target
    end
    super
    end
    RUBY
    end
    private
    # aliasses
    def ft
    @association.foreign_type
    http://bit.ly/eager-loadable-polymorph

    View full-size slide

  35. def define_scope(ref_from)
    ref_from.scope "with_#{@association.name}", re
    end
    def override_accessor(ref_from)
    ref_from.class_eval <<-RUBY.strip_heredoc
    def #{@association.name}
    acutual_item = association(self[#{ft.dump}
    polymorphic_item = association(:#{@associa
    if acutual_item.loaded? && !polymorphic_it
    polymorphic_item.target = acutual_item.t
    end
    super
    end
    RUBY
    end
    private
    ΋ͬͱίϯύΫτʹॻ͖͍ͨ!
    http://bit.ly/eager-loadable-polymorph

    View full-size slide

  36. ͜ͷϨγϐͷ·ͱΊ
    AssociationProxyؔ࿈ϝιου͸ʮͧ͜͜ʯͱ͍
    ͏ͱ͖໾ཱͭɻ
    ϙϦϞϧϑΟοΫΞιγΤʔγϣϯͰN+1໰୊͸
    ղܾՄೳɻ໾ཱͭɻ
    (౦ژʹ໭ͬͨΒgemʹ͠·͢)

    View full-size slide

  37. AR::Relation#merge
    Λ࢖͍౗͢
    AR::Relation#mergeΛ࢖ͬͯRelationͬΆ͍΋ͷΛ
    ͍Ζ͍Ζmergeͯ͠Έ·͠ΐ͏ɻ
    AR::Relation#mergeʹ͍ͭͯ͸ [066] ϙϦϞʔ
    ϑΟοΫؔ࿈Λར༻͢Δ Λࢀর͍ͯͩ͘͠͞ɻ
    004

    View full-size slide

  38. class Blog < AR
    scope :rails, where(name: ‘Riding Rails’)
    end
    class Post < AR
    scope :recent, -> { where 'posts.created_at >= ?',
    1.week.ago }
    end
    class User < AR
    has_many :blogs, through: ...
    end
    models

    View full-size slide

  39. > Post.joins(:blog).recent.merge(Blog.rails)
    SELECT posts.* FROM posts INNER JOIN blogs ON blogs.id =
    posts.blog_id WHERE blogs.name = 'Riding Rails' AND
    (posts.created_at >= '2012-09-08 03:16:03.888840')
    scopeͱmerge

    View full-size slide

  40. > Post.joins(:blog).recent
    .merge(User. rst.blogs.scoped)
    SELECT posts.* FROM posts INNER JOIN blogs ON blogs.id =
    posts.blog_id INNER JOIN user_blogs ON blogs.id =
    user_blogs.blog_id WHERE user_blogs.user_id = 1 AND
    (posts.created_at >= '2012-09-08 03:14:52.839676')
    Associationͱmerge
    AR 3.2Ͱ͸ඞཁ
    4.0Ͱ͸ෆཁ

    View full-size slide

  41. whereͰαϒΫΤϦ
    > Post.where(blog_id: Blog.rails)
    SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" IN
    (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."name" = 'Riding
    Rails')

    View full-size slide

  42. not΍likeͷΫΤϦΛ
    Rubyishʹهड़͢Δ
    ARͰ͸RubyͷHashΛ࢖ͬͯwhere۟ΛΧοίΑ͘
    ॻ͚ΔΑ͏ʹ޻෉͕ͳ͞Ε͍ͯ·͕͢ɺnot΍like
    ͕ೖΔͱͱͨΜʹจࣈྻͰSQLΛॻ͔͞ΕΔӋ໨
    ʹͳͬͯΨοΧϦ͠·͢ɻ
    Ͳ͏ʹ͔ͳΒͳ͍΋ͷͰ͠ΐ͏͔ʁ
    005

    View full-size slide

  43. ARͷwhereͷ੯͍͠ͱ͜Ζ
    select * from users where name = ‘tenderlove’
    <= User.where(name: ‘tenderlove’)
    select * from users where name <> ‘tenderlove’
    <= User.where(“name <>‘tenderlove’”)
    select * from users where name like ‘tender%’
    <= User.where(“name like ‘tender%’”)
    RubyͷHash
    SQLͦͷ΋ͷ

    View full-size slide

  44. A new syntax for AR4
    https://github.com/rails/rails/pull/
    5950#issuecomment-5591330

    View full-size slide

  45. not
    User.where.not(name: ‘DHH’)
    User.where.not(name: nil)

    View full-size slide

  46. like, not like
    User.where.like(name: ‘John%’)
    User.where.not.like(name: ‘David%’)

    View full-size slide

  47. ෳࡶͳΫΤϦ
    User.where.not(name: nil).where.like(name:
    ‘Aaron%’, email: ‘tender%).where(‘age > 20’)

    View full-size slide

  48. A new syntax for AR4
    https://github.com/rails/rails/pull/
    5950#issuecomment-5591330 4 months ago...

    View full-size slide

  49. sprkऴΘͬͨΒPRग़͠·͢
    ΄Μͱ஗͘ͳͬͯ͢Έ·ͤΜ…

    View full-size slide

  50. Ұ଍͓ઌʹࢼ͍ͨ͠ํ
    Gem le
    gem ‘everywhere’
    % bundle

    View full-size slide

  51. Twisting SQLite3
    (@tenderlove)

    View full-size slide

  52. • Step 1: Read the API
    • Step 2: Write your code
    • Step 3: Annoy your friends (or profit)

    View full-size slide

  53. struct sqlite3_vfs {
    int iVersion; /* Structure version number */
    int szOsFile; /* Size of subclassed sqlite3_file */
    int mxPathname; /* Maximum file pathname length */
    sqlite3_vfs *pNext; /* Next registered VFS */
    const char *zName; /* Name of this virtual file system */
    void *pAppData; /* Pointer to application-specific data */
    int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
    int flags, int *pOutFlags);
    int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
    int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
    int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
    void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
    void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
    void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);
    void (*xDlClose)(sqlite3_vfs*, void*);
    int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
    int (*xSleep)(sqlite3_vfs*, int microseconds);
    int (*xCurrentTime)(sqlite3_vfs*, double*);
    int (*xGetLastError)(sqlite3_vfs*, int, char *);
    /* New fields may be appended in figure versions. The iVersion
    ** value will increment whenever this happens. */
    };
    Step 1: Read the API

    View full-size slide

  54. Step 2: Write our Code

    View full-size slide

  55. static int rbFile_close(sqlite3_file * ctx)
    {
    rubyFilePtr rfile = (rubyFilePtr)ctx;
    VALUE file = rfile->file;
    rb_funcall(file, rb_intern("close"), 0);
    return SQLITE_OK;
    }
    Example of the hooks
    we have to write

    View full-size slide

  56. class VFS < SQLite3::VFS
    def open(name, flags)
    OurFile.new(name, flags)
    end
    end
    class OurFile
    def read(...); ... end
    def write(...); ... end
    end

    View full-size slide

  57. Anything can store
    a SQLite Database

    View full-size slide

  58. __END__ + DATA
    Anything after __END__
    is provided in the DATA
    object.

    View full-size slide

  59. class EvilVFS < SQLite3::VFS
    def open name, flags
    DATABase.new name, flags
    end
    end
    class DATABase < SQLite3::VFS::File
    def initialize name, flags
    super
    @store = File.open(name, File::RDWR | File::CREAT)
    @offset = 0
    if File.expand_path(__FILE__) == name
    @store.seek DATA.pos
    @offset = DATA.pos
    end
    end
    def close
    @store.close
    end
    def read amt, offset
    @store.seek(offset + @offset)
    @store.read amt
    end
    def write data, offset
    @store.seek(offset + @offset)
    @store.write data
    end
    def sync opts
    @store.fsync
    end
    def file_size
    File.size(@store.path) - @offset
    end
    end
    Stores data to after then
    __END__ part

    View full-size slide

  60. SQLite3.vfs_register(EvilVFS.new)
    db = SQLite3::Database.new(__FILE__, nil, 'EvilVFS')
    db.execute(<<-eosql)
    create table if not exists
    users(id integer primary key, name string)
    eosql
    100.times {
    db.execute(<<-eosql, 'tenderlove')
    insert into users(name) values (?)
    eosql
    }
    p db.execute('select count(*) from users')
    __END__

    View full-size slide

  61. Step 3: Profit?

    View full-size slide

  62. Get the Code!
    http://gist.github.com/319224/

    View full-size slide

  63. Rails consoleͷ
    Ӆ͠ίϚϯυͨͪ
    Rails consoleʹ͸RailsΞϓϦ։ൃΛࢧԉ͢Δศར
    ͳϝιου͕͍͔ͭ͋͘Β͔͡Ίఆٛ͞Ε͍ͯ·
    ͢ɻ
    006

    View full-size slide

  64. app
    > app.blogs_path #=> "/blogs"
    > app.url_for(Blog. rst)
    #=> "http://www.example.com/blogs/1"
    > app.get('/blogs')
    SELECT "blogs".* FROM "blogs"
    #=> 200

    View full-size slide

  65. helper
    > helper.link_to 'Please Login', '/login'

    View full-size slide

  66. reload!
    ΞϓϦΛͦΕͳΓʹϦϩʔυͯ͘͠ΕΔ

    View full-size slide

  67. σϑΥϧτͰੵ·Ε͍ͯΔRackϛυϧ΢ΣΞͷ͏
    ͪɺෆཁͳ΋ͷΛফ͢ͱύϑΥʔϚϯε͕ΊͪΌ
    ΊͪΌվળͨ͠Γ͢Δ͜ͱ͕͋Γ·͢ɻ
    ɹɹɹɹɹɹɹ (thanks to: @hotchpotch, @takai)
    ෆཁͳRackϛυϧ΢ΣΞΛ
    ࡟ͬͯϨεϙϯεΛ଎͘͢Δ
    007

    View full-size slide

  68. % rake middleware
    use ActionDispatch::Static
    use Rack::Lock
    use #
    use Rack::Runtime
    use Rack::MethodOverride
    use ActionDispatch::RequestId
    use Rails::Rack::Logger
    use ActionDispatch::ShowExceptions
    use ActionDispatch::DebugExceptions
    use ActionDispatch::RemoteIp
    use ActionDispatch::Reloader
    use ActionDispatch::Callbacks
    use ActiveRecord::ConnectionAdapters::ConnectionManagement
    use ActiveRecord::QueryCache
    use ActionDispatch::Cookies
    use ActionDispatch::Session::CookieStore
    use ActionDispatch::Flash
    use ActionDispatch::ParamsParser
    use ActionDispatch::Head
    use Rack::ConditionalGet
    use Rack::ETag
    use ActionDispatch::BestStandardsSupport
    run Test328::Application.routes
    ੵ·ΕͯΔmiddlewareΛ֬ೝ

    View full-size slide

  69. Rails 3.2ΞϓϦͰϦΫΤετ͝ͱʹmemcached΁
    ͷΞΫηεͷൃੜΛ؍ଌ
    ؀ڥʹΑͬͯ͸memcachedͰ٧·Δ
    Rack::Cache͕ΫηϞϊʁ

    View full-size slide

  70. con g.middleware.delete 'Rack::Cache'
    ͳͷͰ֎ͯ͠ΈΔ

    View full-size slide

  71. localhost, memcached, “hello”ͬͯݴ͏͚ͩͷ
    ΞϓϦ
    before
    Requests per second: 355.45 [#/sec] (mean)
    after
    Requests per second: 381.99 [#/sec] (mean)
    ๭ࣾͰ͸͜Ε͚ͩͰ1.5ഒ͘Β͍଎͘ͳͬͨͱ͍
    ͏ࣄྫ΋
    ϕϯν

    View full-size slide

  72. con g.middleware.delete 'Rack::ETag'
    con g.middleware.delete 'Rack::ConditionalGet'
    ͞Βʹ৭ʑ

    View full-size slide

  73. localhost, memcached, “hello”ͬͯݴ͏͚ͩͷ
    ΞϓϦ
    before
    Requests per second: 381.99 [#/sec] (mean)
    after
    Requests per second: 410.48 [#/sec] (mean)
    ϕϯν

    View full-size slide

  74. APαʔόʔͰ͜ͷ͋ͨΓͷΩϟογϡΛѻΘͳ͍
    ݶΓ͸ෆཁͳ͸ͣ…ͩͱࢥ͏Μ͚ͩͲͲ͏ͳΜͩ
    Ζ͏ʁ
    ফ͚ͩ͢Ͱ଎͘ͳͬͨʂ

    View full-size slide

  75. ࣮ߦՄೳͳRakeλεΫΛදࣔ͢Δʹ͸جຊతʹ͸
    `rake -T`ͱ͍͏ίϚϯυΛୟ͚͹ྑ͍ͷͰ͕͢ɺ
    RailsͰ͸`rake -T`΍`rake -D`Ͱදࣔ͞Εͳ͍λεΫ
    ΋࣮͸݁ߏఆٛ͞Ε͍ͯͨΓ͠·͢ɻ
    Ӆ͠RakeλεΫΛ
    શ෦஌Γ͍ͨ
    008

    View full-size slide

  76. commit 29acc17
    Author: David Heinemeier Hansson

    Date: 2010-06-09 16:19:03 -0400
    Cut down on tasks shown in rake -T
    DHHʮrake -Tݟʹ͘͘Ͷʁʯ

    View full-size slide

  77. Rake.application.tasks
    ϩʔυ͞ΕͯΔTaskͷίϨΫγϣϯ
    lib/tasks/t.rake
    task :t do
    puts *Rake.application.tasks.map(&:name)
    end

    View full-size slide

  78. % rake -T | wc -l
    32
    % rake t | wc -l
    103 (102 + 1)

    View full-size slide

  79. % rake t | grep db
    db:_dump
    db:abort_if_pending_migrations
    db:charset
    db:collation
    db:create
    db:create:all
    db:drop
    db:drop:all
    db: xtures:identify
    db: xtures:load
    db:forward
    db:load_con g
    db:migrate
    db:migrate:down
    db:migrate:redo
    db:migrate:reset
    db:migrate:status
    db:migrate:up
    db:reset
    db:rollback
    db:schema:dump
    db:schema:load
    db:schema:load_if_ruby
    db:seed
    db:sessions:clear
    db:sessions:create
    db:setup
    db:structure:dump
    db:structure:load
    db:structure:load_if_sql
    db:test:clone
    db:test:clone_structure
    db:test:load
    db:test:load_schema
    db:test:load_structure
    db:test:prepare
    db:test:purge

    View full-size slide

  80. migrationͷ࡞੒΍࣮ߦΛ͍͍ͪͪखଧͪ͢Δͷͬ
    ͯΊΜͲ͍͘͞͠ɺ໊લͷ෇͚ํͷن໿ͳΜ͔΋
    ֮͑ͯΒΕͳ͍Ͱ͢ΑͶɻ
    migrationΛ
    GUIͰ࣮ߦ͢Δ
    009

    View full-size slide

  81. Gem le
    gem ‘erd’
    % bundle
    % open http://localhost:3000/erd
    erd

    View full-size slide

  82. ϑϩϯτΤϯυ͕͠ΐ΅͍…
    JS: @os0x
    σβΠϯ: @machida

    View full-size slide

  83. Stay Tuned!
    https://github.com/amatsuda/erd

    View full-size slide

  84. helperΛΦϒδΣΫτࢦ޲
    ͬΆ͘ॻ͘
    RailsͷhelperͷΠέͯͳ͞Λղফ͢Δํ๏ɻ
    010

    View full-size slide

  85. app/helpers/*
    helperϝιου͸ʮؔ਺ʯɻRuby͡Όͳ͍ײɻ
    app/helpers/user_helper.rb
    # ϢʔβʔͷΞΠίϯ
    def user_icon(user)
    image_tag user.icon_url
    end
    ໊લ͕μα͍ Ҿ਺͕μα͍

    View full-size slide

  86. active_decorator
    Gem le
    gem ‘active_decorator’
    % bundle
    % rails g decorator user
    create app/decorators/user_decorator.rb

    View full-size slide

  87. user_decorator.rb
    app/decorators/user_decorator.rb
    module UserDecorator
    def icon
    image_tag icon_url
    end
    end
    ↑ͷmodule͕view_assigns࣌ʹ@userͱ͔@usersత
    ͳ΍ͭʹࣗಈతʹextend͞ΕΔ
    view͔Β࢖͏ͱ͖͸
    <%= @user.icon %>

    View full-size slide

  88. ؔ࿈ઌΛdecorate͍ͨ࣌͠͸
    app/decorators/post_decorator.rb
    module PostDecorator
    def summary
    truncate body, length: 20
    end
    end
    app/views/entries/show.html.erb
    render @entry.posts
    app/views/posts/_post.html.erb
    <%= post.summary %>

    View full-size slide

  89. ARelͷΦϒδΣΫτΛ
    Rubyͷίʔυʹม׵͢Δ
    ωλͰ͢ɻ
    011

    View full-size slide

  90. AR::Relation#arel
    > Post.scoped.arel
    => #@ast=
    #@cores=
    [#@groups=[],
    @having=nil,
    @projections=
    [#relation=
    #@aliases=[],
    @columns=nil,
    @engine=
    Post(id: integer, blog_id: integer, title: string, created_at: datetime, updated_at:
    datetime),
    @name="posts",
    ...

    View full-size slide

  91. ARel AST => SQL
    > Post.scoped.arel.class
    => Arel::SelectManager
    > puts Post.where(name: 'foo').arel.to_sql
    SELECT "posts".* FROM "posts" WHERE "posts"."name" = 'foo'

    View full-size slide

  92. ARel AST => Ruby
    > puts Post.where(name: 'foo').order('id').
    offset(10).limit(5).to_ruby.to_source
    select {|o| o.name == ‘foo’}.
    sort_by(&:id).from(10).take(5)

    View full-size slide

  93. arel_ruby
    https://github.com/amatsuda/arel_ruby

    View full-size slide

  94. usage
    ֎෦DBͱ͔͕͋ͬͯtestͰ͸ܨ͛ͨ͘ͳ͍৔߹
    ʹɺRDBΛ࢖ΘͣʹRuby͚ͩͰARͷ໰͍߹Θͤ
    ʹରԠͰ͖ͯ΂ΜΓɺ͔΋ɻ

    View full-size slide

  95. RailsϨγϐϒοΫ
    Λߋ৽͢Δ
    ؾͷར͍ͨRails޲͚Ϩγϐͷ࣋ͪ߹Θ͕ͤͳ͍ͷͰɺ
    Rails3ϨγϐϒοΫࣥචʹ࢖ΘΕٕͨͷ͝঺հͰ͢ɻ
    (@takahashim)
    333

    View full-size slide

  96. ݩ૆RailsϨγϐϒοΫ
    2008೥6݄ൃߦ
    ͓͔͛͞·Ͱ͝޷ධ
    վగͯ͠Rails3൛Λ→ຊॻ
    RailsϨγϐϒοΫ

    View full-size slide

  97. େ෯ͳՃච͸ͭͭ͠΋ɺϕʔε͸ʰRailsϨγϐ
    ϒοΫʱ͔Β
    Ұ෦ͷষ͸શ໘ॻ͖׵͑
    ʰRailsϨγϐϒοΫʱͷ൛Լ༻PDFϑΝΠϧΛ
    ݩʹɺ΋͏Ұ౓ςΩετϑΝΠϧʹ໭ͯ͠ɺͦ͜
    ͔Βॻ͖௚͍ͯ͘͠
    Rails3ϨγϐϒοΫ

    View full-size slide

  98. ίϐϖ
    Preview.appͰPDFΛ։͘
    ຊจΛίϐʔˍϖʔετͯ͠ςΩετʹ
    ഑ஔ͕͓͔͔ͬͨ͠ΒखͰ௚͢
    PDF→ςΩετม׵

    View full-size slide

  99. ୙఺ɾ൒୙఺͕ผͷจࣈʹ
    Unicodeਖ਼نԽͷ᠘

    View full-size slide

  100. moro͞Μۘ੡
    Ruby 1.9ઐ༻
    EncodingΛUTF8-Mac͔ΒUTF-8ʹม׵
    ม׵εΫϦϓτ

    View full-size slide

  101. ARGV.each do |f|
    File.open(f, 'r+:UTF8-MAC') do |f|
    str = f.read
    f.rewind
    f.truncate(0)
    f.set_encoding('UTF-8')
    f.write(str)
    end
    end

    View full-size slide

  102. ҙ֎ʹେ׆༂
    Mac OS X͸ൺֱత͜͏ਖ਼نԽ͢Δ͜ͱ͕ଟ͍
    Preview.appɹ
    ϑΝΠϧ໊ʢύε໊ʣ
    ೔ʑͷੜ׆ͷதͰ
    ͜ͷεΫϦϓτΛ࢖͑͹௚Γ·͢
    Ͳ͏͓ͧ࢖͍͍ͩ͘͞

    View full-size slide

  103. Rails4ϨγϐϒοΫʁ
    ʁʁʁ

    View full-size slide

  104. Rails3ϨγϐϒοΫ
    Λߪೖ͢Δ
    օ༷͓͔࣋ͪͱ͸ࢥ͍·͕͋͢ΒͨΊͯɻ
    000

    View full-size slide

  105. EngineΛ࢖͏
    Rails EngineΛ࢖ͬͨΓ࡞ͬͨΓςετͨ͠ΓϚχ
    ΞοΫͳ࢖͍ํΛͨ͠Γͯ͠Έ·͠ΐ͏ɻ
    012

    View full-size slide

  106. جຊతͳEngineͷͭ͘Γ
    ᵓᴷᴷ app
    ᴹ ᵓᴷᴷ controllers
    ᴹ ᴹ ᵋᴷ foo
    ᴹ ᴹ ᵋᴷ bar_controller.rb
    ᴹ ᵓᴷᴷ helpers
    ᴹ ᴹ ᵋᴷ foo
    ᴹ ᴹ ᵋᴷ bar_helper.rb
    ᴹ ᵋᴷᴷ views
    ᴹ ᵋᴷ foo
    ᴹ ᵋᴷ bar
    ᴹ ᵋᴷ index.html.erb
    ᵓᴷᴷ con g
    ᴹ ᵋᴷ routes.rb
    ᵋᴷᴷ lib
    ᵋᴷ foo
    ᵋᴷ engine.rb

    View full-size slide

  107. isolated Engine
    lib/foo/engine.rb
    module Foo
    class Engine < ::Rails::Engine
    isolate_namespace Foo
    end
    end

    View full-size slide

  108. ਌ΞϓϦ͔Βmount
    ਌ΞϓϦ/con g/routes.rb
    mount Foo::Engine, at: ‘foo’
    /foo/bar ͰEngine಺ͷίϯτϩʔϥʔ͕ݺ΂Δ

    View full-size slide

  109. EngineΛςετ͢Δ
    ςετίʔυ಺Ͱಈతʹ਌RailsΞϓϦΛ࡞ͬͯmount͢Δͷָ͕
    app = Class.new(Rails::Application)
    app.con g.secret_token = '3b7cd727ee24e8444053437c36cc66c4'
    app.con g.session_store :cookie_store, key: '_myapp_session'
    app.initialize!
    app.routes.draw { resources(:users) }
    class User < ActiveRecord::Base; end
    class ApplicationController < ActionController::Base; end
    class UsersController < ApplicationController
    def index
    @users = User.all
    render inline: ’<%= @users.map(&:name).join("\n") %>’
    end
    end
    Object.const_set(:ApplicationHelper, Module.new)

    View full-size slide

  110. ൚༻తͳEngine͕Ͱ͖ͨΒ
    gem push͠·͠ΐ͏ʂ

    View full-size slide

  111. Engine gem͔ΒผͷαϒEngineΛmount͍ͨ͠
    ؅ཧ͕໘౗͔ͩΒGem͸Ұͭʹ͍ͨ͠
    gemspec le
    Gem::Speci cation.new do |s|
    s.require_paths = ['lib', 'engines/foo/lib']
    end
    ࢀߟ: https://github.com/amatsuda/hocus_pocus
    Engine gemʹEngineΛmount
    ͢Δ

    View full-size slide

  112. ֎఻
    ???
    Sapporo RubyKaigi 2012

    View full-size slide