3

I have a little bit of a complex model and want to get the full functionality capable with my limited understanding of rails.

I have a section, a header (which uses acts_as_tree), and an item.

I use json to bring sets of data in. This has worked very well. I would like to be able to bring attributes in for whole sets of data such as 'is_shippable'. I would like to be able to specify anywhere in the tree the is_shippable value and set to true. Also, I would like to be able to override at the header or item level to set it to false.

I have decided that it makes sense to have is_shippable as an attribute on the section, header, and item and try to use a before_create callback to determine whether it should be is_shippable.

For example:

section
  header -acts_as_tree
    item - is_shippable

sample json:

{
 "name":"sample section",
 "is_shippable": true,
 "headers_attributes":[
   {
    "name":"sample_section"
    "items_attributes":[{
      "name":"sample item",
      "is_shippable":false,
           }
    ]       
   }
 ]

}

in header.rb

before_save :default_values
private 
def default_values
  self.is_shippable ||=self.section.is_shippable
  # need to be able to set header to is_shippable=false if specified explicitly at that level    
end

in item.rb

before_save :default_values


private 
def default_values
  # if not set, default to 0
  self.is_shippable ||= 0
  self.is_shippable=1 if self.header.is_shippable==true
  # need to be able to set item to is_shippable=false if specified explicitly at that level
end

Is there a better way to do this than I am doing? How would I execute in if statements checking if is_shippable is set to false if it has been set to true higher in the hierarchy?

EDIT - also there are more features that is_shippable like is_fragile, is_custom_size etc...

Cœur
  • 37,241
  • 25
  • 195
  • 267
timpone
  • 19,235
  • 36
  • 121
  • 211

2 Answers2

1

I would be more inclined to use a before_filter in the controller to modify the nested item params.

something like:

before_filter :set_is_shippable, :only => [:update, :create]

def set_is_shippable
  is_shippable = params[:section][:is_shippable]
  params[:section][:items_attributes].each_with_index do |item, index|
    unless item[:is_shippable]
      params[:section][:items_attributes][index][:is_shippable] = is_shippable
    end
  end
end
Aaron Renoir
  • 4,283
  • 1
  • 39
  • 61
  • seeds are used for seeding the db, not consuming json. If you are only seeding the data once, why not preprocess the json to have the correct data. If you are doing this often you should build a parsing model. – Aaron Renoir Feb 27 '12 at 18:59
  • seed data is json. not an external resource - no need for what you're discussing – timpone Feb 28 '12 at 10:39
0

I highly recommend the ancestry gem. It is has many more tree traversal methods, and also optimizes the number of queries to the database.

If I understand your dilemma correctly, ancestry would allow you to do something such as:

@section.descendants.all?(&:is_shippable)

In any case, ancestry is much more expressive and will certainly give you greater flexibility. The github wiki linked below for the gem is the best I've ever seen. Very well organized, perhaps perusing it will give you some more ideas.

https://github.com/stefankroes/ancestry

http://railscasts.com/episodes/262-trees-with-ancestry

kwarrick
  • 5,930
  • 2
  • 26
  • 22
  • thx - we use ancestry for our messaging system. Parts of it I really like AT THE APPLICATION level but we're so hands-on on the db in this model, that we want to use acts_as_tree as the model is more straightforward. But we could have made a mistake on this – timpone Mar 01 '12 at 03:59