0

Structure of ruby file is as shown below,

market
  |__abcd
       |__classification
              |__for_sale_mobile_phone.rb
              |__catalogues
                  |__mobile_phone_brands
                      |__acer.rb
                      |__apple.rb
                      |__samsung.rb

In for_sale_mobile_phone.rb, I like to include all brands of mobile_phone_brands under a block.

I m trying to include brands in below fashion,

 .....
    c.tree_field :brand, { child_key: :model } do |b|
      Dir[
        File.dirname(__FILE__) + '/catalogues/mobile_phone_brands/*.rb'
      ].each { |brand| load brand }

      b.required = true
    end
.....

here is how brand file looks like. for example, apple.rb

b.value "apple" do |brand|
  brand.value "6plus"
  brand.value "6s"
  brand.value "6s-plus"
  brand.value "se"
  brand.value "7"
  brand.value "7-plus"
  brand.value "other-model"
end

I m getting below error,

undefined local variable or method `b' on line 1: apple.rb

How can I include file under a block scope?

Thanks in advance!

Arun G
  • 1,678
  • 15
  • 17

2 Answers2

1

You should separate file "loading" from function execution. Redefine your file like this.

class AppleLoader

  def self.load(b)
    b.value "apple" do |brand|
      brand.value "6plus"
      brand.value "6s"
      brand.value "6s-plus"
      brand.value "se"
      brand.value "7"
      brand.value "7-plus"
      brand.value "other-model"
    end
  end
end

On top of file you load required classes like this:

require '/catalogues/mobile_phone_brands/apple_loader.rb'

And when you want to load Apple brand in the b object:

AppleLoader.load b

Better Approach: To me it feels like Apple.rb defers from Samsung.rb only in terms of data. If that's the case, and functionality for both of them are same then I would rather:

  1. put that data in a Yaml file(brands.yml) instead of rb file.

        brands:
         apple: ["6plus", "6s"]
         samsung: ["galaxy"]
    
  2. Have just one common loader called BrandLoader

    class BrandLoader
    
      def self.load(b, brand_name, values)
        b.value brand_name do |brand|
          brand_values.each do |value|
            brand.value value
          end
        end
      end
    end
    
  3. Iterate over brands by reading Yaml

    configurations = YAML.load "brands.yml"
    configurations["brands"].each do |brand_name, values|
      BrandLoader.load(b, brand_name, values)
    end
    
Vijay Agrawal
  • 1,643
  • 12
  • 17
  • Thanks for your effort, But I figured it out – Arun G Jul 27 '17 at 08:21
  • Agarwal: Can give upvote for your attempt, Which works perfectly but solution below I figured out make it simple – Arun G Jul 27 '17 at 08:29
  • I had a look at it, though it works but not a recommended way of doing things in ruby. When you have performance and maintainability concerns, you should switch – Vijay Agrawal Jul 27 '17 at 15:40
0

The way I figured out is by using eval but cautious read up is-eval-supposed-to-be-nasty, eval works for me as I'm not using user input.

To make any code in different file, execute as the code written in the place of call. Use eval(File.read(file_path)

 .....
    c.tree_field :brand, { child_key: :model } do |b|
      Dir[
        File.dirname(__FILE__) + '/catalogues/mobile_phone_brands/*.rb'
      ].each { |brand| eval(File.read(brand)) }

      b.required = true
    end
.....
Arun G
  • 1,678
  • 15
  • 17