简体   繁体   中英

How would I refactor this code to use procs?

The original code:

  def self.user_admin_links
    ADMIN_PAGES.inject([]) do |result, page|
      result << Page.new(controller: page[:name]) if page[:menu] && (page[:group_admin] || page[:company_admin])
      result
    end
  end

  def self.super_admin_links
    ADMIN_PAGES.inject([]) do |result, page|
      result << Page.new(controller: page[:name]) if page[:super_admin]
      result
    end
  end

I tried to refactor like this:

array_builder = Proc.new do |conditional|
    ADMIN_PAGES.inject([]) do |result, page|
      result << Page.new(controller: page[:name]) if conditional
      result
    end
  end

  def self.user_admin_links
    array_builder.call(page[:menu] && (page[:group_admin] || page[:company_admin]))
  end

  def self.super_admin_links
    array_builder.call(page[:super_admin])
  end

But I get this error:

Error: undefined local variable or method `array_builder' for Page:Class.

When I turned array_builder into a class method, like this:

  def self.array_builder 
    Proc.new do |conditional|
      ADMIN_PAGES.inject([]) do |result, hsh|
        result << Page.new(controller: hsh[:name]) if conditional
        result
      end
    end
  end

I got an error that in the self.user_admin_links method that "page" is not recognized.

In Ruby, methods are not closures. Ie, they cannot use local variables from the surrounding scope, such as array_builder .

And if you want page to be evaluated for every element of ADMIN_PAGES , you should use a block argument (which is basically just some syntax for passing procs as arguments). Otherwise it is just evaluated once in the links methods, where page is not defined.

def array_builder &conditional
  ADMIN_PAGES.inject([]) do |result, page|
    result << Page.new(controller: page[:name]) if conditional[page]
    result
  end
end

Also, here is a better Ruby idiom for the job. Instead of manually pushing to an array and using inject, use Enumerable#select and Enumerable#map :

def array_builder &conditional
  ADMIN_PAGES.select(&conditional).map {|page| Page.new(controller: page[:name])}
end

The other methods then pass a block:

def self.user_admin_links
  array_builder {|page| page[:menu] && (page[:group_admin] || page[:company_admin])}
end

def self.super_admin_links
  array_builder {|page| page[:super_admin]}
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM