Папярэдні занятак

Active Record, rake

Active Record гэта object-relational mapping бібліятэка, або проста ORM. З апісаньня Active Record: “Active Record злучае класы з табліцамі ў рэляцыйнай базе дадзеных каб усталяваць слой мадэляў які патрабуе амаль нулявую канфігурацыю. Бібліятэка прадастаўляе клас, пры ўспадкаваньні ад якога, новаму класу надаецца сувязь паміж ім і існай табліце ў базе дадзеных”. Замест таго каб пісаць SQL запыт для звяртаньня да базы дадзеных, Вы працуеце ў тэрмінах аб’ектаў. Давайце паглядзім як гэта будзе працаваць. Створым прыкладаньне ToDo зноў.

Спачатку нам трэба ўсталяваць Active Record. $ gem install activerecord.

Зараз усталюйце гем які завецца rake. Rake гэта прылада якая выкарыстоўваецца для выкананьня аднаразовых заданьняз; У Active Record ёсьць заданьні для стварэньня, выдаленьня й зьмяненьня баз дадзеных. З дапамогай Active Record, зьмены ў Вашай базе дадазеных робяцца міграцыямі (migrations), пра якія мы даведаемся трохі пазьней.

Active Record гэта частка Ruby on Rails фрэймўорка для будаваньня Вэб прыкладаньняў. Active Records амаль працуе без Rails, але яго міграцыі й іншыя заданьні злучаны з Rails. Каб карыстацца імі бяз Rails, нам будзе патрэбен гем які завецца standalone_migrations. Усталюйце яго.

Давайце створым праект to_do. У яго будзе звычайная структура плюс дадатковая тэчка db.

Калі Вы выконваеце rake, ён шукае ў Вашае тэчцы файл з назвай Rakefile дзе вызначаюцца заданьні. Стварыце файл Rakefile у галоўнай тэчцы праекта й дадайце туды:

require'standalone_migrations'StandaloneMigrations::Tasks.load_tasks

Стварэньне й выдаленьне базы дадзеных

Першая рэч якую мы павінны зрабіць з Active Record гэта сказаць яму дзе знаходзіцца база дадзеных. У тэчцы db, стварыце файл config.yml і ўнутар яго дадайце:

development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
    timeout: 5000

Мы сканфігуравалі нашу базу дадзеных каб яна карысталася sqlite3. Зараз давайце выканаем першы таск каб стварыць базу дадзеных:

  • rake db:create

Вы можаце заўважыць што ў тэчцы db зьявіўся новы файл. Файл вашай базы дадзеных.

Калі Вы хочаце выдаліць базу дадзеных, проста выканайце $ rake db:drop.

Стварэньне міграцыі

Дадайце дададзім табліцу tasksу нашу базу дадзеных:

  • rake db:new_migration name=create_tasks
      create  db/migrate/20121229023145_create_tasks.rb

Active Record створыць тэчку migrate у тэчцы db з файлам, які мае назву Вашае міграцыі. Active Record таксама дадае бягучы час у назву файла, каб ведаць у якім парадку выконваць міграцыі.

Адчыніце сваю міграцыю. Яна павінна выглядаць прыкладна так:

classCreateTasks<ActiveRecord::Migrationdef up
  enddef down
  endend

Зьмяніце яе на:

classCreateTasks<ActiveRecord::Migrationdef up
    create_table :tasks do|t|
      t.column :name,:string
      t.column :done,:boolean

      t.timestamps
    endenddef down
    drop_table :tasks
  endend

Калі мы выканаем гэтую міграцыю, яна створыць табліцу з назвай tasks. У гэтай таблічцы будзе калёнка name тыпа varchar (аналаг string у Рубі) і калёнку done тыпа boolean. t.timestamps кажа стварыць дзьве калёнкі created_at і updated_at якія Active Record будзе аўтаматычна запаўняць часам стварэньня й часам зьмяньня запіса адпаведна. У рэшце Active Record створыць поле id з аўтаінкрыментам.

Выкананьне міграцыі

Давайце выканаем міграцыю:

  • rake db:migrate

Паглядзіце што зьмянілася ў базе дадзеных. Адчыніце яе й выканайце:

  • .scheme

Праверце тэчку db; у ёй зьявіўся файл schema.rb. Адчыніце яго – гэта файл канфігурацыі Active Record які адлюстроўвае бягучую схему базы дадзеных. Ён выкарыстоўваецца каб з нуля стварыць схему базу дадзеных. Зьвярніце ўвагу на тое як Active Record сочыць за тым якія міграцыі былі выкананы – :version => 20121229023145 code.

Прыгажосьць міграцыяў у тым што мы можам па крокам зьмяняць стан нашай базы дадзеных. Зараз усе распрацоўшчыкі, якія працуюць над праектам змогуць карыстацца самае апошняй версіяй базы дадзеных, калі Вы абнавіце праект на Github.

Active Record дазваляе рабіць шмат чаго з база дадзеных і вельмі спрашчае работу зь ёй. У нашых занятках мы разглядаем толькі зусім не маленькую частку Active Record. Выдатнейшая крыніцы сучасных ведаў па Rails (у нашым выпадку Active Record) гэта Ruby on Rails Guides. Праглядзіце сэкцыю migrations; ня трэба ўсё чытаць, проста пазнаёмцеся з тым, якія ёсьць магчымасьці. Калі Вы працуеце над праектам і ня памятаеце як напісаць міграцыю, карыстайцеся Rails Guides.

Усталёўваньне сувязі з базай дадзеных

Давайце будзем выкарыстоўваць інтэрфэйс з мінулай праграмы Todo. Вось код трохі зьмянённы для працы з Active Record:

require'active_record'require'./lib/task'

database_configurations = YAML::load(File.open('./db/config.yml'))
development_configuation = database_configurations["development"]ActiveRecord::Base.establish_connection(development_configuation)def welcome
  puts "Welcome to the To Do list!"
  menu
enddef menu
  choice =niluntil choice =='e'
    puts "Press 'a' to add a task, 'l' to list your tasks, or 'd' to mark a task as done."
    puts "Press 'e' to exit."
    choice = gets.chomp
    case choice
    when'a'
      add
    when'l'
      list
    when'd'
      mark_done
    when'e'exitelse
      invalid
    endendenddef add
  puts "What do you need to do?"
  task_name = gets.chomp
  task =Task.new(:name => task_name,:done=>false)
  task.save
  "'#{task_name}' has been added to your To Do list."enddef list
  puts "Here is everything you need to do:"
  tasks =Task.all
  tasks.each {|task| puts task.name}end

welcome

Зверху мы адчынілі файл db/config.yml і прачыталі яго, вымаючы хэш з канфігурацыяй для базы дадзеных, і выкарысталі яго для канфігураваньня сувязі Active Record з базай дадзеных. Потым у мэтадзе add мы зьмянілі аргумэнт Task.new на хэш, з сымбаламі ў якасьцямі калёнак і значэньнямі як значэньнямі ў табліцы.

Успадкоўваньне ад класаў Active Record

Зараз калі мы маем табліцу й базу дадзеных, давайце створым мадэдь. Стварыце файл у тэчцы lib з назвай task.rb і надрукуйце наступны код:

classTask<ActiveRecord::Baseend

Усе… Выканайце інтэрфэйс і паспрабуйце дадаць і адлюстраваць усе заданьні. Усе працуе! Магія напэўна.

Зьмяненьне запісаў

Давайце напішам інтэрфэйс каб адзначыць заданьне выкананым:

def mark_done
  puts "Which of these tasks would you like to mark as done?"Task.all.each {|task| puts task.name}

  done_task_name = gets.chomp
  done_task =Task.where(:name => done_task_name).first
  done_task.update_attributes(:done=>true)end

Вы напэўна здагадаліся што ActiveRecord ужо мае ўсе гэтыя мэтады ў класе Task. Сур’ёзна. Нам больш нічога ня трэба рабіць з мадэльлю. Гэты код проста працуе.

Каб абанавіць запіс у базе дадзеных, update_attributes прымае хэш гэтак жа як і мэтад new.

Валідацыя

Зараз мы можам дадаць заданьне з пустым імём. Гэта дрэнна. Давайце пазьбегнем гэтага. Спачатку зьменім інтэрфэйс карыстальніка:

def add
  puts "What do you need to do?"
  task_name = gets.chomp
  task =Task.new(:name => task_name,:done=>false)if task.save
    puts "'#{task_name}' has been added to your To Do list."else
    puts "That wasn't a valid task."endend

Потым адчыніце task.rb і надрукуйце:

classTask<ActiveRecord::Base
  validates :name,:presence =>trueend

Усе працуе.

Як і міграцыі, валідацыя – вельмі моцная рэч. Таму варта пачытаць яе дакумэнтацыю ў Rails Guides.

Стварыце валідацыю каб імя таска было ўнікальным і прынамсі мела даўжыню ў два сымбала.

Памылкі

Калі заданьне няправільнае мы павінны казаць пра гэта карыстальніку:

if task.save
  puts "'#{task_name}' has been added to your To Do list."else
  puts "That wasn't a valid task:"
  task.errors.full_messages.each {|message| puts message}end

Зноў, нам не патрэбна рабіць зьмены ў нашае мадэлі; Active Record робіць усё сам.

Пачытайце пра памылкі Active Record на Rails Guide.

Callbacks (Зваротны выклік)

Часам нам трэба каб наша мадэль выканала якія-небудзь дзеяньні пасьля якога-небудзь этапа яе жыцьця, напрыклад нармалізацыя дадзеных перад захаваньнем. Тупы прыклад, але давайце захаваем усе нашыя заданьні маленькімі літарамі:

classTask<ActiveRecord::Base
  before_save :downcase_name

  privatedef downcase_name
    self.name =self.name.downcase
  endend

Мы дадалі мэтад downcase_name пасьля ключавога слова private. Усе мэтады якія дадаюцца пасьля private завуцца прыватнымі. Яны бачны толькі самому аб’екту й нікому больш. Нікто звонку класа ня можа выклікаць гэтыя мэтады. У нашым выпадку downcase_name выкарыстоўваецца толькі для зваротных выклікаў, таму па дэфолту мы заўсёды павінны ствараць прыватныя мэтады й рабіць іх усеагульна даступны толькі калі ёсьць такая патрэба.

Зноў пра зваротныя выклікі. Як заўсёды трэба пачытаць Rails Guide :)

Адносіны

Давайце дадзім новую ўласьцівасьць у нашае прыкладаньне – магчымасьць стварыць сьпісы заданьняў. Самі стварыць інтэрфэйс карыстальніка а вось пакуль код мадэлі.

Звычайна калі я ствараю новую мадэль я ўспадкоўваю яе ад Active Record, потым ствараю міграцыю. Зрабіце ўсё гэта для класа List а потым дадайце гэта код у міграцыю:

require'./lib/list'require'./lib/task'classCreateLists<ActiveRecord::Migrationdef change
    create_table :lists do|t|
      t.column :name,:string
      t.timestamps
    end
    default_list =List.create(:name =>'default list')

    add_column :tasks,:list_id,:integer
    Task.all.each {|task| task.update_attributes(:list_id => default_list.id)}endend

Звярніце ўвагу на тое, што я магу дадаваць звычайны код Ruby у міграцыі. У дадзеным выпадку я хачу каб кожнае заданьне належала да якога-небудзь сьпіса, таму трэба стварыць які-небудзь сьпіс, да якога будуць належаць існыя заданьні.

Зараз калі мы ўжо маем табліцу са сьпісам нам трэба сказаць Active Record пра адносіны якія маюць гэтыя дзьве мадэлі:

class Task < ActiveRecord::Base
  belongs_to :list
end

Праглядзіце дакумэнтацыю па Active Record associations.

Вось як праглядзець усе заданьні якія належаць да сьпіса:

list =List.find(1)
list.tasks

Вы можаце стварыць заданьне вось так Task.new(:name => "my task", :list_id => 1), але ёсьць іншая форма, якая дазваляе стварыць яго адразу ў сьпісе:

list =List.find(1)
list.tasks.new(:name =>"my task")

Выманьне дадзеных

Зараз мы адлюстроўваем усе заданьні з дапамогай мэтада Task.all. Але нам трэба адлюстраваць заданьні, якія яшчэ трэба выканаць; давайце пачнем з інтэрфэйса:

def list
  puts "Here is everything you need to do:"Task.not_done.each {|task| puts task.name}end

Зараз у файл task.rb дадайце :

classTask<ActiveRecord::Base
  scope :not_done,where("done = false")end

scope – гэта мэтад які дазваляе стварыць прадвызначанныя запыты да базы дадзеных.

Мэтад where прымае SQL накштал часткі WHERE у запыце. Але лепей выкарыстоўваць хэшы:

classTask<ActiveRecord::Base
  scope :not_done,where(:done=>false)end

Зьмяніце not_done на выкарыстоўваньне сінтаксіса з хэшамі.

Зараз стварыце мэтад Task.done.

Як звычайна пачытайце гайд Rails па запытам.

Наступны занятак