views:

150

answers:

3

In the database I have a field named 'body' that has an XML in it. The method I created in the model looks like this:

def self.get_personal_data_module(person_id) 
    person_module = find_by_person_id(person_id) 
    item_module = Hpricot(person_module.body) 
    personal_info = Array.new 
    personal_info = {:studies => (item_module/"studies").inner_html, 
                            :birth_place => (item_module/"birth_place").inner_html, 
                            :marrital_status => (item_module/"marrital_status").inner_html} 
    return personal_info 
end

I want the function to return an object instead of an array. So I can use Module.studies instead of Model[:studies].

Thank you for your time.
Silviu

p.s I am new to rails and ruby. I have a C background.

+2  A: 

This is relatively simple; you're getting an Array because the code is building one. If you wanted to return an object, you'd do something like this:

class PersonalData
  attr_accessor :studies
  attr_accessor :birth_place
  attr_accessor :marital_status

  def initialize(studies,birth_place,marital_status)
    @studies = studies
    @birth_place = birth_place
    @marital_status = marital_status
  end
end

And your translation code would look like:

def self.get_personal_data_module(person_id) 
  person_module = find_by_person_id(person_id) 
  item_module = Hpricot(person_module.body) 
  personal_info = PersonalData.new((item_module/"studies").inner_html,
                                   (item_module/"birth_place").inner_html,
                                   (item_module/"marital_status").innner_html)
  return personal_info 
end
Atiaxi
Works like a charm ;)
Silviu Postavaru
+2  A: 

Or, if you want to avoid a model class, you could do something weird:

class Hash
  def to_obj
    self.inject(Object.new) do |obj, ary| # ary is [:key, "value"]
      obj.instance_variable_set("@#{ary[0]}", ary[1])
      class << obj; self; end.instance_eval do # do this on obj's metaclass
        attr_reader ary[0].to_sym # add getter method for this ivar
      end
      obj # return obj for next iteration
    end
  end
end

Then:

h = {:foo => "bar", :baz => "wibble"}
o = h.to_obj # => #<Object:0x30bf38 @foo="bar", @baz="wibble">
o.foo # => "bar"
o.baz # => "wibble"

It's like magic!

jtbandes
Any cases you can think of when it'd be logically better to use this method than the answer's method?
junkforce
For these type of solutions I am starting to love Ruby
Silviu Postavaru
+1  A: 

on a slightly different tack.

The idea of using a class method to do this feels wrong from an OO point of view.

You should really refactor this so that it works from an instance method.

  def personal_data_module
    item_module = Hpricot(body) 
    {
      :studies => (item_module/"studies").inner_html, 
      :birth_place => (item_module/"birth_place").inner_html, 
      :marrital_status => (item_module/"marrital_status").inner_html
    }
  end

Then, where you need to use it, instead of doing....

Foobar.get_personal_data_module(the_id)

you would do

Foobar.find_by_person_id(the_id).personal_data_module

This looks worse, but in fact, thats a bit artificial, normally, you would be referencing this from some other object, where in fact you would have a 'handle' on the person object, so would not have to construct it yourself.

For instance, if you have another class, where you reference person_id as a foreign key, you would have

class Organisation belongs_to :person end

then, where you have an organisation, you could go

organisation.person.personal_information_module

Yes, I know, that breaks demeter, so it would be better to wrap it in a delegate

class Organisation
  belongs_to :person

  def personal_info_module
    person.personal_info_module
  end
end

And then from controller code, you could just say

organisation.personal_info_module

without worrying about where it comes from at all.

This is because a 'personal_data_module' is really an attribute of that class, not something to be accessed through a class method.

But this also brings up some questions, for instance, is person_id the primary key of this table? is this a legacy situation where the primary key of the table is not called 'id'?

If this is the case, have you told ActiveRecord about this or do you have to use 'find_by_person_id' all over where you would really want to write 'find'?

fatgeekuk
http://stackoverflow.com/questions/158482/inject-data-members-to-an-object - The next related question. As I have said I am new to Ruby and I am experimenting right now. Thanks for the inpu; it is really helpful.
Silviu Postavaru