Rails3 Recipe Book Gaiden

76a777ff80f30bd3b390e275cce625bc?s=47 Akira Matsuda
September 16, 2012

Rails3 Recipe Book Gaiden

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

76a777ff80f30bd3b390e275cce625bc?s=128

Akira Matsuda

September 16, 2012
Tweet

Transcript

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

  2. before

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

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

  6. begin

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

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

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

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

  11. % rails g model blog name % rails g model

    entry blog:references title body:text Blog.has_many :entries ॻ੶தͷαϯϓϧΞϓϦ
  12. % rails c > Blog.scoped.entries => [#<Blog id: 1, name:

    "Riding Rails">, #<Blog id: 2, name: "Matzʹ͖ͬ">, ...] > Blog.scoped.entries.entries.entries.entries => [#<Blog id: 1, name: "Riding Rails">, #<Blog id: 2, name: "Matzʹ͖ͬ">, ...] Ṗͷݱ৅ શͯͷRelationʹ࠷ॳ͔Β#entries͕ੜ͑ͯΔʂʁ ԿճͰ΋܁Γฦ͠ݺ΂Δʂʁ
  13. Blog. rst.entries.class #=> Array (Arrayʹݟ͑Δ͚Ͳ࣮ࡍ͸”Association”) Blog. rst.entries.order(‘title’) #=> order by͞ΕΔ

    (Association Proxyతͳ΋ͷͷಇ͖) Blog#entries
  14. > [1, 2, 3].entries #=> [1, 2, 3] Enumerable#entries entries

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

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

  17. “type”͍ͬͯ͏ΧϥϜ໊ => polymorphicؔ࿈ͱΈͳ͞Εͯڍಈෆ৹ʹͳΔ “Task”͍ͬͯ͏Ϟσϧ໊ => Rake಺ͰRake::TaskͱέϯΧ͢ΔՄೳੑ “Application”͍ͬͯ͏Ϧιʔε໊ => ApplicationϞσϧͱ͔ApplicationsControllerͱ͔… RailsΞϓϦࣗମͷ໊લʹকདྷϞσϧʹͳΓͦ͏ͳ໊લΛ͚ͭΔ

    => ϞσϧΛ࡞Εͳͯ͘ϋϚΔ ϓϥάΠϯ(gem)ͷ໊લʹ୭͔͕Ϟσϧʹͦ͠͏ͳ໊લΛ͚ͭΔ => Ϟσϧ͕ݟ͑ͳͯ͘ࠔΔ => gem໊͸ΩϥΩϥωʔϜਪ঑ (ʁ) ·ͩ·ͩ͋Δͧɺةͳ໊͍લ
  18. AssociationProxy Λ࢖͏ AssociationProxyͱݺ͹ΕΔ(ݺ͹Ε͍ͯͨ)΋ͷʹ ͍ͭͯɺͬ͘͟Γ֓ཁΛޠΓ·͢ɻ 002

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

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

    by @moro
  21. Ұ౓ಡΜͩࢠڙΛΩϟογϡ > user.emails.loaded? #=> false > user.emails SELECT "emails".* FROM

    "emails" WHERE "emails"."user_id" = 1 > user.emails.loaded? #=> true > user.emails ࠓ౓͸SQL͕࣮ߦ͞Εͳ͍ʂ
  22. Ͳ͜ʹΩϟογϡͯ͠Δͷʁ > user.emails.class #=> Array > user.emails.instance_variables #=> [] >

    user.emails.proxy_association #=> #<ActiveRecord::Associations::HasManyAssociation: 0x007f8dcf1c6df8 @loaded=true, @owner= #<User id: 1, name: "a_matsuda">, ... @target= [#<Email id: 1, user_id: 1, email: "ronnie@dio.jp">...], ... (AR 3.2ͷ৔߹)
  23. ϝιουΛੜ΍ͨ͠ΓͰ͖Δ 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
  24. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺෳ਺छྨ ͷΤϯςΟςΟ΁ͷؔ࿈Λɺ1ͭͷؔ࿈Ͱ͋Δ͔ͷ Α͏ʹѻ͏ͨΊͷΞιγΤʔγϣϯͰ͢ɻ ৄ͘͠͸[078] ϙϦϞʔϑΟοΫؔ࿈Λར༻͢Δ Λݟ͍ͯͩ͘͞ɻ (@moro) ϙϦϞϧϑΟοΫΞιγΤʔ γϣϯͷN+1໰୊Λճආ͢Δ 003

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

  26. =begin PR about us

  27. About me •MOROHASHI Kyosuke •@moro on Twitter and GitHub •works

    at @esminc •One of the authors of Rails3 Recipe Book
  28. 4VO4FQ

  29. None
  30. Web built a nice "confidential" paste service. Available in beta.

    https://www.copi.pe/
  31. You can paste both text snippet and picture.

  32. =end

  33. Core models of copipedential pastes item_id:int item_type:string Snippet body:text letype_id:fk

    Picture storage_key:string :item
  34. None
  35. 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
  36. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺnݸͷ௨ৗ ͷؔ࿈ʹ෼ղͰ͖Δɻ item_typeͰߜΓࠐΜͰ͔Βɺitem_idͰJOIN͢ Ε͹Α͍ɻ ௨ৗͷؔ࿈Ͱ͋Ε͹eager loadͰ͖Δɻ ͦΕΛ࢖͑͹͍͍Μ͡Όͳ͍͔? جຊํ਑

  37. ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺ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
  38. 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
  39. ͍͍ͩͨͰ͖ͨ > 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))
  40. ΋ͬͱίϯύΫτʹॻ͖͍ͨ! class Paste < ActiveRecord::Base extend EagerLoadablePolymorph belongs_to :item, polymorphic:

    true eager_loadable_polymorphic_association \ :item, [:snippet, :picture] ... end
  41. ΋ͬͱίϯύΫτʹॻ͖͍ͨ! 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
  42. 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
  43. ͜ͷϨγϐͷ·ͱΊ AssociationProxyؔ࿈ϝιου͸ʮͧ͜͜ʯͱ͍ ͏ͱ͖໾ཱͭɻ ϙϦϞϧϑΟοΫΞιγΤʔγϣϯͰN+1໰୊͸ ղܾՄೳɻ໾ཱͭɻ (౦ژʹ໭ͬͨΒgemʹ͠·͢)

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

  45. 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
  46. > 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
  47. > 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Ͱ͸ෆཁ
  48. 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')
  49. not΍likeͷΫΤϦΛ Rubyishʹهड़͢Δ ARͰ͸RubyͷHashΛ࢖ͬͯwhere۟ΛΧοίΑ͘ ॻ͚ΔΑ͏ʹ޻෉͕ͳ͞Ε͍ͯ·͕͢ɺnot΍like ͕ೖΔͱͱͨΜʹจࣈྻͰSQLΛॻ͔͞ΕΔӋ໨ ʹͳͬͯΨοΧϦ͠·͢ɻ Ͳ͏ʹ͔ͳΒͳ͍΋ͷͰ͠ΐ͏͔ʁ 005

  50. 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ͦͷ΋ͷ
  51. A new syntax for AR4 https://github.com/rails/rails/pull/ 5950#issuecomment-5591330

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

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

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

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

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

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

  58. Twisting SQLite3 (@tenderlove)

  59. • Step 1: Read the API • Step 2: Write

    your code • Step 3: Annoy your friends (or profit)
  60. 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
  61. IO Hooks

  62. Step 2: Write our Code

  63. 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
  64. class VFS < SQLite3::VFS def open(name, flags) OurFile.new(name, flags) end

    end class OurFile def read(...); ... end def write(...); ... end end
  65. Anything can store a SQLite Database

  66. __END__ + DATA Anything after __END__ is provided in the

    DATA object.
  67. 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
  68. 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__
  69. Step 3: Profit?

  70. None
  71. Get the Code! http://gist.github.com/319224/

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

  73. app > app.blogs_path #=> "/blogs" > app.url_for(Blog. rst) #=> "http://www.example.com/blogs/1"

    > app.get('/blogs') SELECT "blogs".* FROM "blogs" #=> 200
  74. helper > helper.link_to 'Please Login', '/login'

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

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

    007
  77. % rake middleware use ActionDispatch::Static use Rack::Lock use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fa191fde078> 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Λ֬ೝ
  78. Rails 3.2ΞϓϦͰϦΫΤετ͝ͱʹmemcached΁ ͷΞΫηεͷൃੜΛ؍ଌ ؀ڥʹΑͬͯ͸memcachedͰ٧·Δ Rack::Cache͕ΫηϞϊʁ

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

  80. localhost, memcached, “hello”ͬͯݴ͏͚ͩͷ ΞϓϦ before Requests per second: 355.45 [#/sec]

    (mean) after Requests per second: 381.99 [#/sec] (mean) ๭ࣾͰ͸͜Ε͚ͩͰ1.5ഒ͘Β͍଎͘ͳͬͨͱ͍ ͏ࣄྫ΋ ϕϯν
  81. con g.middleware.delete 'Rack::ETag' con g.middleware.delete 'Rack::ConditionalGet' ͞Βʹ৭ʑ

  82. localhost, memcached, “hello”ͬͯݴ͏͚ͩͷ ΞϓϦ before Requests per second: 381.99 [#/sec]

    (mean) after Requests per second: 410.48 [#/sec] (mean) ϕϯν
  83. APαʔόʔͰ͜ͷ͋ͨΓͷΩϟογϡΛѻΘͳ͍ ݶΓ͸ෆཁͳ͸ͣ…ͩͱࢥ͏Μ͚ͩͲͲ͏ͳΜͩ Ζ͏ʁ ফ͚ͩ͢Ͱ଎͘ͳͬͨʂ

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

  85. commit 29acc17 Author: David Heinemeier Hansson <david@loudthinking.com> Date: 2010-06-09 16:19:03

    -0400 Cut down on tasks shown in rake -T DHHʮrake -Tݟʹ͘͘Ͷʁʯ
  86. Rake.application.tasks ϩʔυ͞ΕͯΔTaskͷίϨΫγϣϯ lib/tasks/t.rake task :t do puts *Rake.application.tasks.map(&:name) end

  87. % rake -T | wc -l 32 % rake t

    | wc -l 103 (102 + 1)
  88. % 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
  89. migrationͷ࡞੒΍࣮ߦΛ͍͍ͪͪखଧͪ͢Δͷͬ ͯΊΜͲ͍͘͞͠ɺ໊લͷ෇͚ํͷن໿ͳΜ͔΋ ֮͑ͯΒΕͳ͍Ͱ͢ΑͶɻ migrationΛ GUIͰ࣮ߦ͢Δ 009

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

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

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

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

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

    ໊લ͕μα͍ Ҿ਺͕μα͍
  95. active_decorator Gem le gem ‘active_decorator’ % bundle % rails g

    decorator user create app/decorators/user_decorator.rb
  96. 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 %>
  97. ؔ࿈ઌΛ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 %>
  98. ARelͷΦϒδΣΫτΛ Rubyͷίʔυʹม׵͢Δ ωλͰ͢ɻ 011

  99. AR::Relation#arel > Post.scoped.arel => #<Arel::SelectManager:0x007fac81ce7478 @ast= #<Arel::Nodes::SelectStatement:0x007fac81ce7428 @cores= [#<Arel::Nodes::SelectCore:0x007fac81ce73d8 @groups=[],

    @having=nil, @projections= [#<struct Arel::Attributes::Attribute relation= #<Arel::Table:0x007fac81aadc20 @aliases=[], @columns=nil, @engine= Post(id: integer, blog_id: integer, title: string, created_at: datetime, updated_at: datetime), @name="posts", ...
  100. 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'
  101. 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)
  102. arel_ruby https://github.com/amatsuda/arel_ruby

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

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

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

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

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

  108. None
  109. None
  110. None
  111. None
  112. ୙఺ɾ൒୙఺͕ผͷจࣈʹ Unicodeਖ਼نԽͷ᠘

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

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

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

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

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

  119. جຊతͳ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
  120. isolated Engine lib/foo/engine.rb module Foo class Engine < ::Rails::Engine isolate_namespace

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

  122. 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)
  123. ൚༻తͳEngine͕Ͱ͖ͨΒ gem push͠·͠ΐ͏ʂ

  124. 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 ͢Δ
  125. end

  126. ֎఻ ??? Sapporo RubyKaigi 2012