Use find to initialize a constant?

前端 未结 3 2045
终归单人心
终归单人心 2021-01-28 09:28

Something like this:

class Category

   SOME_CATEGORY = find_by_name(\"some category\")

end

Category::SOME_CATEGORY
tried wi

相关标签:
3条回答
  • 2021-01-28 09:46

    What do you want to do?

    Maybe:

    class Category
      def self.some_category
        Category.find_by_name("some category")
      end
    end
    

    So you can call:

    Category.some_category
    => <Category#2....>
    
    0 讨论(0)
  • 2021-01-28 10:10

    If you don't want to hit the database each time you'll have to cache the model. There are several ways to do this, but one quick way is using Memoization. This was introduced in Rails 2.2.

    class Category < ActiveRecord::Base
      class << self
        extend ActiveSupport::Memoizable
        def named(name)
          find_by_name(name)
        end
        memoize :named
      end
    end
    

    Use it like this.

    Category.named("some category") # hits the database
    Category.named("some category") # doesn't hit the database
    

    The cache should stay persistent across requests. You can reset the cache by passing true as the last parameter.

    Category.named("some category", true) # force hitting the database
    
    0 讨论(0)
  • 2021-01-28 10:11

    It's not a terrible idea, but it's not really a good one either. It doesn't really fall in line with the way Rails does things. For one thing, you'll end up with a lot of ugly constant code. Too many ALL_CAPS_WORDS and your Ruby starts to look like C++. Bleah.

    For another, it's inflexible. Are you going to make one of these constants for every category? If you add a new category two months from now, will you remember to update your Rails code, add a new constant, redeploy it and restart your server?

    If it's important to you to be able to access categories very easily, and not repeat DB queries, here's a bit of metaprogramming that'll automatically look them up and create static methods like Lichtamberg's for you on first access:

    def self.method_missing(category, *args)  # The 'self' makes this a class method
      @categories ||= {}
      if (@categories[category] = find_by_name(category.to_s))
        class_eval "def self.#{category.to_s}; @categories[#{category}]; end" 
        return @categories[category]
      end
      super
    end
    

    With this method in place, whenever you first call Category.ham, it'll create a class method that returns the value of find_by_name("ham") -- so that neither the query nor method_missing() runs again the next time you call it. This is pretty much the way the OpenStruct class works, BTW; look it up in the Pickaxe book if you want to learn more.

    (Of course you'll still have the risk that, because these are all memoized, your Rails app won't reflect any changes you make to your category objects. This makes the assumption that changes won't happen or don't really matter. It's up to you to determine whether that assumption is valid for your app. You could always put an after_update callback in your code that resets @@categories if that's a problem; but at that point this starts to get complicated.)

    0 讨论(0)
提交回复
热议问题