Рубиновые константы во вложенных классах.Метапрограммирование - PullRequest
1 голос
/ 10 февраля 2011

Q: как делиться информацией во родительских классах о родительском классе

class Post
  MODEL = self
  extend PostMod
  has_many :comments, :class_name => 'Post::Comment'

  class Comment
    include CommentMod
    belongs_to :model, :class_name => "Post"
    def method_in_comment
      puts MODEL
    end
  end
  class Another

  end
end
module  CommentMod
  def method_in_mod1
    puts self.class::MODEL
  end
  def method_in_mod2
    puts MODEL
  end
end
module  PostMod
  def method_in_mod1
    puts self::Comment
  end
end
b = Post::Comment.new
b.method_in_comment # Post
b.method_in_mod1 # uninitialized constant Post::Comment::MODEL (NameError)
b.method_in_mod2 # uninitialized constant CommentMod::MODEL (NameError)

Причина такого дизайна, в данном примере (реальная система намного сложнее), состоит в том, чтобы добавлять комментарии к любой модели, просто«включить модуль».Это добавит контроллеры, представления и методы модели.

. Комментарии ведут себя одинаково для всех моделей.Но модель может переопределить любой метод в классе Comment, если нужно что-то настроить.

Хитрая часть в том, что модули ничего не знают о верхних классах (Post) и классах на одном уровне (Comment и Another),но им нужно вызвать некоторые методы класса для них.

Сейчас я использую синтаксический анализ class.name, чтобы получить имя класса верхнего уровня, но должны быть другие способы.

Любые предложения приветствуются, включая изменение дизайна.

ОБНОВЛЕНИЕ

Сообщения и комментарии являются лишь примером, у меня нет этих моделей в моемproject.

Я перехожу с обозначения подчеркивания (или CamelCase) на вложенные классы (из ArticleTranslation в Article :: Translation).Это выглядит более понятным для меня.В предыдущей версии я использовал имя модели для вызова методов класса в классах (в ModelTranslation и т. Д.) Теперь, после рефакторинга lib модулей, мне больше не нужно знать имя_модели.

Но я попал в ловушку:ruby, вы можете снова открыть классы, например

class Post < ActiveRecord::Base

end
class Post::Comment < ActiveRecord::Base
  belongs_to  :language
end

class Post::Comment::Another1 < ActiveRecord::Base

end

class Post::Comment::Another2 < ActiveRecord::Base

end

class Language < ActiveRecord::Base
  has_many :post_comments, :class_name => "Post::Comment"
end

И у меня есть проблема: если страница была загружена сразу после запуска сервера - все в порядке, но при следующих вызовах этой страницы выдается ошибка: нет такой ассоциации;вызов методов включенных модулей - ошибок метода нет.Я думаю, что rails загружает неверный файл для Post :: Coment, хуже всего то, что я не могу отладить эту ошибку ... но это другой вопрос.

UPDATE2

Вторая проблема решена.Проблема была в вспомогательных классах.

Ответы [ 3 ]

1 голос
/ 10 февраля 2011

Итак, исходя из ваших последующих комментариев, вот что-то более похожее на то, что вы используете после использования Module.extended:

module PostMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from PostMod#method_in_mod1: #{self.class.inspect}"
    end
  end
end

module CommentMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from CommentMod#method_in_mod1: #{self.class.inspect}"
    end

    def method_in_mod2
      puts "from CommentMod#method_in_mod2: #{self.class.ancestors.inspect}"
    end
  end
end

class Post
  extend PostMod

  class Comment
    extend CommentMod

    def method_in_comment
      puts "from Post::Comment#method_in_comment: #{self.class.inspect}"
    end
  end

  class Another

  end
end


b = Post::Comment.new
b.method_in_comment # Post::Comment
b.method_in_mod1 # Post::Comment
b.method_in_mod2 # [Post::Comment, CommentMod::InstanceMethods, Object, Kernel]

Хотя вывод не совсем так, как ваш пример. Может быть трудно делить переменные класса так, как вы пытаетесь, потому что вы фактически не используете наследование, просто вложенные классы.

Можете ли вы подробно рассказать о намерении поделиться этими переменными, возможно, есть лучший способ достичь цели?

1 голос
/ 11 февраля 2011

Я решил проблему самостоятельно путем рефакторинга кода. Так что разделение class.name больше не нужно. Если мне когда-нибудь понадобится это, лучше всего использовать class.name.scan или совпадения, чтобы получить имя класса верхнего уровня, затем изменить код и удалить class.name.scan =) Разделять имя класса было плохой идеей.

1 голос
/ 10 февраля 2011

Можете ли вы уточнить «добавить комментарии к любой модели, просто« включив модуль »».?Вы имеете в виду, что у вас есть комментарий к модели ActiveRecord, и вы хотите полиморфно связать его со многими другими моделями, такими как, например,

Post
  has_many :comments

Article
 has_many :comments

... и т.д.?

...