From: "Jesús Gabriel y Galán" Date: 2011-05-05T23:59:56+09:00 Subject: Re: Creating variables on an OpenStruct with dynamic names On Thu, May 5, 2011 at 4:19 PM, Jolyon R. wrote: > I think I was averse to creating multiple classes in one file, so > thought it best to try and create the CsvRow inline, it had crossed my > mind but I got hung up on the semantics of the book asking for an > object. > > I create a CsvRow class and am now getting an each to return a CsvRow, > AWESOME! > > The next bit I'm getting a little caught up on is how to get CsvRow to > return a single value in a row based on a headers value. > > When I store a line in my CsvRow like this: > > @fields = line.chomp.split(', ') > > Is that an array or hash? You can read about String#split in the documentation and you will see that it returns an array. > > Using method_missing and it's an array will this work: > > class CsvRow >  attr_reader :fields, :headersPattern Convention in Ruby is snake_case, so headers_pattern is more typical. >  def initialize(line) >    @fields = line.chomp.split(', ') >  end > >  def headerPattern( pattern ) >    @headersPattern = pattern >  end Who is calling this method? If you use attr_accessor instead of reader, you get a method called header_pattern=, which sets the value, as you are doing, so that would be more convenient. Also, you are rewriting the method headerPattern created by attr_reader with this one. So, remove it and use attr_accessor :header_pattern. >  def self.method_missing name, *args >    @headersPattern.each_with_index |header, index| >      if header = name.to_s yield @fields.send(@headers[index]) >    end >  end > end method_missing is an instance method, so drop the self there. And I think that you want to access the corresponding field of the header in the @fields array, so no need to send anything, just: def method_missing name, *args @header_pattern.each_with_index do |header, index| if header == name.to_s @fields[index] break end end Which could be also written as: def method_missing name, *args if index = @header_pattern.index name @fields[index] end end Which will find the name in the header_pattern, if it finds it indexes the @fields array at that index. Anyway, if the numbers of headers could be big, or the number of times you are going to call this method is relevant, I would think of having a different data structure. I would have a hash whose keys are the header names, and whose values are what you have in @fields. If possible, I would pass the headers when you construct the CsvRow, I think it's cleaner: class CsvRow attr_reader :header_pattern, :values end > > Thanks again for your help, it really makes a difference knowing a > language has a community of helpful people behind it :) > > -- > Posted via http://www.ruby-forum.com/. > >