Si on admet une relation 1,1 ⇔ 1,n, type un "bidule" est composé de plusieurs "parties" :
class Bidule has_many :parties end class Partie belongs_to :bidule end
Et que l'on veut créer le tout en une fois, genre :
bidule = Bidule.new(attributes) bidule.save! # save! levant une exception si l'insertion échoue, la méthode save normale ne contentant que de retourner true/false until ... # une itération quelconque partie = bidule.parties.create(attributes) end
Par défaut, et c'est normal car RoR ne peut bien évidemment pas interpréter nos intentions, chacune des requêtes d'insertion (pour bidule et chaque partie) va être réalisée individuellement dans une transaction à part. Bien évidemment, si on veut quelque chose de cohérent (ie ne pas se retrouver avec moitié des insertions mais plutôt tout ou rien), c'est le tout qui doit être exécuté dans une unique et même transaction. Ce que l'on peut aisément réaliser en encapsulant le code en question comme bloc de la méthode transaction. Peu de choses à changer donc, le code devient tout simplement :
Bidule.transaction do bidule = Bidule.new(attributes) bidule.save! until ... partie = bidule.parties.create(attributes) end end
Particularité essentielle non des moindres pour ceux qui, comme moi, seraient habitués de MySQL : il n'y a pas "nativement" d'insensibilité à la casse. Avec MySQL, il faut avouer que c'est facile/inné : on colle un interclassement case insensitive (suffixe _ci) à notre colonne et point barre. Rien à gérer nul part, même au niveau de Rails. Pour PostgreSQL, c'est complètement différent, à commencer par la validation d'unicité (uniqueness) où vous devez indiquer qu'il ne faut pas tenir compte de la casse puisque, par défaut, c'est le cas (ce qu'on ne pourrait lui reprocher) :
validates :colonne, uniqueness: { case_sensitive: false }
Ainsi, la requête derrière pour valider passe de (qui va passer à moins de planter en ayant mis un index par sécurité) :
SELECT 1 AS one FROM TABLE WHERE TABLE.colonne = valeur LIMIT 1
A :
SELECT 1 AS one FROM TABLE WHERE LOWER(TABLE.colonne) = LOWER(valeur) LIMIT 1
(en ayant bien sûr l'index qui va bien côté SGBD - CREATE UNIQUE INDEX <nom> ON <table>(LOWER(<colonne>));
)
Autre solution : passer par l'extension citext et remplacer les colonnes concernées du type VARCHAR au type CITEXT :
-- CREATE EXTENSION citext; DROP INDEX <nom>; ALTER TABLE <table> ALTER COLUMN <colonne> TYPE CITEXT; CREATE UNIQUE INDEX ON <table>(<colonne>);
Pour que le layout actif par rapport à l'action, en invoque un autre, par exemple 'application' (celui par défaut), il suffit de mettre dans le fichier du premier :
<%= render template: 'layouts/application' %>
Ceci permet par exemple de définir des blocs (content_for) par défaut communs sur une partie de votre application.
(bootup_)rails generate task myTask
rvm wrapper `rvm current` bootup rake
... cd /path/de/l/application && $HOME/.rvm/bin/bootup_rake myTask:action RAILS_ENV=[production|development]
RAILS_ENV=production (bootup_)rake assets:precompile
C'est parfaitement possible tout en étant assez simple et proche de ce que l'on réalise avec ActiveRecord (ce qui est normal puisque ActiveRecord "hérite" en coulisse d'ActiveModel). Créer, comme d'habitude, un fichier app/models/<nom>.rb. Par contre, niveau contenu, la classe ne sera la fille de personne (elle n'hérite pas d'ActiveRecord::Base) et il va falloir inclure des modules. Exemple :
class Recherche include ActiveModel::Model # équivaut à #include ActiveModel::Validations # pour la validation #include ActiveModel::Conversion extend ActiveModel::Naming extend ActiveModel::Translation # pour la traduction attr_accessor :dans, :requete # les attributs, ce qu'on aurait eu comme colonne si c'était en bdd # ajouter une validation end r = Recherche.new(requete: 'php & utf-8', dans: %w[articles commentaires]) r.requete # => 'php & utf-8' r.dans # => [ 'articles', 'commentaires' ]
Par contre, pour la traduction des noms des attributs (fichiers config/locales/*.yml), prendre garde à utiliser la partie activemodel au lieu de activerecord :
activemodel: errors: <<: *errors attributes: recherche: dans: "Parmi" requete: "Texte à rechercher"