Rails 3 - Создание форм из сериализированных данных - PullRequest
2 голосов
/ 03 августа 2011

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

Я действовал инстинктивно и пытался с ожидаемым поведением. Это было бы использовать что-то вроде этого:

f.check_box :permissions_customer_club_events_read

Но поскольку для сериализованных данных не существует геттеров или сеттеров, это не сработает (очевидно: p). Теперь мне интересно, как бы я решил эту проблему, и единственное, что приходит на ум, это динамически генерировать методы получения и установки из моего сериализованного хэша.

Пример:

def permissions_customer_club_events_read=(val)
  permissions[:customer][:club][:events][:read] = val
end

def permissions_customer_club_events_read
  permissions[:customer][:club][:events][:read]
end

Кто-нибудь понимает, к чему я клоню?

Вот моя Модель :

class User::Affiliation::Role < ActiveRecord::Base
  require 'yajl'

  class YajlCoder
    def dump data
      Yajl.dump data
    end
    def load data
      return unless data
      Yajl.load data
    end
  end

  serialize :permissions, YajlCoder.new

  after_initialize :init

  def init
    ## Sets base permission structure ##
    self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
  end

end

Ответы [ 3 ]

1 голос
/ 14 сентября 2011

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

Похоже, вы знаете, каковы все ваши разрешения, но вы хотите сериализовать их все в одном поле базы данных.Но в вашем реальном приложении rails вы хотите обрабатывать все ваши разрешения, как если бы они были полностью отдельными полями.Это именно то, что позволит вам сделать решение типа attr_bucket.Давайте возьмем ваш пример, вы бы сделали что-то вроде этого:

class User::Affiliation::Role < ActiveRecord::Base
  attr_bucket :permissions => [:permissions_customer_club_events_read, :permissions_customer_club_events_write, :permission_do_crazy_things]

  after_initialize :init

  def init
    ## Sets base permission structure ##
    self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
  end
end

Теперь вы сможете использовать permissions_customer_club_events_read, permissions_customer_club_events_write, permission_do_crazy_things, как если бы они были отдельными полями базы данных (это включает использованиеих в формах и т. д.), но когда вы на самом деле сохраняете свои объекты, все эти поля объединяются и сериализуются в поле :permissions.

Единственное предостережение - механизм сериализации, я полагаю attr_bucket будет сериализовать все, используя YAML, тогда как вы использовали JSON.Если это не имеет значения, то вы золотой, в противном случае вам может потребоваться установить патч attr_bucket, чтобы использовать json вместо YAML, что должно быть довольно простым.

0 голосов
/ 15 сентября 2011

В конце концов это сработало для меня, вот как я это решил.


serialize :permissions, YajlCoder.new

after_initialize :init

def init
  self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")['customer']
  build_attributes_from self.permissions, :permissions
end

private

def build_attributes_from store, prefix, path=[]
  store.each do |k,v|
    if v.class == Hash
      build_attributes_from v, prefix, ( path + [k] )
    else
      create_attr_accessors_from prefix, ( path + [k] )
    end
  end
end

def create_attr_accessors_from prefix, path=[]
  method_name = prefix.to_s + "_" + path.join('_')
  class << self
    self
  end.send :define_method, method_name do
    self.permissions.dig(:path => path)
  end
  class << self
    self
  end.send :define_method, "#{method_name}=" do |value|
    self.permissions.dig(:path => path, :value => value)
  end
end

И какая-то обезьяна, исправляющая хэши ...

class Hash
  def dig(args={})

    path  = args[:path].to_enum   ||  []
    value = args[:value]          ||  nil

    if value == nil
      path.inject(self) do |location, key|
        location.respond_to?(:keys) ? location[key] : nil
      end
    else
      path.inject(self) do |location, key|
        location[key] = ( location[key].class == Hash ) ? location[key] : value
      end
    end
  end
end

ТеперьМетоды getter и setter генерируются для всех сериализованных полей.

0 голосов
/ 14 сентября 2011

Извините, если я не понял вопроса;)

Вы можете иметь модуль пользовательских данных, включенный в вашу модель, и использовать method_missing:

module CustomData
    def self.included(base)
        base.instance_eval do
        after_save        :save_data
    end

    def method_missing(method, *args, &block)
        if method.to_s =~ /^data_/
            data[method] ? data[method] : nil
        else
            super
        end
     end

      def data
          @data ||= begin
            #get and return your data
          end
      end

      private

      def save_data

      end
end

При использовании этого метода вам придется использовать f.check_box :data_permissions_customer_club_events_read

Это не совсем завершено, но я надеюсь, вы поняли идею;) attr_bucket тоже кажется хорошим решением.

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