0

I want to try the gem closure_tree to build a tree for my genealogy database. I installed the gem under Ruby MRI 2.3.0 on a windows 7 box. The versions of the dependencies are in the errorlog below.

$ gem install closure_tree
Fetching: with_advisory_lock-3.0.0.gem (100%)
Successfully installed with_advisory_lock-3.0.0
Fetching: closure_tree-6.2.0.gem (100%)
Successfully installed closure_tree-6.2.0
2 gems installed

When I try the most minimalistic code it fails, I suppose because I don't use Rails and don't do the migration which again I presume creates the table tag_hierachies

My question is: Can I use this gem without Rails and if so how ?

require 'active_record'
require 'closure_tree'

ActiveRecord::Base.establish_connection(
    :adapter => "sqlite3",
    :database  => "sample.db"
)

if !ActiveRecord::Base.connection.table_exists?('tags')
  ActiveRecord::Schema.define do
    create_table :tags do |table|
      table.column :name, :string
      table.column :parent_id, :integer
    end
  end
end

class Tag < ActiveRecord::Base
  has_closure_tree
end

grandparent = Tag.create(name: 'Grandparent')

gives

d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/sqlite3_adapter.rb:513:in `table_structure': Could not find table 'tag_hierarchies' (ActiveRecord::StatementInvalid)
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/sqlite3_adapter.rb:387:in `columns'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/schema_cache.rb:43:in `columns'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/attributes.rb:93:in `columns'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/attributes.rb:98:in `columns_hash'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/inheritance.rb:205:in `subclass_from_attributes?'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/inheritance.rb:54:in `new'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/persistence.rb:50:in `create!'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/hierarchy_maintenance.rb:65:in `block in rebuild!'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/support.rb:108:in `block (2 levels) in with_advisory_lock'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:211:in `transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:220:in `transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/support.rb:108:in `block in with_advisory_lock'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/with_advisory_lock-3.0.0/lib/with_advisory_lock/base.rb:77:in `yield_with_lock'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/with_advisory_lock-3.0.0/lib/with_advisory_lock/base.rb:65:in `yield_with_lock_and_timeout'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/with_advisory_lock-3.0.0/lib/with_advisory_lock/base.rb:48:in `with_advisory_lock_if_needed'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/with_advisory_lock-3.0.0/lib/with_advisory_lock/concern.rb:16:in `with_advisory_lock_result'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/with_advisory_lock-3.0.0/lib/with_advisory_lock/concern.rb:10:in `with_advisory_lock'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/support.rb:107:in `with_advisory_lock'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/hierarchy_maintenance.rb:63:in `rebuild!'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/closure_tree-6.2.0/lib/closure_tree/hierarchy_maintenance.rb:39:in `_ct_after_save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:432:in `block in make_lambda'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:228:in `block in halting_and_conditional'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:506:in `block in call'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:506:in `each'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:506:in `call'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:92:in `__run_callbacks__'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb:778:in `_run_save_callbacks'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/callbacks.rb:302:in `create_or_update'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/persistence.rb:120:in `save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/validations.rb:37:in `save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/attribute_methods/dirty.rb:21:in `save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:286:in `block (2 levels) in save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:351:in `block in with_transaction_returning_status'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `block in transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/abstract/transaction.rb:184:in `within_new_transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:220:in `transaction'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:348:in `with_transaction_returning_status'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:286:in `block in save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:301:in `rollback_active_record_state!'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/transactions.rb:285:in `save'
    from d:/Ruby/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/persistence.rb:34:in `create'
    from C:/Users/Gebruiker/BoxSync/ruby_werk/acts_as_tree/closure_tree.rb:22:in `<main>'
peter
  • 41,770
  • 5
  • 64
  • 108
  • found this article very usefull https://blog.openshift.com/tmp-part-2-using-clojure-tree-with-activerecord/ – peter Dec 30 '16 at 21:29

1 Answers1

3

Seems the installation instructions in README is focused with only Rails in mind. It does however tell you that you need another table(in addition to your current tags table) with the name tag_hierarchies.

In Rails, generation of this table seems to be part of the installation steps; Step 5 in the linked documentation.

So, you should be creating tag_hierarchies table with the following content, which is what rails g closure_tree:migration tag generates as far as the table structure is concerned. See create_hierarchies_table.rb.erb, as you may also want to add index as shown in this linked file.

So, updating your script to following worked as expected for me:

require 'active_record'                                                                                                
require 'closure_tree'                                                                                                 

ActiveRecord::Base.establish_connection(                                                                               
    :adapter => "sqlite3",                                                                                             
    :database  => "sample.db"                                                                                          
)                                                                                                                      

if !ActiveRecord::Base.connection.data_source_exists?('tags')                                                          
  ActiveRecord::Schema.define do                                                                                       
    create_table :tags do |table|                                                                                      
      table.column :name, :string                                                                                      
      table.column :parent_id, :integer                                                                                
    end                                                                                                                
  end                                                                                                                  
end                                                                                                                    

# You also need a corresponding model's table name followed by "_hierarchies" table.                                   
#                                                                                                                      
# Migration created based on                                                                                           
# `https://github.com/mceachen/closure_tree/blob/master/lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb`
if !ActiveRecord::Base.connection.data_source_exists?(:tag_hierarchies)                                                
  ActiveRecord::Schema.define do                                                                                       
    create_table :tag_hierarchies do |table|                                                                           
      table.integer :ancestor_id, null: false                                                                          
      table.integer :descendant_id, null: false                                                                        
      table.integer :generations, null: false                                                                          
    end                                                                                                                
  end                                                                                                                  
end                                                                                                                    

class Tag < ActiveRecord::Base                                                                                         
  has_closure_tree                                                                                                     
end                                                                                                                    

grandparent = Tag.create(name: 'Grandparent')                                                                          

grandparent.children.create(name: 'Father')                                                                            
grandparent.children.create(name: 'Uncle')                                                                             
vee
  • 38,255
  • 7
  • 74
  • 78
  • Works great, thnx vee – peter Dec 30 '16 at 21:16
  • 1
    Heya, author of ClosureTree here. Make sure you check out the tests, too, to see lots of example model setups. There's a gitter chatroom linked to the readme if you like more conversational solutions. – mrm Dec 31 '16 at 02:33
  • 1
    @mrm, thanks for point regarding test. One slight improvement if I may suggest in the `README.md` is to include a sample non-rails example. Also, I found the note on step 5 of installation a little confusing as it says "By default the table name will be the model's table name, followed by _hierarchies", but looks like the table name needs to be singular, e.g "tag_hierarchies" instead of "tags_hierarchies" for `Tag` model's `tags` table. – vee Dec 31 '16 at 19:10
  • Good idea, will do. – mrm Feb 18 '17 at 00:13
  • Why does the tag_hierarchy table is set with id: false, i.e no id column is created. Also can we add an id column to it with auto incrementing ids – Mahesh Mesta May 28 '20 at 06:35