acts_as_many_trees 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2e3aaaa29d8492c978bc86b0b61b9cb50eb29c14
4
+ data.tar.gz: 360dfdbedf00f63acb234ebb692104dee7940c83
5
+ SHA512:
6
+ metadata.gz: ebf6c139330b618a017e6254ea52f033e769fff0c49326081ccc3562ca38b6fa5f07a3fa15caedb2bbbcc6c4870140a256d1ccc53ba7db5cd2113bc1aa7ab874
7
+ data.tar.gz: 643a394d45595339c8e48e8a645f96a997773fc3d104293d366bd06c10ba4728ccd789d6a10bc391dab447e2e790407e708c3f44ce1fc51c1107b2a7e7475703
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = ActsAsManyTrees
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActsAsManyTrees'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+ Bundler::GemHelper.install_tasks
17
+
18
+ begin
19
+ require 'rspec/core/rake_task'
20
+
21
+ RSpec::Core::RakeTask.new(:spec)
22
+
23
+ if defined?(RSpec)
24
+ desc 'Run factory specs.'
25
+ RSpec::Core::RakeTask.new(:factory_specs) do |t|
26
+ t.pattern = './spec/factories_spec.rb'
27
+ end
28
+ end
29
+
30
+ # task spec: :factory_specs
31
+ task :default => :spec
32
+ #http://erniemiller.org/
33
+ desc 'run the console'
34
+ task :console do
35
+ require 'irb'
36
+ require 'irb/completion'
37
+ require 'lib/acts_as_many_trees'
38
+ ARGV.clear
39
+ IRB.start
40
+ end
41
+ rescue LoadError
42
+ # no rspec available
43
+ end
44
+
45
+
@@ -0,0 +1,75 @@
1
+ module ActsAsManyTrees
2
+ module Base
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_many_trees(options = {})
10
+ class_attribute :hierarchy_class
11
+ self.hierarchy_class = (name+'Hierarchy').constantize
12
+ include ActsAsManyTrees::InstanceMethods
13
+ extend ActsAsManyTrees::ClassMethods
14
+ hierarchy_class.send :include,ActsAsManyTrees::HierarchyTable
15
+ end
16
+ end
17
+
18
+ end
19
+ module ClassMethods
20
+ def hierarchy_table_name
21
+ hierarchy_class.table_name
22
+ end
23
+ end
24
+ module InstanceMethods
25
+ extend ActiveSupport::Concern
26
+ included do
27
+ has_many :unscoped_descendant_links,
28
+ class_name:hierarchy_class.to_s,
29
+ foreign_key: 'ancestor_id',
30
+ dependent: :delete_all,
31
+ inverse_of: :unscoped_ancestor
32
+
33
+ has_many :unscoped_ancestor_links,
34
+ class_name: hierarchy_class.to_s,
35
+ foreign_key: 'descendant_id',
36
+ dependent: :delete_all,
37
+ inverse_of: :unscoped_descendant
38
+ has_many :unscoped_ancestors,through: :unscoped_ancestor_links
39
+ has_many :unscoped_descendants, through: :unscoped_descendant_links
40
+ scope :roots , ->(hierarchy=''){
41
+ on = Arel::Nodes::On.new(Arel::Nodes::Equality.new(arel_table[:id],hierarchy_class.arel_table[:descendant_id])
42
+ .and(hierarchy_class.arel_table[:hierarchy_scope].eq(hierarchy))
43
+ )
44
+ outer_join = Arel::Nodes::OuterJoin.new(hierarchy_class.arel_table,on)
45
+ joins(outer_join).merge(hierarchy_class.where(ancestor_id: nil))
46
+ }
47
+ end
48
+ delegate :hierarchy_class, to: :class
49
+ def parent=(new_parent,hierarchy_scope='')
50
+ hierarchy_class.set_parent_of(self,new_parent,hierarchy_scope)
51
+ end
52
+
53
+ def set_parent(new_parent,hierarchy_scope='')
54
+ hierarchy_class.set_parent_of(self,new_parent,hierarchy_scope)
55
+ end
56
+
57
+ def parent(hierarchy_scope='')
58
+ ancestors(hierarchy_scope).where('generation=1').first
59
+ end
60
+
61
+ def children(hierarchy_scope='')
62
+ descendants(hierarchy_scope).where('generation=1')
63
+ end
64
+
65
+ def ancestors(hierarchy='')
66
+ unscoped_ancestors.merge(hierarchy_class.scope_hierarchy(hierarchy))
67
+ end
68
+
69
+ def descendants(hierarchy='')
70
+ unscoped_descendants.merge(hierarchy_class.scope_hierarchy(hierarchy))
71
+ end
72
+ end
73
+
74
+ end
75
+ ActiveRecord::Base.send :include, ActsAsManyTrees::Base
@@ -0,0 +1,91 @@
1
+
2
+ module ActsAsManyTrees
3
+ module HierarchyTable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :item_class_name
8
+ self.item_class_name = self.to_s.gsub('Hierarchy','')
9
+ class_attribute :item_class
10
+ self.item_class = item_class_name.constantize
11
+
12
+ belongs_to :unscoped_ancestor,
13
+ class_name: item_class_name,
14
+ foreign_key: 'ancestor_id',
15
+ inverse_of: :unscoped_descendant_links
16
+
17
+ belongs_to :unscoped_descendant,
18
+ class_name: item_class_name,
19
+ foreign_key: 'descendant_id',
20
+ inverse_of: :unscoped_ancestor_links
21
+
22
+ scope :scope_hierarchy,->(scope_hierarchy=''){ where hierarchy_scope: scope_hierarchy}
23
+ # select t1.* from item_trees t1 left outer join item_trees t2 on t1.ancestor_id = t2.descendant_id and t1.tree_scope = t2.tree_scope where t2.ancestor_id is null
24
+ scope :roots,->do
25
+ t1 = arel_table
26
+ t2 = arel_table.alias
27
+ t1.project(Arel::star).join(t2,Arel::Nodes::OuterJoin)
28
+ .on(t1[:ancestor_id]
29
+ .eq(t2[:descendant_id])
30
+ .and(t1[:hierarchy_scope].eq(t2[:hierarchy_scope])
31
+ )
32
+ )
33
+ .where(t2[:ancestor_id].eq(nil)
34
+ )
35
+
36
+ end
37
+
38
+ def self.set_parent_of(item,new_parent,hierarchy_scope='')
39
+ self.delete_ancestors(item,hierarchy_scope)
40
+ self.fill_in_ancestors_for(new_parent,item,hierarchy_scope)
41
+ self.delete_ancestors_of_item_children(item,hierarchy_scope)
42
+ self.set_new_ancestors_of_item_children(item,hierarchy_scope)
43
+ end
44
+
45
+ private
46
+ def self.delete_ancestors(item,hierarchy_scope)
47
+ delete_all(descendant_id: item.id,hierarchy_scope: hierarchy_scope)
48
+ end
49
+
50
+ def self.delete_ancestors_of_item_children(item,hierarchy_scope)
51
+ sql = <<-SQL
52
+ delete from #{table_name} as p using #{table_name} as p1
53
+ where p.descendant_id = p1.descendant_id
54
+ and p1.ancestor_id = #{item.id}
55
+ and p.generation > p1.generation
56
+ and p.hierarchy_scope = p1.hierarchy_scope
57
+ and p1.hierarchy_scope = '#{hierarchy_scope}'
58
+ SQL
59
+ connection.execute(sql)
60
+ end
61
+
62
+ def self.set_new_ancestors_of_item_children(item,hierarchy_scope)
63
+ sql=<<-SQL
64
+ insert into #{table_name}(ancestor_id,descendant_id,generation,hierarchy_scope)
65
+ select it.ancestor_id,ct.descendant_id,it.generation+ct.generation,it.hierarchy_scope
66
+ from #{table_name} it
67
+ join #{table_name} ct
68
+ on ct.ancestor_id = it.descendant_id
69
+ and ct.hierarchy_scope = it.hierarchy_scope
70
+ where it.descendant_id=#{item.id}
71
+ and it.hierarchy_scope = '#{hierarchy_scope}'
72
+ SQL
73
+ connection.execute(sql)
74
+ end
75
+
76
+ def self.fill_in_ancestors_for(new_parent,item,hierarchy_scope)
77
+ if new_parent
78
+ create(ancestor_id: new_parent.id,descendant_id: item.id,generation: 1,hierarchy_scope: hierarchy_scope)
79
+ sql=<<-SQL
80
+ insert into #{table_name}(ancestor_id,descendant_id,generation,hierarchy_scope)
81
+ select it.ancestor_id,#{item.id},it.generation+1,it.hierarchy_scope
82
+ from #{table_name} it
83
+ where it.descendant_id=#{new_parent.id}
84
+ and it.hierarchy_scope = '#{hierarchy_scope}'
85
+ SQL
86
+ ActiveRecord::Base.connection.execute(sql)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsManyTrees
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support'
2
+ require 'acts_as_many_trees/version'
3
+ require 'acts_as_many_trees/base'
4
+
5
+ module ActsAsManyTrees
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :Base
9
+ autoload :HierarchyTable
10
+ autoload :Version
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/active_record/migration'
3
+ require 'forwardable'
4
+
5
+ module ActsAsManyTrees
6
+ module Generators # :nodoc:
7
+ class MigrationGenerator < ::Rails::Generators::NamedBase # :nodoc:
8
+ include ActiveRecord::Generators::Migration
9
+
10
+ extend Forwardable
11
+ def_delegators :hierarchy_table_name
12
+
13
+ def self.default_generator_root
14
+ File.dirname(__FILE__)
15
+ end
16
+
17
+ def create_migration_file
18
+ migration_template 'create_hierarchies_table.rb.erb', "db/migrate/create_#{hierarchy_table_name}.rb"
19
+ end
20
+
21
+ def migration_class_name
22
+ "Create#{hierarchy_table_name.camelize}"
23
+ end
24
+
25
+ def hierarchy_table_name
26
+ (class_name+'Hierarchy').tableize
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= hierarchy_table_name %>, id: false do |t|
4
+ t.integer :ancestor_id, null: false
5
+ t.integer :descendant_id, null: false
6
+ t.integer :generation, null: false
7
+ t.string :hierarchy_scope,null: false
8
+ t.decimal :position
9
+ end
10
+
11
+ add_index :<%= hierarchy_table_name %>, [:ancestor_id, :descendant_id,:hierarchy_scope],
12
+ unique: true,
13
+ name:'<%="#{hierarchy_table_name}_anc_desc_scope_idx" %>'
14
+
15
+ add_index :<%= hierarchy_table_name -%>, [:descendant_id,:hierarchy_scope],
16
+ name: '<%="#{hierarchy_table_name}_desc_scope_idx" %>'
17
+
18
+ add_index :<%= hierarchy_table_name -%>, [:ancestor_id,:hierarchy_scope,:position],
19
+ name: '<%="#{hierarchy_table_name}_anc_scope_pos_idx" %>'
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :acts_as_many_trees do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_many_trees
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Small
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_girl_rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Uses the closure tree pattern with a scope field to maintain separate
70
+ hierarchies
71
+ email:
72
+ - jds340+rubygems@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - MIT-LICENSE
78
+ - README.rdoc
79
+ - Rakefile
80
+ - lib/acts_as_many_trees.rb
81
+ - lib/acts_as_many_trees/base.rb
82
+ - lib/acts_as_many_trees/hierarchy_table.rb
83
+ - lib/acts_as_many_trees/version.rb
84
+ - lib/generators/acts_as_many_trees/migration_generator.rb
85
+ - lib/generators/acts_as_many_trees/templates/create_hierarchies_table.rb.erb
86
+ - lib/tasks/acts_as_many_trees_tasks.rake
87
+ homepage: ''
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.2.2
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: ActiveRecord acts as tree, with many trees
111
+ test_files: []