9

I use ancestry to make a tree of goals. I would like to send the contents of that tree to the browser using json.

My controller is like this:

@goals = Goal.arrange
respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @goals }
  format.json { render :json =>  @goals}
end

When I open the json file, I get this output:

{"#<Goal:0x7f8664332088>":{"#<Goal:0x7f86643313b8>":{"#<Goal:0x7f8664331048>":{"#<Goal:0x7f8664330c10>":{}},"#<Goal:0x7f8664330e68>":{}},"#<Goal:0x7f86643311b0>":{}},"#<Goal:0x7f8664331f70>":{},"#<Goal:0x7f8664331d18>":{},"#<Goal:0x7f8664331bd8>":{},"#<Goal:0x7f8664331a20>":{},"#<Goal:0x7f86643318e0>":{},"#<Goal:0x7f8664331750>":{},"#<Goal:0x7f8664331548>":{"#<Goal:0x7f8664330aa8>":{}}}

How can I render the contents of the Goal-objects in the json file?

I have tried this:

@goals.map! {|goal| {:id => goal.id.to_s}

but it doesn't work, since @goals is an ordered hash.

Johan Hovda
  • 855
  • 2
  • 10
  • 23
  • 1
    If you format code as code (indent by 4 spaces, or surround with backticks [`\``]) you don't have to randomly remove `<` and `>`s. http://stackoverflow.com/editing-help – Matt Ball Mar 30 '12 at 13:35

3 Answers3

11

I got some help from user tejo at https://github.com/stefankroes/ancestry/issues/82.

The solution is to put this method in the goal model:

def self.json_tree(nodes)
    nodes.map do |node, sub_nodes|
      {:name => node.name, :id => node.id, :children => Goal.json_tree(sub_nodes).compact}
    end
end

and then make the controller look like this:

@goals = Goal.arrange
respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @goals }
  format.json { render :json =>  Goal.json_tree(@goals)}
end
tipycalFlow
  • 7,594
  • 4
  • 34
  • 45
Johan Hovda
  • 855
  • 2
  • 10
  • 23
  • 2
    It seem's like you don't need ancestry then – SET001 Mar 08 '13 at 10:33
  • 1
    am I the only one who gets the message in the logs? `N+1 Query detected Directory => [:children] Add to your finder: :includes => [:children] N+1 Query method call stack` I am using `closure_tree`. – cantonic Jul 24 '15 at 01:48
2

Inspired from this https://github.com/stefankroes/ancestry/wiki/arrange_as_array

def self.arrange_as_json(options={}, hash=nil)
  hash ||= arrange(options)
  arr = []
  hash.each do |node, children|
    branch = {id: node.id, name: node.name}
    branch[:children] = arrange_as_json(options, children) unless children.empty?
    arr << branch
  end
  arr
end
Danny
  • 415
  • 4
  • 6
1

I ran into this problem the other day (ancestry 2.0.0). I modified Johan's answer for my needs. I have three models using ancestry, so it made sense to extend OrderedHash to add a as_json method instead of adding json_tree to three models.

Since this thread was so helpful, I thought I'd share this modification.

Set this up as a module or monkey patch for ActiveSupport::OrderedHash

def as_json(options = {})
    self.map do |k,v|
        x = k.as_json(options)
        x["children"] = v.as_json(options)
        x
    end
end

We call the model and use it's default json behavior. Not sure If I should call to_json or as_json. I've used as_json here and it works in my code.

In the controller

...
format.json { render :json => @goal.arrange}
...
The Hungry Dictator
  • 3,444
  • 5
  • 37
  • 53