mongestry 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+
9
+ gem 'bson_ext'
10
+ gem 'mongoid'
11
+
12
+ group :development do
13
+ gem "rspec", "~> 2.3.0"
14
+ gem "yard", "~> 0.6.0"
15
+ gem "bundler", "~> 1.0.0"
16
+ gem "jeweler", "~> 1.6.2"
17
+ gem "rcov", ">= 0"
18
+ gem 'linecache19', '0.5.11'
19
+ gem 'ruby-debug19'
20
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.7)
5
+ activesupport (= 3.0.7)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activesupport (3.0.7)
9
+ archive-tar-minitar (0.5.2)
10
+ bson (1.3.1)
11
+ bson_ext (1.3.1)
12
+ builder (2.1.2)
13
+ columnize (0.3.2)
14
+ diff-lcs (1.1.2)
15
+ git (1.2.5)
16
+ i18n (0.5.0)
17
+ jeweler (1.6.2)
18
+ bundler (~> 1.0)
19
+ git (>= 1.2.5)
20
+ rake
21
+ linecache19 (0.5.11)
22
+ ruby_core_source (>= 0.1.4)
23
+ mongo (1.3.1)
24
+ bson (>= 1.3.1)
25
+ mongoid (2.0.2)
26
+ activemodel (~> 3.0)
27
+ mongo (~> 1.3)
28
+ tzinfo (~> 0.3.22)
29
+ rake (0.9.2)
30
+ rcov (0.9.9)
31
+ rspec (2.3.0)
32
+ rspec-core (~> 2.3.0)
33
+ rspec-expectations (~> 2.3.0)
34
+ rspec-mocks (~> 2.3.0)
35
+ rspec-core (2.3.1)
36
+ rspec-expectations (2.3.0)
37
+ diff-lcs (~> 1.1.2)
38
+ rspec-mocks (2.3.0)
39
+ ruby-debug-base19 (0.11.25)
40
+ columnize (>= 0.3.1)
41
+ linecache19 (>= 0.5.11)
42
+ ruby_core_source (>= 0.1.4)
43
+ ruby-debug19 (0.11.6)
44
+ columnize (>= 0.3.1)
45
+ linecache19 (>= 0.5.11)
46
+ ruby-debug-base19 (>= 0.11.19)
47
+ ruby_core_source (0.1.5)
48
+ archive-tar-minitar (>= 0.5.2)
49
+ tzinfo (0.3.27)
50
+ yard (0.6.8)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ bson_ext
57
+ bundler (~> 1.0.0)
58
+ jeweler (~> 1.6.2)
59
+ linecache19 (= 0.5.11)
60
+ mongoid
61
+ rcov
62
+ rspec (~> 2.3.0)
63
+ ruby-debug19
64
+ yard (~> 0.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2011 DailyDeal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.rdoc ADDED
@@ -0,0 +1,161 @@
1
+ = Mongestry
2
+
3
+ Mongestry is a gem that allows the records of a Mongoid model to be organized as a tree structure (or hierarchy). It uses a single, intuitively formatted database column, using a variation on the materialised path pattern. It exposes all the standard tree structure relations (ancestors, parent, root, children, siblings, descendants) and all of them can be fetched in a single query. Additional features are scopes, depth caching, depth constraints, easy migration from similar plugins/gems.
4
+
5
+ Mongestry is inspired by the famous {Ancestry}[https://github.com/stefankroes/ancestry] gem by {Stefan Kroes}[https://github.com/stefankroes]. It implements most of its functionality but lacks some. So be prepared.
6
+
7
+ = Installation
8
+
9
+ To apply Mongestry to any Mongoid model, follow these simple steps:
10
+
11
+ == Gem installation
12
+
13
+ Add Mongestry to your app's Gemfile:
14
+
15
+ gem 'mongestry'
16
+
17
+ Install required gems:
18
+
19
+ bundle install
20
+
21
+ Add mongestry to your model via the following declarative line:
22
+
23
+ has_mongestry
24
+
25
+ == Example
26
+
27
+ class TreeNode
28
+ include Mongoid::Document
29
+ include Mongoid::Timestamps
30
+
31
+ field :name, type: String
32
+
33
+ has_mongestry
34
+ end
35
+
36
+ Your model is now a tree!
37
+
38
+ = Organizing records into a tree
39
+
40
+ You can use the parent attribute to organize your records into a tree. If you have the id of the record you want to use as a parent and don't want to fetch it, you can also use parent_id. Like any virtual model attributes, parent and parent_id can be set using parent= and parent_id= on a record or by including them in the hash passed to new, create, create!. For example:
41
+
42
+ TreeNode.create! :name => 'Stinky', :parent => TreeNode.create!(:name => 'Squeeky')
43
+
44
+ As of now you can <b>NOT</b> create children through the children relation on a node, so be patient, this will come in an upcoming release.
45
+
46
+ = Navigating your tree
47
+
48
+ To navigate a Mongestry model, use the following methods on any instance / record:
49
+
50
+ parent Returns the parent of the record, nil for a root node
51
+ parent_id Returns the id of the parent of the record, nil for a root node
52
+ root Returns the root of the tree the record is in, self for a root node
53
+ root_id Returns the id of the root of the tree the record is in
54
+ is_root? Returns true if the record is a root node, false otherwise
55
+ ancestor_ids Returns a list of ancestor ids, starting with the root id and ending with the parent id
56
+ ancestors Scopes the model on ancestors of the record
57
+ children Scopes the model on children of the record
58
+ child_ids Returns a list of child ids
59
+ has_children? Returns true if the record has any children, false otherwise
60
+ is_childless? Returns true is the record has no childen, false otherwise
61
+ siblings Scopes the model on siblings of the record, the record itself is included
62
+ sibling_ids Returns a list of sibling ids
63
+ has_siblings? Returns true if the record's parent has more than one child
64
+ is_only_child? Returns true if the record is the only child of its parent
65
+ descendants Scopes the model on direct and indirect children of the record
66
+ descendant_ids Returns a list of a descendant ids
67
+ subtree Scopes the model on descendants and itself
68
+ subtree_ids Returns a list of all ids in the record's subtree
69
+ depth Return the depth of the node, root nodes are at depth 0
70
+
71
+ = Options for has_mongestry
72
+
73
+ Currently there are none.
74
+
75
+ = Scopes
76
+
77
+ Where possible, the navigation methods return scopes instead of records, this means additional ordering, conditions, limits, etc. can be applied and that the result can be either retrieved, counted or checked for existence. For example:
78
+
79
+ node.children.exists?(:name => 'Mary')
80
+ node.subtree.all(:order => :name, :limit => 10).each do; ...; end
81
+ node.descendants.count
82
+
83
+ For convenience, a couple of named scopes are included at the class level:
84
+
85
+ roots # Root nodes
86
+ ancestors_of(node) # Ancestors of node, node can be either a record or an id
87
+ children_of(node) # Children of node, node can be either a record or an id
88
+ descendants_of(node) # Descendants of node, node can be either a record or an id
89
+ subtree_of(node) # Subtree of node, node can be either a record or an id
90
+ siblings_of(node) # Siblings of node, node can be either a record or an id
91
+
92
+ == Selecting nodes by depth
93
+
94
+ In Mongestry depth caching is enabled by default. Therefore five more scopes can be used to select nodes on their depth:
95
+
96
+ before_depth(depth) # Return nodes that are less deep than depth (node.depth < depth)
97
+ to_depth(depth) # Return nodes up to a certain depth (node.depth <= depth)
98
+ at_depth(depth) # Return nodes that are at depth (node.depth == depth)
99
+ from_depth(depth) # Return nodes starting from a certain depth (node.depth >= depth)
100
+ after_depth(depth) # Return nodes that are deeper than depth (node.depth > depth)
101
+
102
+ The depth scopes are also available through calls to descendants, descendant_ids, subtree, subtree_ids, path and ancestors. In this case, depth values are interpreted relatively. Some examples:
103
+
104
+ node.subtree(:to_depth => 2) # Subtree of node, to a depth of node.depth + 2 (self, children and grandchildren)
105
+ node.subtree.to_depth(5) # Subtree of node to an absolute depth of 5
106
+ node.descendants(:at_depth => 2) # Descendant of node, at depth node.depth + 2 (grandchildren)
107
+ node.descendants.at_depth(10) # Descendants of node at an absolute depth of 10
108
+ node.ancestors.to_depth(3) # The oldest 4 ancestors of node (its root and 3 more)
109
+
110
+ node.ancestors(:from_depth => -6, :to_depth => -4)
111
+ node.descendants(:from_depth => 2, :to_depth => 4)
112
+ node.subtree.from_depth(10).to_depth(12)
113
+
114
+ Please note that depth constraints cannot be passed to ancestor_ids and path_ids. The reason for this is that both these relations can be fetched directly from the ancestry column without performing a database query. It would require an entirely different method of applying the depth constraints which isn't worth the effort of implementing. You can use ancestors(depth_options).map(&:id) or ancestor_ids.slice(min_depth..max_depth) instead.
115
+
116
+ = Tests
117
+
118
+ The Mongestry gem comes with a RSpec test suite consisting of about 190+ assertions in about 45+ tests. It takes about 0.2 seconds to run on MongoDB. To run it yourself check out the repository from GitHub, check and fix <em>spec/support/connection.rb</em> to your needs and type:
119
+
120
+ rake
121
+
122
+ = Internals
123
+
124
+ As can be seen in the previous section, Mongestry stores a path from the root to the parent for every node. This is a variation on the materialised path database pattern. It allows Mongestry to fetch any relation (siblings, descendants, etc.) in a single db request without the complicated algorithms and incomprehensibility associated with left and right values. Additionally, any inserts, deletes and updates only affect nodes within the affected node's own subtree.
125
+
126
+ In the example above, the ancestry field is created as a string. This puts a limitation on the depth of the tree of about 40 or 50 levels, which I think may be enough for most users. To increase the maximum depth of the tree, increase the size of the string that is being used or change it to a text to remove the limitation entirely. Changing it to a text will however decrease performance because an index cannot be put on the column in that case.
127
+
128
+ = Limitations
129
+
130
+ Mongestry was created with Rails3 and Ruby >= 1.9.2 in mind. Sorry. You need Rails2 or Ruby prior to 1.9 support? Feel free to fork, fix and request a pull.
131
+
132
+ = Missing Features
133
+
134
+ Compared to {Ancestry}[https://github.com/stefankroes/ancestry] there are some missing features.
135
+
136
+ - Creation of nodes through relational scopes
137
+ - Integrity checking
138
+ - options for <em>has_mongestry</em> (don't know if we need any)
139
+ - STI support
140
+ - arrangement
141
+ - sorting by ancestry
142
+ - migration from other plugins
143
+ - integrity checking and fixing
144
+ - Rails2 support
145
+ - support for Ruby versions < 1.9
146
+ - instance methods: path, path_ids
147
+
148
+ = Contributing to Mongestry
149
+
150
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
151
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
152
+ * Fork the project
153
+ * Start a feature/bugfix branch
154
+ * Commit and push until you are happy with your contribution
155
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
156
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so we can cherry-pick around it.
157
+
158
+ = Copyright
159
+
160
+ Copyright (c) 2011 DailyDeal GmbH. See LICENSE.txt for further details.
161
+
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "mongestry"
18
+ gem.homepage = "http://github.com/DailyDeal/mongestry"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Mongestry is Ancestry for Mongo}
21
+ gem.description = %Q{Mongestry is Ancestry for Mongo, build for ORM Mongoid}
22
+ gem.email = %q{jan.roesner@dailydeal.de lars.kluge@dailydeal.de}
23
+ gem.authors = ["Jan Roesner", "Lars Kluge"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.5
data/lib/mongestry.rb ADDED
@@ -0,0 +1,214 @@
1
+ module Mongestry
2
+
3
+ class << self.class.superclass
4
+ def has_mongestry
5
+ include Mongestry::InstanceMethods
6
+ field :ancestry, type: String
7
+ index :ancestry
8
+ field :persisted_depth, type: Integer
9
+ before_create :build_ancestry
10
+ end
11
+ end
12
+
13
+ module InstanceMethods
14
+
15
+ def self.included base
16
+ base.extend Mongestry::ClassMethods
17
+ end
18
+
19
+ def build_ancestry
20
+ raise "Either parent or parent_id can be given, not both at once" if self.attributes.keys.include?("parent") and self.attributes.keys.include?("parent_id")
21
+ return unless self.respond_to?(:parent) or self.respond_to?(:parent_id)
22
+
23
+ parent = self.class.object_for(self.attributes["parent"] || self.attributes["parent_id"])
24
+
25
+ self.ancestry = nil unless parent
26
+ self.ancestry = parent.ancestry.nil? ? parent.id.to_s : parent.ancestry.to_s + "/#{parent.id.to_s}" if parent
27
+ self.persisted_depth = parent.depth + 1 rescue 0
28
+
29
+ self.attributes.delete("parent")
30
+ self.attributes.delete("parent_id")
31
+ end
32
+
33
+ # Returns a list of ancestor ids, starting with the root id and ending with the parent id
34
+ def ancestor_ids
35
+ self.ancestry.split('/').collect{ |s| BSON::ObjectId.from_string s }
36
+ end
37
+
38
+ # Scopes the model on ancestors of the record
39
+ def ancestors
40
+ return [] if self.is_root?
41
+ self.class.where(_id: {"$in" => self.ancestor_ids})
42
+ end
43
+
44
+ # Returns the parent of the record, nil for a root node
45
+ def parent
46
+ self.class.where(_id: self.ancestry.split('/').last).first rescue nil
47
+ end
48
+
49
+ # Returns the id of the parent of the record, nil for a root node
50
+ def parent_id
51
+ self.parent.id rescue nil
52
+ end
53
+
54
+ # Returns the root of the tree the record is in, self for a root node
55
+ def root
56
+ return self unless self.ancestry
57
+ self.class.where(_id: self.ancestry.split('/').first).first
58
+ end
59
+
60
+ # Returns the id of the root of the tree the record is in
61
+ def root_id
62
+ self.root.id
63
+ end
64
+
65
+ # Returns true if the record is a root node, false otherwise
66
+ def is_root?
67
+ self.ancestry.nil?
68
+ end
69
+
70
+ # Scopes the model on children of the record
71
+ def children
72
+ case self.is_root?
73
+ when true
74
+ self.class.where(:ancestry => self.ancestry.to_s + "#{self.id.to_s}")
75
+ else
76
+ self.class.where(:ancestry => self.ancestry.to_s + "/#{self.id.to_s}")
77
+ end
78
+ end
79
+
80
+ # Returns a list of child ids
81
+ def child_ids
82
+ self.children.map(&:id)
83
+ end
84
+
85
+ # Returns true if the record has any children, false otherwise
86
+ def has_children?
87
+ !self.children.to_a.blank?
88
+ end
89
+
90
+ # Returns true if the record has no childen, false otherwise
91
+ def is_childless?
92
+ !self.has_children?
93
+ end
94
+
95
+ # Scopes the model on siblings of the record, the record itself is included
96
+ def siblings
97
+ self.class.where(:ancestry => self.ancestry.to_s).and(:_id => {'$ne' => self.id})
98
+ end
99
+
100
+ # Returns a list of sibling ids
101
+ def sibling_ids
102
+ self.siblings.map(&:id)
103
+ end
104
+
105
+ # Returns true if the record's parent has more than one child
106
+ def has_siblings?
107
+ self.siblings.present?
108
+ end
109
+
110
+ # Returns true if the record is the only child of its parent
111
+ def is_only_child?
112
+ !self.has_siblings?
113
+ end
114
+
115
+ # Scopes the model on direct and indirect children of the record
116
+ def descendants
117
+ expression = self.is_root? ? self.id.to_s : (self.ancestry + "/#{self.id.to_s}").split('/').join('\/')
118
+ self.class.where(:ancestry => Regexp.new(expression))
119
+ end
120
+
121
+ # Returns a list of a descendant ids
122
+ def descendant_ids
123
+ self.descendants.map(&:id)
124
+ end
125
+
126
+ # Scopes the model on descendants and itself
127
+ def subtree
128
+ self.class.where(_id: {"$in" => self.descendant_ids.push(self.id)})
129
+ end
130
+
131
+ # Returns a list of all ids in the record's subtree
132
+ def subtree_ids
133
+ self.subtree.map(&:id)
134
+ end
135
+
136
+ # Return the depth of the node, root nodes are at depth 0
137
+ def depth
138
+ self.ancestry.split('/').size rescue 0
139
+ end
140
+
141
+ end
142
+
143
+ module ClassMethods
144
+
145
+ def object_for identifier
146
+ return nil if identifier == ""
147
+ case identifier
148
+ when BSON::ObjectId
149
+ self.find identifier
150
+ when String
151
+ self.find(BSON::ObjectId.from_string(identifier))
152
+ when self
153
+ identifier
154
+ end
155
+ end
156
+
157
+ #Root nodes
158
+ def roots
159
+ self.where(ancestry: nil)
160
+ end
161
+
162
+ # Ancestors of node, node can be either a record or an id
163
+ def ancestors_of node
164
+ node.ancestors
165
+ end
166
+
167
+ # Children of node, node can be either a record or an id
168
+ def children_of node
169
+ node.children
170
+ end
171
+
172
+ # Descendants of node, node can be either a record or an id
173
+ def descendants_of node
174
+ node.descendants
175
+ end
176
+
177
+ # Subtree of node, node can be either a record or an id
178
+ def subtree_of node
179
+ node.subtree
180
+ end
181
+
182
+ # Siblings of node, node can be either a record or an id
183
+ def siblings_of node
184
+ node.siblings
185
+ end
186
+
187
+ # Return nodes that are less deep than depth (node.depth < depth)
188
+ def before_depth depth
189
+ self.where(persisted_depth: {"$lt" => depth})
190
+ end
191
+
192
+ # Return nodes up to a certain depth (node.depth <= depth)
193
+ def to_depth depth
194
+ self.where(persisted_depth: {"$lte" => depth})
195
+ end
196
+
197
+ # Return nodes that are at depth (node.depth == depth)
198
+ def at_depth depth
199
+ self.where(persisted_depth: depth)
200
+ end
201
+
202
+ # Return nodes starting from a certain depth (node.depth >= depth)
203
+ def from_depth depth
204
+ self.where(persisted_depth: {"$gte" => depth})
205
+ end
206
+
207
+ # Return nodes that are deeper than depth (node.depth > depth)
208
+ def after_depth depth
209
+ self.where(persisted_depth: {"$gt" => depth})
210
+ end
211
+
212
+ end
213
+
214
+ end
data/mongestry.gemspec ADDED
@@ -0,0 +1,78 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mongestry}
8
+ s.version = "0.5.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jan Roesner", "Lars Kluge"]
12
+ s.date = %q{2011-07-20}
13
+ s.description = %q{Mongestry is Ancestry for Mongo, build for ORM Mongoid}
14
+ s.email = %q{jan.roesner@dailydeal.de lars.kluge@dailydeal.de}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/mongestry.rb",
29
+ "mongestry.gemspec",
30
+ "spec/mongestry_spec.rb",
31
+ "spec/spec_helper.rb",
32
+ "spec/support/category.rb",
33
+ "spec/support/connection.rb"
34
+ ]
35
+ s.homepage = %q{http://github.com/DailyDeal/mongestry}
36
+ s.licenses = ["MIT"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.7}
39
+ s.summary = %q{Mongestry is Ancestry for Mongo}
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<bson_ext>, [">= 0"])
47
+ s.add_runtime_dependency(%q<mongoid>, [">= 0"])
48
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
49
+ s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
50
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
52
+ s.add_development_dependency(%q<rcov>, [">= 0"])
53
+ s.add_development_dependency(%q<linecache19>, ["= 0.5.11"])
54
+ s.add_development_dependency(%q<ruby-debug19>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<bson_ext>, [">= 0"])
57
+ s.add_dependency(%q<mongoid>, [">= 0"])
58
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
59
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
62
+ s.add_dependency(%q<rcov>, [">= 0"])
63
+ s.add_dependency(%q<linecache19>, ["= 0.5.11"])
64
+ s.add_dependency(%q<ruby-debug19>, [">= 0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<bson_ext>, [">= 0"])
68
+ s.add_dependency(%q<mongoid>, [">= 0"])
69
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
70
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
71
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
72
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
73
+ s.add_dependency(%q<rcov>, [">= 0"])
74
+ s.add_dependency(%q<linecache19>, ["= 0.5.11"])
75
+ s.add_dependency(%q<ruby-debug19>, [">= 0"])
76
+ end
77
+ end
78
+
@@ -0,0 +1,450 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ def initialize_country_tree
4
+ @root = Category.create!(name: "Root", persisted_depth: 0)
5
+ @germany = Category.create!(name: "Germany", persisted_depth: 1, ancestry: "#{@root.id}")
6
+ @switzerland = Category.create!(name: "Switzerland", persisted_depth: 1, ancestry: "#{@root.id}")
7
+ @austria = Category.create!(name: "Austria", persisted_depth: 1, ancestry: "#{@root.id}")
8
+ @berlin = Category.create!(name: "Berlin", persisted_depth: 2, ancestry: "#{@root.id}/#{@germany.id}")
9
+ @munich = Category.create!(name: "Munich", persisted_depth: 2, ancestry: "#{@root.id}/#{@germany.id}")
10
+ @hamburg = Category.create!(name: "Hamburg", persisted_depth: 2, ancestry: "#{@root.id}/#{@germany.id}")
11
+ @bern = Category.create!(name: "Bern", persisted_depth: 2, ancestry: "#{@root.id}/#{@switzerland.id}")
12
+ @zurich = Category.create!(name: "Zurich", persisted_depth: 2, ancestry: "#{@root.id}/#{@switzerland.id}")
13
+ @vienna = Category.create!(name: "Vienna", persisted_depth: 2, ancestry: "#{@root.id}/#{@austria.id}")
14
+ @graz = Category.create!(name: "Graz", persisted_depth: 2, ancestry: "#{@root.id}/#{@austria.id}")
15
+ @pankow = Category.create!(name: "Pankow", persisted_depth: 3, ancestry: "#{@root.id}/#{@germany.id}/#{@berlin.id}")
16
+ end
17
+
18
+ describe "Mongestry" do
19
+ context "with fixed tree" do
20
+
21
+ before :all do
22
+ Category.destroy_all
23
+ initialize_country_tree
24
+ Category.class_eval{ has_mongestry }
25
+ end
26
+
27
+ describe '#has_mongestry' do
28
+
29
+ it 'should include instance methods into class where invoked' do
30
+ class Foo
31
+ include Mongoid::Document
32
+ has_mongestry
33
+ end
34
+
35
+ [:build_ancestry, :ancestor_ids, :ancestors, :parent, :parent_id, :root, :root_id, :is_root?, :children, :child_ids, :has_children?, :is_childless?, :siblings, :sibling_ids, :has_siblings?, :is_only_child?, :descendants, :descendant_ids, :subtree, :subtree_ids, :depth].each do |method|
36
+ Foo.new.respond_to?(method).should be_true
37
+ end
38
+
39
+ end
40
+
41
+ it 'should include class methods into class where invoked' do
42
+ class Bar
43
+ include Mongoid::Document
44
+ has_mongestry
45
+ end
46
+
47
+ [:roots, :ancestors_of, :children_of, :descendants_of, :subtree_of, :siblings_of, :before_depth, :to_depth, :at_depth, :from_depth, :after_depth, :object_for].each do |method|
48
+ Bar.respond_to?(method).should be_true
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#ancestor_ids' do
54
+ it 'should return the ancestor_ids of the given node' do
55
+ ids = Category.where(name:"Pankow").first.ancestor_ids
56
+
57
+ ids.size.should == 3
58
+ ids.include?(@berlin.id).should be_true
59
+ ids.include?(@germany.id).should be_true
60
+ ids.include?(@root.id).should be_true
61
+ end
62
+ end
63
+
64
+ describe '#ancestors' do
65
+ it 'should return ancestors of the given node scoped' do
66
+ ancestors = Category.where(name:"Pankow").first.ancestors.to_a
67
+
68
+ ancestors.size.should == 3
69
+ ancestors.include?(@berlin).should be_true
70
+ ancestors.include?(@germany).should be_true
71
+ ancestors.include?(@root).should be_true
72
+ end
73
+
74
+ it 'should return an empty array if called on a root category' do
75
+ ancestors = Category.where(name: "Root").first.ancestors.to_a
76
+ ancestors.size.should == 0
77
+ end
78
+ end
79
+
80
+ describe '#parent' do
81
+ it 'should return the given nodes parent' do
82
+ Category.where(name: "Germany").first.parent.should == @root
83
+ Category.where(name: "Pankow").first.parent.should == @berlin
84
+ Category.roots.first.parent.should be_nil
85
+ end
86
+ end
87
+
88
+ describe '#parent_id' do
89
+ it 'should return the given nodes parents id' do
90
+ Category.where(name: "Germany").first.parent_id.should == @root.id
91
+ Category.where(name: "Pankow").first.parent_id.should == @berlin.id
92
+ Category.roots.first.parent_id.should be_nil
93
+ end
94
+ end
95
+
96
+ describe '#root' do
97
+ it 'should return the root of the tree of the given node' do
98
+ Category.where(name: "Pankow").first.root.should eql @root
99
+ Category.where(name: "Berlin").first.root.should eql @root
100
+ Category.where(name: "Germany").first.root.should eql @root
101
+ Category.where(name: "Root").first.root.should eql @root
102
+ end
103
+ end
104
+
105
+ describe '#root_id' do
106
+ it 'should return the id of the root of the tree of the given node' do
107
+ Category.where(name: "Pankow").first.root_id.should eql @root.id
108
+ Category.where(name: "Berlin").first.root_id.should eql @root.id
109
+ Category.where(name: "Germany").first.root_id.should eql @root.id
110
+ Category.where(name: "Root").first.root_id.should eql @root.id
111
+ end
112
+ end
113
+
114
+ describe '#is_root?' do
115
+ it 'should return true if given node is a root' do
116
+ Category.where(name: "Root").first.is_root?.should be_true
117
+ end
118
+ it 'should return false if given node is no root' do
119
+ Category.where(name: "Pankow").first.is_root?.should be_false
120
+ Category.where(name: "Germany").first.is_root?.should be_false
121
+ Category.where(name: "Berlin").first.is_root?.should be_false
122
+ Category.where(name: "Bern").first.is_root?.should be_false
123
+ end
124
+ end
125
+
126
+ describe '#children' do
127
+ it 'should return the given nodes children scoped' do
128
+ children_scope = Category.where(name: "Germany").first.children
129
+ children_scope.is_a?(Mongoid::Criteria).should be_true
130
+ children_scope.count.should == 3
131
+ children_scope.to_a.include?(@berlin).should be_true
132
+ children_scope.to_a.include?(@hamburg).should be_true
133
+ children_scope.to_a.include?(@munich).should be_true
134
+ end
135
+ end
136
+
137
+ describe '#child_ids' do
138
+ it 'should return the given nodes childs ids' do
139
+ ids = Category.where(name: "Germany").first.child_ids
140
+ ids.count.should == 3
141
+ ids.include?(@berlin.id).should be_true
142
+ ids.include?(@hamburg.id).should be_true
143
+ ids.include?(@munich.id).should be_true
144
+ end
145
+ end
146
+
147
+ describe '#has_children?' do
148
+ it 'should return true if given node has children' do
149
+ Category.where(name: "Root").first.has_children?.should be_true
150
+ Category.where(name: "Germany").first.has_children?.should be_true
151
+ Category.where(name: "Berlin").first.has_children?.should be_true
152
+ end
153
+ it 'should return false if given node has no children' do
154
+ Category.where(name: "Pankow").first.has_children?.should be_false
155
+ Category.where(name: "Bern").first.has_children?.should be_false
156
+ Category.where(name: "Zurich").first.has_children?.should be_false
157
+ end
158
+ end
159
+
160
+ describe '#is_childless?' do
161
+ it 'should return true if given node has no children' do
162
+ Category.where(name: "Root").first.is_childless?.should be_false
163
+ Category.where(name: "Germany").first.is_childless?.should be_false
164
+ Category.where(name: "Berlin").first.is_childless?.should be_false
165
+ end
166
+ it 'should return false if given node has children' do
167
+ Category.where(name: "Pankow").first.is_childless?.should be_true
168
+ Category.where(name: "Bern").first.is_childless?.should be_true
169
+ Category.where(name: "Zurich").first.is_childless?.should be_true
170
+ end
171
+ end
172
+
173
+ describe '#siblings' do
174
+ it 'should return the given nodes siblings scoped' do
175
+ siblings_scope = Category.where(name: "Berlin").first.siblings
176
+ siblings_scope.is_a?(Mongoid::Criteria).should be_true
177
+ siblings_scope.to_a.size.should == 2
178
+ siblings_scope.to_a.include?(@hamburg).should be_true
179
+ siblings_scope.to_a.include?(@munich).should be_true
180
+ end
181
+ end
182
+
183
+ describe '#sibling_ids' do
184
+ it 'should return the given nodes siblings ids' do
185
+ ids = Category.where(name: "Berlin").first.sibling_ids
186
+ ids.size.should == 2
187
+ ids.include?(@hamburg.id).should be_true
188
+ ids.include?(@munich.id).should be_true
189
+ end
190
+ end
191
+
192
+ describe '#has_siblings?' do
193
+ it 'should return true if given node has siblings' do
194
+ Category.where(name: "Berlin").first.has_siblings?.should be_true
195
+ Category.where(name: "Bern").first.has_siblings?.should be_true
196
+ Category.where(name: "Germany").first.has_siblings?.should be_true
197
+ end
198
+ it 'should return false if given node has no siblings' do
199
+ Category.where(name: "Root").first.has_siblings?.should be_false
200
+ Category.where(name: "Pankow").first.has_siblings?.should be_false
201
+ end
202
+ end
203
+
204
+ describe '#is_only_child?' do
205
+ it 'should return true if given node has no siblings' do
206
+ Category.where(name: "Berlin").first.is_only_child?.should be_false
207
+ Category.where(name: "Bern").first.is_only_child?.should be_false
208
+ Category.where(name: "Germany").first.is_only_child?.should be_false
209
+ end
210
+ it 'should return false if given node has siblings' do
211
+ Category.where(name: "Root").first.is_only_child?.should be_true
212
+ Category.where(name: "Pankow").first.is_only_child?.should be_true
213
+ end
214
+ end
215
+
216
+ describe '#descendants' do
217
+ it 'should return the given nodes descendants scoped' do
218
+ desc_scope = Category.where(name: "Germany").first.descendants
219
+ desc_scope.is_a?(Mongoid::Criteria).should be_true
220
+ desc_scope.to_a.size.should == 4
221
+ desc_scope.to_a.include?(@berlin).should be_true
222
+ desc_scope.to_a.include?(@hamburg).should be_true
223
+ desc_scope.to_a.include?(@munich).should be_true
224
+ desc_scope.to_a.include?(@pankow).should be_true
225
+ end
226
+ end
227
+
228
+ describe '#descendant_ids' do
229
+ it 'should return the given nodes descendants ids' do
230
+ ids = Category.where(name: "Germany").first.descendant_ids
231
+ ids.size.should == 4
232
+ ids.include?(@berlin.id).should be_true
233
+ ids.include?(@hamburg.id).should be_true
234
+ ids.include?(@munich.id).should be_true
235
+ ids.include?(@pankow.id).should be_true
236
+ end
237
+ end
238
+
239
+ describe '#subtree' do
240
+ it 'should return the given nodes subtree including the node itself' do
241
+ subtree_scope = Category.where(name: "Germany").first.subtree
242
+ subtree_scope.is_a?(Mongoid::Criteria).should be_true
243
+ subtree_scope.to_a.size.should == 5
244
+ subtree_scope.to_a.include?(@germany).should be_true
245
+ subtree_scope.to_a.include?(@berlin).should be_true
246
+ subtree_scope.to_a.include?(@hamburg).should be_true
247
+ subtree_scope.to_a.include?(@munich).should be_true
248
+ subtree_scope.to_a.include?(@pankow).should be_true
249
+ end
250
+ end
251
+
252
+ describe '#subtree_ids' do
253
+ it 'should return the ids of the given nodes subtree including the code itself' do
254
+ ids = Category.where(name: "Germany").first.subtree_ids
255
+ ids.size.should == 5
256
+ ids.include?(@germany.id).should be_true
257
+ ids.include?(@berlin.id).should be_true
258
+ ids.include?(@hamburg.id).should be_true
259
+ ids.include?(@munich.id).should be_true
260
+ ids.include?(@pankow.id).should be_true
261
+ end
262
+ end
263
+
264
+ describe '#depth' do
265
+ it 'should return the computed depth of the given node' do
266
+ Category.all.each do |category|
267
+ category.depth.should == category.persisted_depth
268
+ end
269
+ end
270
+ end
271
+
272
+ describe '#object_for' do
273
+ it 'should return the correct object if object was given' do
274
+ Category.object_for(Category.where(name: "Berlin").first).should == @berlin
275
+ Category.object_for(Category.where(name: "Germany").first).should == @germany
276
+ Category.object_for(Category.where(name: "Root").first).should == @root
277
+ end
278
+ it 'should return the correct object if object_id was given as BSON::ObjectId' do
279
+ Category.object_for(Category.where(name: "Berlin").first.id).should == @berlin
280
+ Category.object_for(Category.where(name: "Germany").first.id).should == @germany
281
+ Category.object_for(Category.where(name: "Root").first.id).should == @root
282
+ end
283
+ it 'should return the correct object if object_id was given as String' do
284
+ Category.object_for(Category.where(name: "Berlin").first.id.to_s).should == @berlin
285
+ Category.object_for(Category.where(name: "Germany").first.id.to_s).should == @germany
286
+ Category.object_for(Category.where(name: "Root").first.id.to_s).should == @root
287
+ end
288
+ it 'should return nil in case an empty string is given as object_id' do
289
+ Category.object_for("").should be_nil
290
+ end
291
+ end
292
+
293
+ describe '#roots' do
294
+ it 'should return all available roots scoped' do
295
+ roots_scope = Category.roots
296
+ roots_scope.is_a?(Mongoid::Criteria).should be_true
297
+ roots_scope.to_a.size.should == 1
298
+ roots_scope.to_a.include?(@root).should be_true
299
+ end
300
+ end
301
+
302
+ describe '#ancestors_of node' do
303
+ it 'should return the given nodes ancestors scoped' do
304
+ anc_scope = Category.ancestors_of(Category.where(name: "Pankow").first)
305
+ anc_scope.is_a?(Mongoid::Criteria).should be_true
306
+ anc_scope.to_a.size.should == 3
307
+ anc_scope.to_a.include?(@berlin).should be_true
308
+ anc_scope.to_a.include?(@germany).should be_true
309
+ anc_scope.to_a.include?(@root).should be_true
310
+ end
311
+ end
312
+
313
+ describe '#children_of node' do
314
+ it 'should return the given nodes children scoped' do
315
+ child_scope = Category.children_of(Category.where(name: "Germany").first)
316
+ child_scope.is_a?(Mongoid::Criteria).should be_true
317
+ child_scope.to_a.size.should == 3
318
+ child_scope.to_a.include?(@berlin).should be_true
319
+ child_scope.to_a.include?(@hamburg).should be_true
320
+ child_scope.to_a.include?(@munich).should be_true
321
+ end
322
+ end
323
+
324
+ describe '#descendants_of node' do
325
+ it 'should return the given nodes descendants scoped' do
326
+ desc_scope = Category.descendants_of(Category.where(name: "Germany").first)
327
+ desc_scope.is_a?(Mongoid::Criteria).should be_true
328
+ desc_scope.to_a.size.should == 4
329
+ desc_scope.to_a.include?(@berlin).should be_true
330
+ desc_scope.to_a.include?(@munich).should be_true
331
+ desc_scope.to_a.include?(@hamburg).should be_true
332
+ desc_scope.to_a.include?(@pankow).should be_true
333
+ end
334
+ end
335
+
336
+ describe '#subtree_of node' do
337
+ it 'should return the given nodes subtree scoped' do
338
+ subtree_scope = Category.subtree_of(Category.where(name: "Germany").first)
339
+ subtree_scope.is_a?(Mongoid::Criteria).should be_true
340
+ subtree_scope.to_a.size.should == 5
341
+ subtree_scope.to_a.include?(@germany).should be_true
342
+ subtree_scope.to_a.include?(@berlin).should be_true
343
+ subtree_scope.to_a.include?(@munich).should be_true
344
+ subtree_scope.to_a.include?(@hamburg).should be_true
345
+ subtree_scope.to_a.include?(@pankow).should be_true
346
+ end
347
+ end
348
+
349
+ describe '#siblings_of node' do
350
+ it 'should return the given nodes siblings scoped' do
351
+ siblings_scope = Category.siblings_of(Category.where(name: "Berlin").first)
352
+ siblings_scope.is_a?(Mongoid::Criteria).should be_true
353
+ siblings_scope.to_a.size.should == 2
354
+ siblings_scope.to_a.include?(@hamburg).should be_true
355
+ siblings_scope.to_a.include?(@munich).should be_true
356
+ end
357
+ end
358
+
359
+ describe '#before_depth depth' do
360
+ it 'should return a scope finding objects with a depth less than given depth' do
361
+ amounts = { 0 => 0, 1 => 1, 2 => 4, 3 => 11, 4 => 12 }
362
+ 0.upto(3) do |depth|
363
+ Category.before_depth(depth).size.should == amounts[depth]
364
+ Category.before_depth(depth).each do |category|
365
+ category.persisted_depth.should < depth
366
+ end
367
+ end
368
+ end
369
+ end
370
+
371
+ describe '#to_depth depth' do
372
+ it 'should return a scope finding objects with a depth less or equal than given depth' do
373
+ amounts = { 0 => 1, 1 => 4, 2 => 11, 3 => 12 }
374
+ 0.upto(3) do |depth|
375
+ Category.to_depth(depth).size.should == amounts[depth]
376
+ Category.before_depth(depth).each do |category|
377
+ category.persisted_depth.should <= depth
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ describe '#at_depth depth' do
384
+ it 'should return a scope finding objects with that exact given depth' do
385
+ amounts = { 0 => 1, 1 => 3, 2 => 7, 3 => 1 }
386
+ 0.upto(3) do |depth|
387
+ Category.at_depth(depth).size.should == amounts[depth]
388
+ Category.at_depth(depth).each do |category|
389
+ category.persisted_depth.should == depth
390
+ end
391
+ end
392
+ end
393
+ end
394
+
395
+ describe '#from_depth depth' do
396
+ it 'should return a scope finding objects with a depth greater or equal than given depth' do
397
+ amounts = { 0 => 12, 1 => 11, 2 => 8, 3 => 1 }
398
+ 0.upto(3) do |depth|
399
+ Category.from_depth(depth).size.should == amounts[depth]
400
+ Category.from_depth(depth).each do |category|
401
+ category.persisted_depth.should >= depth
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ describe '#after_depth depth' do
408
+ it 'should return a scope finding objects with a depth greater than given depth' do
409
+ amounts = { 0 => 11, 1 => 8, 2 => 1, 3 => 0 }
410
+ 0.upto(3) do |depth|
411
+ Category.after_depth(depth).size.should == amounts[depth]
412
+ Category.after_depth(depth).each do |category|
413
+ category.persisted_depth.should > depth
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+
420
+ context "with new nodes" do
421
+ describe '#build_ancestry' do
422
+ it 'should raise an error in case parent and parent_id were given' do
423
+ lambda{ Category.create(name: "Error", parent: Category.first, parent_id: Category.first.id) }.should raise_error "Either parent or parent_id can be given, not both at once"
424
+ end
425
+
426
+ it 'should set the ancestry string' do
427
+ category = Category.create!(name: "Correct country", parent: Category.where(name: "Root").first)
428
+ category.reload.ancestry.should == Category.where(name: "Root").first.id.to_s
429
+ category = Category.create!(name: "Correct country2", parent_id: Category.where(name: "Root").first.id)
430
+ category.reload.ancestry.should == Category.where(name: "Root").first.id.to_s
431
+ end
432
+
433
+ it 'should set persisted depth' do
434
+ category = Category.create!(name: "Depth test", parent: Category.where(name: "Pankow").first)
435
+ category.reload.persisted_depth.should == 4
436
+ category = Category.create!(name: "Depth test2", parent: Category.where(name: "Root").first)
437
+ category.reload.persisted_depth.should == 1
438
+ end
439
+
440
+ it 'should not persist the parent object when given' do
441
+ category = Category.create!(name: "Persistence test", parent: Category.where(name: "Root").first)
442
+ category.reload.attributes.keys.include?(:parent).should be_false
443
+ end
444
+ it 'should not persist the parent_id when given' do
445
+ category = Category.create!(name: "Persistence test2", parent: Category.where(name: "Root").first)
446
+ category.reload.attributes.keys.include?(:parent_id).should be_false
447
+ end
448
+ end
449
+ end
450
+ end
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'mongoid'
5
+ require File.expand_path(File.dirname(__FILE__) + "/support/connection")
6
+ require File.expand_path(File.dirname(__FILE__) + "/support/category")
7
+ require 'rspec'
8
+ require 'mongestry'
9
+
10
+ # Requires supporting files with custom matchers and macros, etc,
11
+ # in ./support/ and its subdirectories.
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
+
14
+ RSpec.configure do |config|
15
+
16
+ end
@@ -0,0 +1,10 @@
1
+ require 'mongestry'
2
+
3
+ class Category
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+
7
+ field :name, type: String
8
+ field :persisted_depth, type: Integer
9
+ field :ancestry, type: String
10
+ end
@@ -0,0 +1,3 @@
1
+ Mongoid.configure do |config|
2
+ config.master = Mongo::Connection.new('localhost', 27017).db('mongestry_test')
3
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongestry
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 5
8
+ - 5
9
+ version: 0.5.5
10
+ platform: ruby
11
+ authors:
12
+ - Jan Roesner
13
+ - Lars Kluge
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-20 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bson_ext
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ prerelease: false
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: mongoid
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 2
56
+ - 3
57
+ - 0
58
+ version: 2.3.0
59
+ type: :development
60
+ prerelease: false
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: yard
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ - 6
72
+ - 0
73
+ version: 0.6.0
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: bundler
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ~>
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 1
86
+ - 0
87
+ - 0
88
+ version: 1.0.0
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
93
+ name: jeweler
94
+ requirement: &id006 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ segments:
100
+ - 1
101
+ - 6
102
+ - 2
103
+ version: 1.6.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: rcov
109
+ requirement: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: *id007
120
+ - !ruby/object:Gem::Dependency
121
+ name: linecache19
122
+ requirement: &id008 !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - "="
126
+ - !ruby/object:Gem::Version
127
+ segments:
128
+ - 0
129
+ - 5
130
+ - 11
131
+ version: 0.5.11
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: *id008
135
+ - !ruby/object:Gem::Dependency
136
+ name: ruby-debug19
137
+ requirement: &id009 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ segments:
143
+ - 0
144
+ version: "0"
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: *id009
148
+ description: Mongestry is Ancestry for Mongo, build for ORM Mongoid
149
+ email: jan.roesner@dailydeal.de lars.kluge@dailydeal.de
150
+ executables: []
151
+
152
+ extensions: []
153
+
154
+ extra_rdoc_files:
155
+ - LICENSE.txt
156
+ - README.rdoc
157
+ files:
158
+ - .document
159
+ - .rspec
160
+ - Gemfile
161
+ - Gemfile.lock
162
+ - LICENSE.txt
163
+ - README.rdoc
164
+ - Rakefile
165
+ - VERSION
166
+ - lib/mongestry.rb
167
+ - mongestry.gemspec
168
+ - spec/mongestry_spec.rb
169
+ - spec/spec_helper.rb
170
+ - spec/support/category.rb
171
+ - spec/support/connection.rb
172
+ has_rdoc: true
173
+ homepage: http://github.com/DailyDeal/mongestry
174
+ licenses:
175
+ - MIT
176
+ post_install_message:
177
+ rdoc_options: []
178
+
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ none: false
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ hash: 4258898673227910918
187
+ segments:
188
+ - 0
189
+ version: "0"
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ segments:
196
+ - 0
197
+ version: "0"
198
+ requirements: []
199
+
200
+ rubyforge_project:
201
+ rubygems_version: 1.3.7
202
+ signing_key:
203
+ specification_version: 3
204
+ summary: Mongestry is Ancestry for Mongo
205
+ test_files: []
206
+