Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

before

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

begin

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

ϝιουΛੜ΍ͨ͠ΓͰ͖Δ 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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

=begin PR about us

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

4VO4FQ

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

You can paste both text snippet and picture.

Slide 32

Slide 32 text

=end

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

ϙϦϞϧϑΟοΫΞιγΤʔγϣϯ͸ɺ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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

͍͍ͩͨͰ͖ͨ > 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))

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

΋ͬͱίϯύΫτʹॻ͖͍ͨ! 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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

> 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

Slide 47

Slide 47 text

> 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Ͱ͸ෆཁ

Slide 48

Slide 48 text

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')

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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ͦͷ΋ͷ

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Twisting SQLite3 (@tenderlove)

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

IO Hooks

Slide 62

Slide 62 text

Step 2: Write our Code

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Anything can store a SQLite Database

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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__

Slide 69

Slide 69 text

Step 3: Profit?

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

% 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Λ֬ೝ

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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ݟʹ͘͘Ͷʁʯ

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

% 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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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 %>

Slide 97

Slide 97 text

ؔ࿈ઌΛ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 %>

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

AR::Relation#arel > Post.scoped.arel => #

Slide 100

Slide 100 text

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'

Slide 101

Slide 101 text

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)

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

No content

Slide 111

Slide 111 text

No content

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

Rails4ϨγϐϒοΫʁ ʁʁʁ

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

جຊతͳ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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

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)

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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 ͢Δ

Slide 125

Slide 125 text

end

Slide 126

Slide 126 text

֎఻ ??? Sapporo RubyKaigi 2012