Outils pour utilisateurs

Outils du site


langages:ruby:rails

Conventions

  • noms des contrôleurs (classes) au pluriel (sans quoi on risque quelques surprises au niveau des routes et de ses helpers)
  • idem pour le répertoire des vues puisque le nom est basé sur celui du contrôleur
  • noms des modèles (classe) au singulier
  • (à compléter)

Grouper des opérations au sein d'une unique et même transaction

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

PostgreSQL comme SGBD

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

Imbrication de layouts

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.

Exécuter une tâche Rake en cron (avec RVM)

  1. Au préalable, générer la tâche myTask :
    (bootup_)rails generate task myTask
  2. "Emballer" le script rails pour qu'il soit exécuté avec les version, implémentation voulue de ruby et le jeu de gemmes par :
    rvm wrapper `rvm current` bootup rake
  3. Ajouter la tâche (crontab -e) :
    ... cd /path/de/l/application && $HOME/.rvm/bin/bootup_rake myTask:action RAILS_ENV=[production|development]

Précompiler les assets pour le passage en production (avec RVM)

RAILS_ENV=production (bootup_)rake assets:precompile

Avoir un modèle qui n'est pas lié à la base de données

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"
langages/ruby/rails.txt · Dernière modification: 08/12/2014 16:28 (modification externe)