Use Ruby closure_tree gem without rails

322 views Asked by At

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>'
1

There are 1 answers

5
vee On BEST ANSWER

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')