Archive for rails
26/08/09 12:38 · Publié dans rails, Web Dev
La solution dans l’article précédent ne me plaisait pas, alors j’ai fais ma solution … bon j’avoue j’ai pas cherché s’il existe déjà une solution prête à l’emploi.
Personnellement, je préfère que config. reload_plugins soit à false. Mais je ne veux pas mettre dans mes plugins un bout de code qui dépend plus de l’application et du contexte (development/production) que du plugin lui même.
Je pourrais mettre reload_plugin à true, oui mais je n’ai pas forcément envie de recharger toute la masse de plugin à chaque requête.
Dans un premier temps, on va ajouter une directive de configuration à savoir reloadable_plugins, et on va la traiter au moment du chargement des plugins. Voici le code :
Rails::Configuration.class_eval do
attr_accessor :reloadable_plugins
def initialize_with_reloadable_plugins
initialize_without_reloadable_plugins
self.reloadable_plugins = default_reloadable_plugins
end
def default_reloadable_plugins
[]
end
alias_method_chain :initialize, :reloadable_plugins
end
Rails::Plugin::Loader.class_eval do
def add_plugin_load_paths_with_reloadable_plugins
load_path = add_plugin_load_paths_without_reloadable_plugins
plugins.each do |plugin|
if configuration.reloadable_plugins.include?(plugin.name.to_sym)
ActiveSupport::Dependencies.load_once_paths -= plugin.load_paths
end
end
load_path
end
alias_method_chain :add_plugin_load_paths, :reloadable_plugins
end
qu’on va mettre dans lib/configuration_reloadable_plugins.rb par exemple.
Ensuite ajouter dans le fichier config/environnement.rb, juste après le boot.rb (l’ordre est important)
...
require File.join(File.dirname(__FILE__), 'boot')
require File.join(File.dirname(__FILE__), '../vendor/plugins/engines/boot')
require 'lib/configuration_reloadable_plugins'
...
Et toujours dans config/environnement.rb on indique les plugins à recharger :
...
Rails::Initializer.run do |config|
config.reloadable_plugins = [ :foo, :bar ]
config.reload_plugins = false
...
end
...
Et pour vérifier que c’est bon :
$ script/console
>> ActiveSupport::Dependencies.load_once_paths.select{ |p| p =~ /foo/ }
=> []
Commentaires
25/08/09 20:24 · Publié dans rails, Web Dev
Les problèmes avec rails 2.3 continuent.
J’utilise depuis longtemps le plugin rails-engines qui permet de transformer une application en un plugin.
Et nouveauté de la version 2.3 de rails, RailsEngine y a été incorporé. Mais comme certains plugins qui ont été incorporé (has_finder, globalize, …) à chaque fois ce n’est pas complètement, bref il faut quand compléter les fonctionnalités en installant le plugin engine expurgé du code commun avec rails.
Mais bon ça bug encore, on obtient un :
A copy of X has been removed from the module tree but is still active!
ou bien un
can’t dup NilClass
uniquement en mode dev … grrr
Alors heureusement, d’autres sont passé par là et certains ont déjà résolu le problème en ajoutant un unloadable dans la classe concernée
class Toto
unloadable
end
That’s it !
UPDATE :
En lisant les commentaires en fait ce n’est pas la bonne solution. En fait il faut lire la doc, enfin la doc dans le code :) dans railties/lib/initializer.rb :
# Enables or disables plugin reloading. You can get around this setting per plugin.
# If reload_plugins? is false, add this to your plugin's init.rb
# to make it reloadable:
#
# ActiveSupport::Dependencies.load_once_paths.delete lib_path
#
# If reload_plugins? is true, add this to your plugin's init.rb
# to only load it once:
#
# ActiveSupport::Dependencies.load_once_paths << lib_path
#
C’est clair ! soit d’office vous ne voulez pas du tout recharger les plugins (reload_plugin à false), et seulement certains (ceux que vous développez par exemple) alors il faut mettre dans le init.rb :
# config/environments/development.rb
config.reload_plugins = false
# plugin's init.rb : remove plugin from unloadable list
ActiveSupport::Dependencies.load_once_paths -= load_paths
soit l’inverse vous voulez que tous les modules soient rechargeable (reload_plugins à true), sauf certains :
# config/environments/development.rb
config.reload_plugins = true
# plugin's init.rb : add plugin in unloadable list
ActiveSupport::Dependencies.load_once_paths += load_paths
UPDATE 2
ou alors encore mieux voir l’article sur l’extension pour rails
Commentaires (1)
25/08/09 20:11 · Publié dans rails, Web Dev
Ah les joies de se mettre à jour et son code à l’occasion … Oui depuis longtemps je trainais avec rails 1.2.6 (oui je sais ça commence à dater mais j’ai mes raisons). Il me tardait donc de pouvoir utiliser les nouvelles versions de rails avec son lots d’améliorations.
Bon tout d’abord grosse surprise un bug uniquement présent en development, et pour cause il concerne le système de déchargement/rechargement du code de l’application.
ça donne soit un
stack level too deep
ou alors un pénible
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:142:in `create_time_zone_conversion_attribute?'
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:75:in `define_attribute_methods'
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:71:in `each'
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:71:in `define_attribute_methods'
vendor/rails/activerecord/lib/active_record/attribute_methods.rb:242:in `method_missing'
Le problème semble être valable depuis rails 2.2 et toujours pas corrigé. Il semble que ce ne soit pas une priorité car le problème se pose uniquement en mode development.
Donc la solution est de patcher rails en attendant. Patch qui se trouve dans le ticket chez lighthouseapp :
if ENV['RAILS_ENV'] != 'production'
class ActiveRecord::Base
class_eval do
def self.reset_subclasses
nonreloadables = []
subclasses.each do |klass|
unless ActiveSupport::Dependencies.autoloaded? klass
nonreloadables << klass
next
end
klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
klass.instance_methods(false).each { |m| klass.send(:undef_method, m) unless m =~ /^id(=$|\?$|$)/ }
end
@@subclasses = {}
nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
end
end
end
end
Voilà et maintenant ça roule on peut développer en paix.
Commentaires
20/08/09 10:01 · Publié dans rails, Web Dev
Dans rails, on trouve des pépites et with_scope en est une. Une vieille pépite et toujours pas finie, je serais tenté de dire.
Par exemple, il ne tient pas compte de la clause :order.
De mon côté, j’ai besoin d’un ordre précis pour faire le merge de mes jointures.
Prenons l’exemple suivant :
class Dumb
class << self
def foo_bar
with_scope(:find => { :join => "INNER JOIN bar ON bar.id=foo.bar_id"}) do
want_foo
end
end
def want_foo
with_scope(:find => { :join => "INNER JOIN foo ON foo.id=dump.foo_id"}) do
# do your stuff
...
end
end
end
end
... INNER JOIN bar ON bar.id=foo.bar_id INNER JOIN foo ON foo.id=dump.foo_id ...
... ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'foo.bar_id' ..
On souhaite avoir l’ordre inversé genre :
... INNER JOIN foo ON foo.id=dump.foo_id INNER JOIN bar ON bar.id=foo.bar_id ...
Voici la solution :
...
do_reverse_merge_joins do
def foo_bar
with_scope(:find => { :join => "INNER JOIN bar ON bar.id=foo.bar_id"}) do
want_foo
end
end
end
...
et le patch ou l’extension comme vous voulez (testé pour rails 2.3)
ActiveRecord::Base.class_eval do
class << self
def merge_joins_with_reverse(*joins)
merge_joins_without_reverse(reverse_merge_joins? ? joins.reverse : joins)
end
alias_method_chain :merge_joins, :reverse
def do_reverse_merge_joins(&block)
set_reverse_merge_joins
result = yield
reset_reverse_merge_joins
result
end
def reset_reverse_merge_joins
set_reverse_merge_joins(nil)
end
def set_reverse_merge_joins(value = true)
Thread.current[:"#{self}_reverse_merge_joins"] = value
end
def reverse_merge_joins?
Thread.current[:"#{self}_reverse_merge_joins"]
end
end
end
Commentaires