fast_tree 0.1.0

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: ef95899f36c494fb5bc3cea8317205591f61dae7
4
+ data.tar.gz: f84331649e1fbf73db8853f7629ef8b7144f69a4
5
+ SHA512:
6
+ metadata.gz: 7a5137010f0f12bf4e6c03543814b542d218c61fecc8aee0e886e7fc7c66a6c39d2f51b1bcd9e843ef50ed99f07333e48002859ea5412dcb8d2b2e6d6ef94f9c
7
+ data.tar.gz: 8a6291deac4b760f876f50f24a2fec8c65262a1bf20b60c3a67041d60d4e6cdd4e13f3b789f71782c33a2e9c16f55782aacfde2534d60a5e8e5508082d9b66fd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Chisato HASEGAWA
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.md ADDED
@@ -0,0 +1,177 @@
1
+ # FastTree
2
+
3
+ [![CircleCI](https://circleci.com/gh/chase0213/fast_tree/tree/master.svg?style=svg)](https://circleci.com/gh/chase0213/fast_tree/tree/master)
4
+
5
+ Fast and Intuitive tree structure using nested sets model.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'fast_tree'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install fast_tree
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ `fast_tree` provides a generator which adds left and right pointers used in nested sets model to your model class.
27
+ Even if you have created a class or not, execute following commands in the terminal:
28
+
29
+ ```bash
30
+ $ bin/rails g fast_tree YOUR_MODEL_NAME
31
+ ```
32
+
33
+ After executing the command, add following sentence into your model:
34
+
35
+ ```ruby
36
+ include FastTree::Model
37
+ ```
38
+
39
+ Finally, you can use several methods as class methods and instance methods.
40
+
41
+ If you are interested in how it works, see the section "How It Works" below.
42
+
43
+ ### Create a tree
44
+
45
+ To initialize tree structure, do the following:
46
+
47
+ ```ruby
48
+ attributes = { name: "root node" }
49
+ YOUR_MODEL_NAME.create_tree(attributes)
50
+ ```
51
+
52
+ ### Create or add child
53
+
54
+ To create a new leaf under a node,
55
+
56
+ ```ruby
57
+ node = YOUR_MODEL_NAME.first
58
+
59
+ attributes = { name: "root node" }
60
+ node.create_child(attributes)
61
+ ```
62
+
63
+ or, to add existed node to another,
64
+
65
+ ```ruby
66
+ node = YOUR_MODEL_NAME.first
67
+
68
+ new_node = YOUR_MODEL_NAME.second
69
+ node.add_child(new_node)
70
+ ```
71
+
72
+ ### Create or add parent
73
+
74
+ To create a new parent over a node,
75
+
76
+ ```ruby
77
+ node = YOUR_MODEL_NAME.first
78
+
79
+ attributes = { name: "root node" }
80
+ YOUR_MODEL_NAME.create_parent(attributes, [node])
81
+ ```
82
+
83
+ or, to add existed node to another,
84
+
85
+ ```ruby
86
+ node = YOUR_MODEL_NAME.first
87
+
88
+ parent = YOUR_MODEL_NAME.second
89
+ YOUR_MODEL_NAME.add_parent(parent, [node])
90
+ ```
91
+
92
+ You can add/create a parent over several nodes:
93
+
94
+ ```ruby
95
+ children = YOUR_MODEL_NAME.take(3)
96
+ parent = YOUR_MODEL_NAME.last
97
+ YOUR_MODEL_NAME.add_parent(parent, children)
98
+ ```
99
+
100
+ NOTE: this method has a issue: https://github.com/chase0213/fast_tree/issues/6
101
+
102
+ ### Remove a node
103
+
104
+ To remove a node reconstructing the tree,
105
+
106
+ ```ruby
107
+ node = YOUR_MODEL_NAME.take
108
+ node.remove
109
+ ```
110
+
111
+ If you don't want to reconstruct the tree after deleting a node, do the following:
112
+
113
+ ```ruby
114
+ node = YOUR_MODEL_NAME.take
115
+ node.destroy
116
+ ```
117
+
118
+ ### Copy a subtree under a node
119
+
120
+ To copy a subtree under a node,
121
+
122
+ ```ruby
123
+ root_of_subtree = YOUR_MODEL_NAME.first
124
+ target = YOUR_MODEL_NAME.second
125
+
126
+ root_of_subtree.copy_to(targe)
127
+ ```
128
+
129
+ ### Move a subtree under a node
130
+
131
+ To move a subtree under a node,
132
+
133
+ ```ruby
134
+ root_of_subtree = YOUR_MODEL_NAME.first
135
+ target = YOUR_MODEL_NAME.second
136
+
137
+ root_of_subtree.move_to(targe)
138
+ ```
139
+
140
+
141
+ ## How It Works
142
+ The migration file will create a migration file, such as:
143
+
144
+ ```ruby
145
+ class AddFastTreeToTestTrees < ActiveRecord::Migration[5.0]
146
+ def self.up
147
+ change_table :test_trees do |t|
148
+ ## Pointers
149
+ t.integer :l_ptr
150
+ t.integer :r_ptr
151
+
152
+ end
153
+
154
+ add_index :test_trees, :l_ptr
155
+ add_index :test_trees, :r_ptr
156
+ end
157
+
158
+ def self.down
159
+ # model already existed. Please edit below which fields you would like to remove in this migration.
160
+ end
161
+ end
162
+ ```
163
+
164
+ , but you don't have to care what `l_ptr` and `r_ptr` are:
165
+ tree operations are executed in methods such as `create_child` or `remove`.
166
+
167
+
168
+ ## Contributing
169
+
170
+ 1. Fork it
171
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
172
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
173
+ 4. Push to the branch (`git push origin my-new-feature`)
174
+ 5. Create new Pull Request
175
+
176
+ ## License
177
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
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 = 'FastTree'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,260 @@
1
+ module FastTree
2
+ module Model
3
+ extend ActiveSupport::Concern
4
+
5
+ # =================
6
+ # Class Methods
7
+ # =================
8
+
9
+ module ClassMethods
10
+
11
+ # ====================
12
+ # structure operation
13
+ # ====================
14
+
15
+ def add_parent(parent, children, &block)
16
+ # create space for parent
17
+ ptrs = _create_parent_embedded_space(children)
18
+
19
+ # parent node's pointer
20
+ parent.l_ptr = ptrs[:l_ptr]
21
+ parent.r_ptr = ptrs[:r_ptr]
22
+ parent.save
23
+ end
24
+
25
+ def create_parent(attributes = {}, children, &block)
26
+ # create space for parent
27
+ ptrs = _create_parent_embedded_space(children)
28
+
29
+ # parent node's pointer
30
+ attributes[:l_ptr] = ptrs[:l_ptr]
31
+ attributes[:r_ptr] = ptrs[:r_ptr]
32
+
33
+ self.create(attributes, &block)
34
+ end
35
+
36
+ def create_tree(attributes={}, &block)
37
+ root = self.find_root
38
+ if root
39
+ root
40
+ else
41
+ attributes[:l_ptr] = 0
42
+ attributes[:r_ptr] = 1
43
+ self.create(attributes, &block)
44
+ end
45
+ end
46
+
47
+
48
+ def _create_parent_embedded_space(children)
49
+ left = children.max {|c| c.l_ptr}.l_ptr
50
+ right = children.min {|c| c.r_ptr}.r_ptr
51
+
52
+ sql = <<-"EOS"
53
+ UPDATE #{self.to_s.underscore.pluralize}
54
+ SET l_ptr = CASE
55
+ WHEN l_ptr >= #{left}
56
+ AND r_ptr <= #{right}
57
+ THEN l_ptr + 1
58
+ WHEN l_ptr > #{right}
59
+ THEN l_ptr + 2
60
+ ELSE l_ptr
61
+ END,
62
+ r_ptr = CASE
63
+ WHEN l_ptr >= #{left}
64
+ AND r_ptr <= #{right}
65
+ THEN r_ptr + 1
66
+ WHEN l_ptr < #{left}
67
+ AND r_ptr > #{right}
68
+ THEN r_ptr + 2
69
+ WHEN l_ptr > #{right}
70
+ THEN r_ptr + 2
71
+ ELSE r_ptr
72
+ END
73
+ WHERE r_ptr > #{left}
74
+ EOS
75
+
76
+ ActiveRecord::Base.connection.execute(sql)
77
+
78
+ # return left and right pointers between which parent is embedded
79
+ {l_ptr: left, r_ptr: right + 2}
80
+ end
81
+
82
+ # ================
83
+ # model operation
84
+ # ================
85
+
86
+ def find_root
87
+ self.find_by(l_ptr: 0)
88
+ end
89
+
90
+ def find_subtree_by_root(node)
91
+ l_ptr = node.l_ptr
92
+ r_ptr = node.r_ptr
93
+
94
+ self.where(self.arel_table[:l_ptr].gteq(l_ptr))
95
+ .where(self.arel_table[:r_ptr].lteq(r_ptr))
96
+ end
97
+
98
+ # ================
99
+ # for debugging
100
+ # ================
101
+
102
+ def print_subtree(root)
103
+ puts("printing sub tree for #{root.name}...")
104
+ subtree = find_subtree_by_root(root)
105
+ subtree.order(l_ptr: :asc).each do |st_node|
106
+ st_node.reload
107
+ # white spaces on the left
108
+ wsl = st_node.l_ptr.times.map{|s| "_"}.join('')
109
+ # arrows
110
+ ars = (st_node.r_ptr - st_node.l_ptr ).times.map{|s| "-"}.join('')
111
+ # white spaces on the right
112
+ wsr = (root.width + 2 - wsl.size - ars.size).times.map{|s| " "}.join('')
113
+
114
+ puts("#{wsl}<#{ars}>#{wsr} ... #{st_node.name}")
115
+ end
116
+ puts("done.\n")
117
+ end
118
+ end
119
+
120
+
121
+ # class methods
122
+ # =========================================================
123
+ # instance methods
124
+
125
+
126
+ # =================
127
+ # Instance Methods
128
+ # =================
129
+
130
+ def add_child(node)
131
+ # bulk update nodes by a sql query
132
+ _update_nodes(r_ptr, r_ptr, "r_ptr >= #{r_ptr}")
133
+
134
+ # child node's pointer
135
+ node.l_ptr = r_ptr
136
+ node.r_ptr = r_ptr + 1
137
+ node.save
138
+ end
139
+
140
+ def create_child(attributes = {}, &block)
141
+ # bulk update nodes by a sql query
142
+ _update_nodes(r_ptr, r_ptr, "r_ptr >= #{r_ptr}")
143
+
144
+ # create child
145
+ attributes[:l_ptr] = r_ptr
146
+ attributes[:r_ptr] = r_ptr + 1
147
+ self.class.create(attributes, &block)
148
+ end
149
+
150
+ def copy_to(node)
151
+ subtree = self.class.find_subtree_by_root(self)
152
+
153
+ # create empty space into which subtree embedded
154
+ _update_nodes(node.l_ptr, node.r_ptr, "r_ptr >= #{r_ptr}", width + 1)
155
+
156
+ bias = node.l_ptr + 1 - l_ptr
157
+ subtree.each do |st_node|
158
+ attributes = st_node.attributes.to_h
159
+ attributes.delete("id")
160
+ attributes["l_ptr"] = attributes["l_ptr"] + bias
161
+ attributes["r_ptr"] = attributes["r_ptr"] + bias
162
+ self.class.create(attributes)
163
+ end
164
+ end
165
+
166
+ def depth
167
+ path.size - 1
168
+ end
169
+
170
+ def move_to(node)
171
+ # NOTE:
172
+ # copy_to and remove change node ids
173
+ # move operation should change nothing but left and right pointers
174
+
175
+ # bind subtree to a variable
176
+ subtree = self.class.find_subtree_by_root(self)
177
+
178
+ # fill (virtual) empty spaces that will be created by moving subtree
179
+ _update_nodes(l_ptr, r_ptr, "l_ptr > #{r_ptr}", - (width + 1))
180
+
181
+ # create empty spaces under the node
182
+ node.reload
183
+ _update_nodes(node.l_ptr, node.r_ptr, "l_ptr >= #{node.l_ptr} AND r_ptr <= #{node.r_ptr}", width + 1)
184
+
185
+ # move subtree under the given node
186
+ bias = node.l_ptr + 1 - l_ptr
187
+ subtree.each do |st_node|
188
+ st_node.l_ptr += bias
189
+ st_node.r_ptr += bias
190
+ st_node.save
191
+ end
192
+ end
193
+
194
+ def remove
195
+ # remove subtree
196
+ n_destroyed = self.class.find_subtree_by_root(self).destroy_all
197
+
198
+ # fill empty space
199
+ _update_nodes(l_ptr, r_ptr, "r_ptr >= #{r_ptr}", - (width + 1))
200
+
201
+ # return count of destroyed nodes
202
+ n_destroyed
203
+ end
204
+
205
+ def path
206
+ self.class.where(self.class.arel_table[:l_ptr].lteq(l_ptr))
207
+ .where(self.class.arel_table[:r_ptr].gteq(r_ptr))
208
+ .order(l_ptr: :asc)
209
+ end
210
+
211
+ def print_subtree
212
+ self.class.print_subtree(self)
213
+ end
214
+
215
+ def width
216
+ r_ptr - l_ptr
217
+ end
218
+
219
+
220
+ protected
221
+
222
+ def _update_nodes(left, right, condition, diff = 2)
223
+ #
224
+ # NOTE:
225
+ # Due to performance reason,
226
+ # use raw SQL query to move nodes
227
+ #
228
+
229
+ sql = <<-"EOS"
230
+ UPDATE #{self.class.to_s.underscore.pluralize}
231
+ SET l_ptr = CASE
232
+ WHEN l_ptr > #{right}
233
+ THEN l_ptr + #{diff}
234
+ WHEN l_ptr > #{left}
235
+ AND r_ptr < #{right}
236
+ THEN l_ptr + #{diff - 1}
237
+ ELSE l_ptr
238
+ END,
239
+ r_ptr = CASE
240
+ WHEN l_ptr <= #{left}
241
+ AND r_ptr >= #{right}
242
+ THEN r_ptr + #{diff}
243
+ WHEN l_ptr > #{right}
244
+ THEN r_ptr + #{diff}
245
+ WHEN l_ptr > #{left}
246
+ AND r_ptr < #{right}
247
+ THEN r_ptr + #{diff - 1}
248
+ ELSE
249
+ r_ptr
250
+ END
251
+ WHERE #{condition}
252
+ EOS
253
+
254
+ ActiveRecord::Base.connection.execute(sql)
255
+ end
256
+
257
+ class InvalidTreeStructureError < ActiveRecord::RecordInvalid; end
258
+
259
+ end
260
+ end
@@ -0,0 +1,3 @@
1
+ module FastTree
2
+ VERSION = '0.1.0'
3
+ end
data/lib/fast_tree.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+
3
+ module FastTree
4
+
5
+ # =====================================
6
+ # Implementation of Fast Tree Structure
7
+ # =====================================
8
+
9
+ autoload :Model, 'fast_tree/model'
10
+
11
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails/generators/active_record'
2
+ require 'generators/fast_tree/orm_helpers'
3
+
4
+ module ActiveRecord
5
+ module Generators
6
+ class FastTreeGenerator < ActiveRecord::Generators::Base
7
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
8
+
9
+ include FastTree::Generators::OrmHelpers
10
+ source_root File.expand_path("../templates", __FILE__)
11
+
12
+ def copy_fast_tree_migration
13
+ if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
14
+ migration_template "migration_existing.rb", "db/migrate/add_fast_tree_to_#{table_name}.rb", migration_version: migration_version
15
+ else
16
+ migration_template "migration.rb", "db/migrate/fast_tree_create_#{table_name}.rb", migration_version: migration_version
17
+ end
18
+ end
19
+
20
+ def generate_model
21
+ invoke "active_record:model", [name], migration: false unless model_exists? && behavior == :invoke
22
+ end
23
+
24
+ def migration_data
25
+ <<RUBY
26
+ ## Pointers
27
+ t.integer :l_ptr
28
+ t.integer :r_ptr
29
+ RUBY
30
+ end
31
+
32
+ def rails5?
33
+ Rails.version.start_with? '5'
34
+ end
35
+
36
+ def postgresql?
37
+ config = ActiveRecord::Base.configurations[Rails.env]
38
+ config && config['adapter'] == 'postgresql'
39
+ end
40
+
41
+ def migration_version
42
+ if rails5?
43
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ class FastTreeCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ <%= migration_data -%>
5
+
6
+ <% attributes.each do |attribute| -%>
7
+ t.<%= attribute.type %> :<%= attribute.name %>
8
+ <% end -%>
9
+
10
+ t.timestamps null: false
11
+ end
12
+
13
+ add_index :<%= table_name %>, :l_ptr
14
+ add_index :<%= table_name %>, :r_ptr
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ class AddFastTreeTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def self.up
3
+ change_table :<%= table_name %> do |t|
4
+ <%= migration_data -%>
5
+
6
+ <% attributes.each do |attribute| -%>
7
+ t.<%= attribute.type %> :<%= attribute.name %>
8
+ <% end -%>
9
+ end
10
+
11
+ add_index :<%= table_name %>, :l_ptr
12
+ add_index :<%= table_name %>, :r_ptr
13
+ end
14
+
15
+ def self.down
16
+ # model already existed. Please edit below which fields you would like to remove in this migration.
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module FastTree
4
+ module Generators
5
+ class FastTreeGenerator < Rails::Generators::NamedBase
6
+ include Rails::Generators::ResourceHelpers
7
+
8
+ namespace "fast_tree"
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ desc "Generates a model with the given NAME (if one does not exist) with a migration file"
12
+
13
+ hook_for :orm
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module FastTree
2
+ module Generators
3
+ module OrmHelpers
4
+ private
5
+
6
+ def model_exists?
7
+ File.exist?(File.join(destination_root, model_path))
8
+ end
9
+
10
+ def migration_exists?(table_name)
11
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_fast_tree_to_#{table_name}.rb$/).first
12
+ end
13
+
14
+ def migration_path
15
+ @migration_path ||= File.join("db", "migrate")
16
+ end
17
+
18
+ def model_path
19
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :fast_tree do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chisato Hasegawa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-07 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.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
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: acts_as_tree
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
+ description: fast_tree is an implementation of tree structure using nested sets model
56
+ email:
57
+ - chase0213@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - lib/fast_tree.rb
66
+ - lib/fast_tree/model.rb
67
+ - lib/fast_tree/version.rb
68
+ - lib/generators/active_record/fast_tree_generator.rb
69
+ - lib/generators/active_record/templates/migration.rb
70
+ - lib/generators/active_record/templates/migration_existing.rb
71
+ - lib/generators/fast_tree/fast_tree_generator.rb
72
+ - lib/generators/fast_tree/orm_helpers.rb
73
+ - lib/tasks/fast_tree_tasks.rake
74
+ homepage: https://github.com/chase0213/fast_tree
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.5.1
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: '["Rails plugin for Fast Tree Structure", "fast_tree is an implementation
98
+ of tree structure using nested sets model", "MIT"]'
99
+ test_files: []