su - postgres postgres@albrecht:~$ createuser rails #Non Le nouveau rôle est-il super-utilisateur ? (o/n) n #Oui Le nouveau rôle est-il autorisé à créer des bases de données ? (o/n) o #Non Le nouveau rôle est-il autorisé à créer de nouveaux rôles ? (o/n) n postgres@albrecht:~$ alter user rails with password '<mot_de_passe>'ou :
su - postgres createuser -U postgres -W -P rails
cd <projet_rails> rake db:createCréation de toutes les B.D.D. (Dev., Test et Prod°) :
rake db:create:all
rake db:drop
rake db:reset=> aucune donnée non insérée par les migrations ne survit

#Commande est la classe du modèle commande @commande = Commande.find_by_id(19719) #@commande est une instance de cette classe en mémoire (un objet Ruby), #qui contient les valeurs (trouvées dans la B.D.D.) de la commande sélectionnée if @commande.prix_ttc.blank? @commande.prix_ttc = @commande.prix_ht * 1.196 end @commande.save
config.active_record.schema_format
db/${RAILS_ENV}_structure.sql
db/schema.rbMais ce type de schema ne peut pas exprimer les éléments spécifiques aux B.D.D. :
gem install annotate-models cd <projet> annotate annotate-models #will wipe out the comment block from the last run including all comments you add to the block
rake db:migrate --trace VERSION=<SSAAMMJJHHMMSS>Défait la(les) dernière(es) migrations
rake db:rollback STEP=<nb de migrations en arrière>Défait puis refait la(les) dernière(es) migration(s)
rake db:migrate:redo STEP=<3>Exécute le up ou down d'une migration particulière
rake db:migrate:{up, down} VERSION=<timestamp>
change_table :visites do |t| #ajout d'une colonne t.integer :duree end
change_table :<table> do |t| t.column t.index t.timestamps t.change t.change_default t.rename t.references t.belongs_to t.string t.text t.integer t.float t.decimal t.datetime t.timestamp t.time t.date t.binary t.boolean t.remove t.remove_references t.remove_belongs_to t.remove_index t.remove_timestamps end
:id => false
:primary_key
:options => "ENGINE=BLACKHOLE"defaut :
:options => "ENGINE=InnoDB"
drop_table :<nom_table>
add_column :products, :part_number, :string #ou avec la syntaxe 'sexy' t.<type> <nom_colonne> #soit dans l'exemple def self.up create_table :products do |t| t.string :part_number end end
change_table :nom_table do |t|
t.change :nom_colonne, :type_colonne, {options}
end
rename_column :<table> :<ancien_nom>, :<nouv_nom> #ou avec la syntaxe 'sexy' t.rename :<ancien_nom>, :<nouv_nom>
remove_column :<table> :<nom_col>, :<nom_col>, ... #ou avec la syntaxe 'sexy' t.remove :<nom_col>, :<nom_col>, ...
:null => false :default => 0
add_column :visites, :id, :primary_key
t.column :name, 'polygon', :null => false
add_index :accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party' t.index :<nom_index>
remove_index :<nom_indexe>
http://agilewebdevelopment.com/plugins/foreign_key_associations http://agilewebdevelopment.com/plugins/foreign_key_migrations
t.references :<modele>
#Cette classe utilise Employe ou Produit de façon polymorphique class Photo < ActiveRecord::Base belongs_to :imageable, :polymorphic => true end class Employe < ActiveRecord::Base has_many :photos, :as => :imageable end class Produit < ActiveRecord::Base has_many :photos, :as => :imageable endDans cet exemple, l'instruction :
t.references :imageable, :polymorphic => {:default => 'Photo'}
Ajoutera un colonne imageable_id et une colonne de type string imageable_type avec une valeur par défaut valant : 'Photo'.@picture.imageable @product.pictures
execute <<-SQL ALTER TABLE products ADD CONSTRAINT fk_products_categories FOREIGN KEY (category_id) REFERENCES categories(id) SQL
execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories"
raise ActiveRecord::IrreversibleMigration, "<message_explicatif>"
<Classe_modele>.reset_column_informationCela permet par exemple d'initialiser un colonne à partir de calculs, juste après son ajout à une table.
script/generate model <NomModel>Ont un nom de la forme :
SSAAMMJJHHMMSS_create_nom_model.rb
script/generate migrationSi le nom de la migration est de la forme :
"AddXXXToYYY"ou
"RemoveXXXFromYYY"et est suivi d'une liste de noms de colonnes avec leur type,
script/generate migration AddDetailsToProducts part_number:string price:decimal script/generate migration AddPositionToDiapositives position:integer
http://ar.rubyonrails.org/classes/Fixtures.html
test/fixturesATTENTION : ils ne sont pas forcément utilisés que pour les tests!
# 1) Charge une garniture depuis un fichier Ruby :
require 'active_record/fixtures'
Fixtures.create_fixtures("#{Rails.root}/test/fixtures", "operating_systems")
Fixtures.create_fixtures("#{Rails.root}/test/fixtures", "users")
# 2) Charge un fichier de garnitures dans une migration avec up and down :
#db/migrate/<fichier_de_migration>.rb
require 'active_record/fixtures'
class LoadCustomerData
def self.up
down
directory = File.join(File.dirname(__FILE__), "data")
Fixtures.create_fixtures(directory, "customers")
end
def self.down
Customer.delete_all
end
end
gem install fastercsv #ou rake gem:install fastercsv
#config/environment.rb :
config.gem 'fastercsv'
#dans le fichier de migration du projet :
FasterCSV.foreach("#{RAILS_ROOT}/fichier.csv") do |ligne|
titre = ligne[0]
next if titre == "Title"
end
db/seeds.rbUtilisation :
rake db:seedExemples :
#db/migrate/<fichier_de_migration>.rb # Chargement du fichier de seed depuis une migration Rake::Task["db:seed"].invoke
# db/seeds.rb
# 1) Charge un fichier texte depuis le fichier de Seed
require 'open-uri'
require 'active_record/fixtures'
Country.delete_all
open("http://openconcept.ca/sites/openconcept.ca/files/country_code_drupal_0.txt")
do |countries|
countries.read.each_line do |country|
code, name = country.chomp.split("|")
Country.create!(:name => name, :code => code)
end
end
# 2) génère automatiquement un grand nombre d'enregistrement
Debut=11
Fin=2000
puts "création de #{Fin - Debut + 1} photos, à partir de #{Debut}"
(Debut..Fin).each { |i|
if Photo.find_by_nom("photo_bis_#{i}").nil?
puts "création de photo_bis_#{i}"
Photo.create( :nom => "photo_bis_#{i}", :commentaire => "commentaire photo bis #{i}" )
else
puts "photo_bis_#{i} existe déjà"
end
}
# lib/tasks/dump_load.rake
namespace :db do
namespace :seed do
require 'db/seed_tables'
desc "décharge les tables contenant les données initiales dans db/<RAILS_ENV>_seed.sql. La variable SEED_TABLES doit-être définie dans db/seed_tables.rb!!!"
task :dump => :environment do
config = ActiveRecord::Base.configurations[RAILS_ENV]
dump_cmd = "mysqldump --user=#{config['username']} --password=#{config['password']} #{config['database']} #{SEED_TABLES.join(" ")} > db/#{RAILS_ENV}_seed.sql"
system(dump_cmd)
end
desc "charge les données initiales depuis db/<RAILS_ENV>_seed.sql dans la base courante"
task :load => :environment do
config = ActiveRecord::Base.configurations['test']
system("mysql --user=#{config['username']} --password=#{config['password']} #{config['database']} < db/#{RAILS_ENV}_seed.sql")
end
end
end
# lib/tasks/fixtures.rake
namespace :db do
namespace :fixtures do
desc 'Personnalized : Create YAML test fixtures from data in an existing database.
Defaults to development database. Set RAILS_ENV to override.'
task :dump => :environment do
puts "cette tâche apparaîtra dans (rake --tasks) du projet"
sql = "SELECT * FROM %s"
skip_tables = ["schema_migrations"]
ActiveRecord::Base.establish_connection(:development)
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
i = "000"
j = 0
File.open("#{RAILS_ROOT}/test/fixtures/#{table_name}.yml", 'w') do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
file.write data.inject({}) { |hash, record|
j += 1
hash["#{table_name.singularize}_#{i.succ!}"] = record
hash
}.to_yaml
puts "#{table_name} : [#{j}]"
end
end
end
end
end
RAILS_ENV=development rake db:fixtures:dump RAILS_ENV=production rake db:fixtures:load
#Dans une méthode contrôleur : ActiveRecord::Base.record_timestamps = false #temporarily turn off magic column updates @page.update_attribute(:position, pos) ActiveRecord::Base.record_timestamps = true #turning updates back on
#rails console _sql = "alter table categories drop column parent_id" ActiveRecord::Base.connection.execute(_sql)
<controlleur>/new
<controlleur>/<id>
<controlleur>/<id>/edit <input type="hidden" name="_method" value="put" />
<a href="<controller>/<id>" onclick="if (confirm('Es-tu sûr(e)?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input'); s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'arpS61rIZTta3ZSiNmmeO1G0NUb30szy0+7Qx9OQIHw='); f.appendChild(s);f.submit(); };return false;">Supprimer</a>
<a href="<controller>/<id>" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Supprimer</a>
<input type="hidden" name="_method" value="delete" />
#exemple : customer.orders(true).empty?
<classe_modele>.find_by_<nom_attribut>("<valeur_attribut>")
<classe_modele>.all
<sequence_de_modeles>.order(:<tri_selon_cet_attribut_>)Si l'on veut un tri sur un attribut virtuel (défini par une méthode dans le modèle) :
# nom_long est une méthode dynamique (à créer) du modèle (pas un attribut de la B.D.D.)
# notez le ! à la fin du sort, qui implique que la séquence elle-même est modifiée :
@all_categories.sort! { |a,b| a.nom_long <=> b.nom_long }
Reservation.find_all_by_date(Date.today, :include => [:evenement, :sessions])Attention : Les noms des relations en jointure doivent respecter le singulier ou pluriel du type de relation (C.f. le modèle).
Reservation.find_all_by_date(Date.today, :include => [{:evenement => :organisateur}, :sessions ])
Où : Un organisateur organise des évènements, qui ont plusieurs réservations, et elle-mêmes plusieurs sessions.@utilisateurs = Utilisateur.find(
:all,
:include => {
:communautes => { :localisation => {} } ,
:conditions => ["localisations.ville = ?", ville]
}
)
#Filtre sur la valeur d'une colonne :
named_scope :school_names, :conditions => {:requirement_type => Type::SCHOOL_NAMES}
named_scope :active, :conditions => {:active => true}
#Filtre avec bloc :
named_scope :recent,
lambda {
{
:conditions => ['created_at > ?', 1.week.ago]
}
}
#Utilisation, on peut les utiliser en cascade :
User.active.recent
Exemple complet :
class Article
named_scope :published_at_like, lambda {|date_at| { :conditions => ['published_at LIKE ? ', "%#{date_at}%"] }}
named_scope :user_id, lambda {|user_id| { :conditions => {:user_id => user_id} }
named_scope :published, { :conditions => {:published => true} }
named_scope :not_published, { :conditions => { :published => false} }
named_scope :category, lambda {|category_id| { :conditions => ['categorizations.category_id = ?', category_id], :include => 'categorizations' }}
named_scope :draft, { :conditions => {:state => :draft} }
named_scope :no_draft, {:conditions => ['state <> ?', 'draft'], :order => 'created_at DESC'}
named_scope :searchstring,
lambda {
|search_string|
tokens = search_string.split.collect {|c| "%#{c.downcase}%"}
{
:conditions => [(['(LOWER(body) LIKE ? OR LOWER(extended) LIKE ? OR LOWER(title) LIKE ?)']*tokens.size).join(' AND '),
*tokens.collect{ |token| [token] * 3 }.flatten]
}
}
def self.search_no_draft_paginate(search_hash, paginate_hash)
list_function = ["Article.no_draft"]
if search_hash.nil?
search_hash = {}
end
if search_hash[:searchstring]
list_function << 'searchstring(search_hash[:searchstring])'
end
if search_hash[:published_at] and %r{(\d\d\d\d)-(\d\d)} =~ search_hash[:published_at]
list_function << 'published_at_like(search_hash[:published_at])'
end
if search_hash[:user_id] && search_hash[:user_id].to_i > 0
list_function << 'user_id(search_hash[:user_id])'
end
if search_hash[:published]
list_function << 'published' if search_hash[:published].to_s == '1'
list_function << 'not_published' if search_hash[:published].to_s == '0'
end
if search_hash[:category] and search_hash[:category].to_i > 0
list_function << 'category(search_hash[:category])'
end
paginate_hash[:order] = 'created_at DESC'
list_function << "paginate(paginate_hash)"
eval(list_function.join('.'))
end
end
Pour savoir quelles sont les conditions résultantes d'une cascade de named scopes,
utilisez la méthode scope(:find) :
#Appliqué à l'exemple ci-dessus : Article.user_id.category.published.scope(:find)
>> p = Post.find(:first)
=> ...
>> p.title = "Another Title"
=> "Another Title"
>> p.changed?
=> true
>> p.changed
=> ["title"]
>> p.changes
=> {"title"=>["New Title", "Another Title"]}
>> p.title_changed?
=> true
>> p.title_was
=> "New Title"
>> p.title_change
=> ["New Title", "Another Title"]
>> p.title_will_change!
=> "Another Title"
has_many :<table_etrangere>, :through => :<table_intermediaire>
#models/employee.rb class Employee < ActiveRecord::Base has_many :subordinates, :class_name => 'Employee', :foreign_key => 'manager_id' belongs_to :manager, :class_name => 'Employee' end #controllers/employee_controller.rb @employee.subordinates @employee.manager
#models/produit.rb class Produit < ActiveRecord::Base has_and_belongs_to_many :categories end #models/categorie.rb #Attention aux inflexions sur cette classe !!! class Categorie < ActiveRecord::Base has_and_belongs_to_many :produits end #controllers/produit_controller.rb @produit.categories #controllers/categorie_controller.rb @categorie.produits
#exemple, models/produit.rb : has_and_belongs_to_many :categories, :join_table => "prods_cats"
belongs_to :follower, :class_name => "Membre", :foreign_key => "membres_follower"
module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, :class_name => "MyApplication::Billing::Account" end end module Billing class Account < ActiveRecord::Base belongs_to :supplier, :class_name => "MyApplication::Business::Supplier" end end end
class Project has_and_belongs_to_many :developers, :after_add => :evaluate_velocity def evaluate_velocity(developer) ... end end
http://m.onkey.org/2010/1/22/active-record-query-interface
active_support/inflections.rb
#config/initializers/inflections.rb inflect.plural(<sing>, <plur>) inflect.singular(<plur>, <sing>) #en anglais category => categories inflect.irregular 'categorie', 'categories' #=> génère dans la liste des inflexions : (?i-mx:(c)ategorie$)\1ategories #Les instructions suivantes ne marchent pas : une règle générale prends le pas #inflect.singular 'categories', 'categorie' #inflect.plural 'categorie', 'categories'
'categories'.singularize
ActiveRecord::Base.connection.reset_pk_sequence!('<nom_table>')