Динамическая перезапись / добавление ассоциации ActiveRecord с использованием одноэлементного класса - PullRequest
2 голосов
/ 21 мая 2009

Бизнес-логика такова: пользователи находятся в лодке через таблицу соединений, я думаю, давайте назовем эту модель Билетом. Но когда пользовательский экземпляр хочет проверить, кто еще находится на лодке, возникает условие, которое спрашивает, имеет ли этот пользователь разрешение видеть всех на лодке или только определенных людей на лодке. Если пользователь может видеть всех, нормальная сделка в порядке: some_user.boats.first.users возвращает всех пользователей с билетом на эту лодку. Но для некоторых пользователей единственными людьми, которые находятся на лодке (насколько они обеспокоены), являются люди, скажем, в столовой. Поэтому, если билет пользователя «помечен» (с использованием системы в стиле acts_as_taggable) с «Столовая», единственными пользователями, которые вернулись из some_user.boats.first.users, должны быть пользователи с билетами, помеченными «Столовая».

Просто для протокола: я не пытаюсь придумать что-то безумное с точки зрения getgo - я пытаюсь втиснуть эту произвольную группировку в (в основном) существующую систему. Итак, мы получили:

class User
  has_many :tickets
  has_many :boats, :through => :tickets
end

class Ticket
  belongs_to :user
  belongs_to :boat
end

class Boat
  has_many :tickets
  has_many :users, :through => :tickets
end

Изначально я думал, что могу условно изменить виртуальный класс, например:

singleton = class << a_user_instance ; self ; end
singleton.class_eval(<<-code
  has_many :tickets, :include => :tags, :conditions => ['tags.id in (?)', [#{tag_ids.to_s(:db)}]]
code
)

Это все сводится к генерации SQL, но при генерации генерирует SQL, заканчивающийся на:

LEFT OUTER JOIN "tags" ON ("tags"."id" = "taggings"."tag_id") WHERE ("tickets"._id = 1069416589 AND (tags.id in (5001,4502)))

Я пробовал копаться в коде ActiveRecord, но я не могу найти нигде, где бы префикс этого 'id' в SQL выше был с подчеркиванием. Я знаю, что ассоциации загружаются при загрузке класса ActiveRecord, и я предполагаю то же самое с одноэлементным классом. пожав плечами .

Я также использовал alias_method_chain как:

singleton = class << a_user_instance ; self ; end
singleton.class_eval(<<-code
  def tickets_with_tag_filtering
    tags = Tag.find(etc, etc)
    tickets_without_tag_filtering.scoped(:include => :tags, :conditions => {:'tags.id' => tags})
  end
  alias_method_chain :tickets, :tag_filtering
code
)

Но в то время как при таком подходе создаются желаемые Билеты, любые объединения в этих билетах используют условия в классе, а не в виртуальном классе. some_user.boats.first.users возвращает всех пользователей.

Любой тип комментария будет оценен, особенно если я использую неправильное дерево с таким подходом. Спасибо!

Ответы [ 4 ]

2 голосов
/ 05 июня 2009

Итак, дикая догадка о вашей проблеме подчеркивания заключается в том, что Rails генерирует код ассоциации на основе контекста во время оценки. Нахождение в одноместном классе может испортить это, вот так:

"#{owner.table_name}.#{association.class.name}_id = #{association.id}"

Вы могли бы войти туда и определить свойство имени класса для вашего синглтон-класса и посмотреть, решит ли это проблему.

В целом, я не рекомендую это. Это создает поведение, которое мучительно для отслеживания и невозможно эффективно расширять. Он создает мину в кодовой базе, которая в дальнейшем ранит вас или кого-то, кого вы любите.

Вместо этого рассмотрите возможность использования объявления named_scope:

class User
   has_many :taggings, :through => :tickets

   named_scope :visible_to, lambda { |looking_user|
      { :include => [ :tickets, :taggings ], 
        :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
      }
    }
end

Хотя вам, возможно, придется вернуться и отредактировать некоторый код, это гораздо более гибкий способ использования:

Boat.last.users.visible_to( current_user )

Понятно, что на находку наложено ограничение, и какова цель этого ограничения. Поскольку условия динамически вычисляются во время выполнения, вы можете справиться со следующей странной модификацией, с которой вас поразит ваш клиент. Скажите, что у некоторых из них есть рентгеновское зрение и ясновидение:

class User
   named_scope :visible_to, lambda { |looking_user|
      if looking_user.superhuman?
        {}
      else
        { :include => [ :tickets, :taggings ], 
          :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
        }
      end
    }
end

Возвращая пустой хеш, вы можете эффективно аннулировать эффект области действия.

0 голосов
/ 07 июня 2009

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

Спящие собаки, которые лгут, возвращаются, чтобы сильно укусить вас, по моему опыту. Обычно в форме будущего разработчика, который не знает, что ваша ассоциация является «волшебной», и использует ее, предполагая, что это всего лишь поручни. У него / нее, вероятно, даже не будет причины написать контрольный пример, который бы также раскрыл поведение, что повышает шансы, которые вы узнаете об ошибке, только когда она работает, а клиент недоволен. Это действительно стоит того времени, которое вы экономите сейчас?

Остинфромбостин указывает путь. Разная семантика? Разные имена Правило номер один - всегда писать код, который говорит о том, что он делает, настолько четко, насколько это возможно. Все остальное - это путь безумия.

0 голосов
/ 28 мая 2009

Какую версию Rails вы используете? Вы пытались обновить, чтобы увидеть, исправлена ​​ли проблема с подчеркиванием? Как будто он не может найти внешний ключ для вставки как «tag_id» или что-то в этом роде.

Мой ruby-fu ограничен, поэтому я не уверен, как динамически включать правильные параметры метода во время выполнения.

Просто, чтобы помочь вам прояснить, вам нужно беспокоиться об этих двух местах. Вы хотите отфильтровать видимых пользователей, чтобы они видели только пользователей с одинаковыми тегами. Ваша структура:

пользователь <-> билеты <-> лодки <-> билеты <-> пользователи

... верно?

Итак, вам нужно отфильтровать оба набора билетов до тех, которые имеют теги current_user.

Может быть, вам просто нужен метод current_user.viewable_users (), а затем фильтровать все через это? Я не уверен, какую существующую функциональность вы должны сохранить.

Blech, я не чувствую, что помогаю тебе вообще. К сожалению.

0 голосов
/ 22 мая 2009

Почему бы просто не захватить всех пользователей на лодке и не включить их теги.

Затем запустите быстрый фильтр, чтобы включить и вернуть только пользователей с тем же тегом, что и у запрашивающего пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...