crawfish 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8abfd49198064290901a2079e5507ece4a6e0d9
4
+ data.tar.gz: 5cb51b64f1f080e86e819b1eb6c47d60a883ef6f
5
+ SHA512:
6
+ metadata.gz: 4fd945203eeb50b33136c8bb1dd11aab30a7c361e648d255511c14ba1dbc9670c405747b7aecac1d41a5acf5c22430a34adf32f8add831afb8c1782c86871cf3
7
+ data.tar.gz: 6364b0fa66b978024c93794e60c5447673122f51a86d090ed4bfcf214af4c30303324c223b51acbc335bb6d1abe9861e223f767963921c4131ddad8a65eb6a2a
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test.db
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ - "2.1.1"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in crawfish.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 lwoodson
2
+
3
+ MIT License
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.
@@ -0,0 +1,29 @@
1
+ # Crawfish
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'crawfish'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install crawfish
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/crawfish/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/test*.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'crawfish/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "crawfish"
8
+ spec.version = Crawfish::VERSION
9
+ spec.authors = ["Lance Woodson"]
10
+ spec.email = ["lance@webmaneuvers.com"]
11
+ spec.summary = %q{Crawfishing for active record models. Yeehaw"}
12
+ spec.description = "Tree and flattened array data structures for traversing an ActiveRecord model graph"
13
+ spec.homepage = "https://github.com/lwoodson/crawfish"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake", "~> 0"
23
+ spec.add_development_dependency 'sqlite3', "~> 0"
24
+ spec.add_development_dependency "pry", "~> 0"
25
+ spec.add_runtime_dependency 'activerecord', '~> 3.1'
26
+ end
@@ -0,0 +1,54 @@
1
+ require "crawfish/version"
2
+ require "crawfish/node"
3
+
4
+ module Crawfish
5
+ class << self
6
+ attr_accessor :node_decorator
7
+
8
+ ##
9
+ # Returns trees of nodes for each entity. Will be an array
10
+ # of root nodes.
11
+ def trees(*args)
12
+ opts, *entities = extract(*args)
13
+ node_decorator = opts[:node_decorator] || NoOpDecorator
14
+ entities.map do |entity|
15
+ node_decorator.decorate(Node.new(entity, node_decorator: node_decorator))
16
+ end
17
+ end
18
+
19
+ ##
20
+ # Returns the flattened list of nodes for the specified
21
+ # entities. Can also accept a hash of options where
22
+ # :filter can be a lambda to filter results.
23
+ def nodes(*args)
24
+ opts, *entities = extract(*args)
25
+ filter = opts[:filter] ||= lambda{|n| true}
26
+ node_decorator = opts[:node_decorator] || NoOpDecorator
27
+ entities.map do |entity|
28
+ node_decorator.decorate(Node.new(entity, node_decorator: node_decorator)).flatten(filter)
29
+ end.flatten
30
+ end
31
+
32
+ ##
33
+ # Returns the unique list of models in the graphs
34
+ # from the specified entities.
35
+ def models(*entities)
36
+ visited_models = Set.new
37
+ filter = lambda {|n| visited_models.add?(n.model)}
38
+ nodes(*entities, filter: filter).map(&:model)
39
+ end
40
+
41
+ private
42
+ def extract(*args)
43
+ opts = args.last.is_a?(Hash) ? args.pop : {}
44
+ entities = args
45
+ [opts] + entities
46
+ end
47
+
48
+ class NoOpDecorator
49
+ def self.decorate(node)
50
+ node
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,117 @@
1
+ require 'set'
2
+
3
+ module Crawfish
4
+ ##
5
+ # A node within the graph of models from a root or entity node.
6
+ class Node
7
+ attr_reader :model, :ref_key, :reflection
8
+ attr_accessor :parent, :node_decorator
9
+ def initialize(model, opts={})
10
+ @model = model
11
+ @ref_key = opts[:ref_key]
12
+ @parent = opts[:parent]
13
+ @reflection = opts[:reflection]
14
+ @node_decorator = opts[:node_decorator]
15
+ end
16
+
17
+ ##
18
+ # Returns a flattened array of nodes from the node
19
+ def flatten(filter=lambda{|n| true}, result=Set.new)
20
+ result.add(self)
21
+ associated_nodes.reject(&visited_models(result)).select(&filter).each do |node|
22
+ result.add(node)
23
+ node.flatten(filter, result)
24
+ end
25
+ result.to_a
26
+ end
27
+
28
+ def path
29
+ if root?
30
+ model.to_s
31
+ else
32
+ "#{parent.path}/#{ref_key}"
33
+ end
34
+ end
35
+
36
+ def associated_nodes
37
+ @associated_nodes ||= model.reflections.map do |key, reflection|
38
+ node = Node.new reflection.klass, ref_key: key,
39
+ parent: self,
40
+ reflection: reflection,
41
+ node_decorator: node_decorator
42
+ if node_decorator
43
+ node = node_decorator.decorate(node)
44
+ end
45
+ node
46
+ end
47
+ end
48
+
49
+ def nodes_above
50
+ associated_nodes.select(&:above?)
51
+ end
52
+
53
+ def nodes_below
54
+ associated_nodes.select(&:below?)
55
+ end
56
+
57
+ def nodes_aside
58
+ associated_nodes.select(&:aside?)
59
+ end
60
+
61
+ def locate(path)
62
+ return self if path == model.to_s || path == ref_key.to_s
63
+
64
+ next_element, *other_elements = path.split('/')
65
+ if next_element == model.to_s
66
+ next_element, *other_elements = other_elements
67
+ end
68
+ return self unless next_element
69
+
70
+ next_node = associated_nodes.detect{|node| node.ref_key.to_s == next_element}
71
+ next_node.locate(other_elements.join("/"))
72
+ end
73
+
74
+ def other_model
75
+ reflection ? reflection.klass : nil
76
+ end
77
+
78
+ def root?
79
+ parent.nil?
80
+ end
81
+ alias_method :entity?, :root?
82
+
83
+ def has_one?
84
+ reflection && reflection.macro == :has_one
85
+ end
86
+
87
+ def belongs_to?
88
+ reflection && reflection.macro == :belongs_to
89
+ end
90
+
91
+ def has_many?
92
+ reflection && reflection.macro == :has_many
93
+ end
94
+
95
+ def has_and_belongs_to_many?
96
+ reflection && reflection.macro == :has_and_belongs_to_many
97
+ end
98
+
99
+ def through?
100
+ reflection && reflection.options[:through].present?
101
+ end
102
+
103
+ alias_method :above?, :belongs_to?
104
+ alias_method :below?, :has_many?
105
+
106
+ def aside?
107
+ has_one? || has_and_belongs_to_many?
108
+ end
109
+
110
+ private
111
+ def visited_models(result)
112
+ lambda do |node|
113
+ result.detect{|n| n.model == node.model}
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ module Crawfish
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ class Author < ActiveRecord::Base
2
+ has_one :author_detail
3
+ has_one :address, through: :author_detail
4
+ has_many :posts
5
+ has_many :comments, through: :posts
6
+ end
7
+
8
+ class Post < ActiveRecord::Base
9
+ belongs_to :author
10
+ has_many :comments
11
+ has_and_belongs_to_many :tags
12
+ end
13
+
14
+ class Comment < ActiveRecord::Base
15
+ belongs_to :post
16
+ end
17
+
18
+ class AuthorDetail < ActiveRecord::Base
19
+ belongs_to :author
20
+ has_one :address
21
+ end
22
+
23
+ class Address < ActiveRecord::Base
24
+ belongs_to :author_detail
25
+ end
26
+
27
+ class Tag < ActiveRecord::Base
28
+ has_and_belongs_to_many :posts
29
+ end
30
+
31
+ module Acme
32
+ class Product < ActiveRecord::Base
33
+ has_many :features
34
+ end
35
+
36
+ class Feature < ActiveRecord::Base
37
+ belongs_to :product
38
+ end
39
+ end
@@ -0,0 +1,80 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe Crawfish do
4
+ class NodeDecorator
5
+ def self.decorate(node)
6
+ node.define_singleton_method(:foo) {"bar"}
7
+ node
8
+ end
9
+ end
10
+ let(:node_decorator) {
11
+ NodeDecorator
12
+ }
13
+
14
+ describe "#trees" do
15
+ it "should return a node for a single entity" do
16
+ Crawfish.trees(Post).map(&:path).must_equal ["Post"]
17
+ end
18
+
19
+ it "should return multiple nodes for multiple entities" do
20
+ paths = Crawfish.trees(Post, Author, Comment).map(&:path)
21
+ paths.must_include "Post"
22
+ paths.must_include "Author"
23
+ paths.must_include "Comment"
24
+ end
25
+
26
+ it "should allow specification of a node decorator" do
27
+ trees = Crawfish.trees(Post, Author, node_decorator: node_decorator)
28
+ trees.each {|root_node| root_node.respond_to?(:foo).must_equal true}
29
+ end
30
+ end
31
+
32
+ describe "#nodes" do
33
+ it "should return the flattened nodes reachable from a root" do
34
+ paths = Crawfish.nodes(Post, Author).map(&:path)
35
+ paths.must_include "Post"
36
+ paths.must_include "Post/author"
37
+ paths.must_include "Post/author/author_detail"
38
+ paths.must_include "Post/comments"
39
+ paths.must_include "Post/tags"
40
+ paths.must_include "Author"
41
+ paths.must_include "Author/author_detail"
42
+ paths.must_include "Author/author_detail/address"
43
+ paths.must_include "Author/address"
44
+ paths.must_include "Author/posts"
45
+ paths.must_include "Author/posts/comments"
46
+ paths.must_include "Author/posts/tags"
47
+ end
48
+
49
+ it "should allow a filter to limit results" do
50
+ visited_models = Set.new
51
+ filter = lambda do |node|
52
+ node.ref_key.match(/comment/).nil?
53
+ end
54
+ paths = Crawfish.nodes(Post, Author, filter: filter).map(&:path)
55
+ paths.must_include "Post/author"
56
+ paths.wont_include "Post/comments"
57
+ paths.wont_include "Author/posts/comments"
58
+ end
59
+
60
+ it "should allow a node_decorator to decorate the flattened nodes" do
61
+ nodes = Crawfish.nodes(Post, Author, node_decorator: node_decorator)
62
+ nodes.each {|node| node.respond_to?(:foo).must_equal true}
63
+ end
64
+ end
65
+
66
+ describe "#models" do
67
+ it "should include distinct models exactly once" do
68
+ models = Crawfish.models(Author, Acme::Product)
69
+ models.must_include Author
70
+ models.must_include AuthorDetail
71
+ models.must_include Address
72
+ models.must_include Post
73
+ models.must_include Comment
74
+ models.must_include Tag
75
+ models.must_include Acme::Product
76
+ models.must_include Acme::Feature
77
+ models.size.must_equal 8
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,12 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'sqlite3'
4
+ require 'active_record'
5
+ require 'crawfish'
6
+
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => ':memory:'
10
+ )
11
+
12
+ require_relative 'model.rb'
@@ -0,0 +1,315 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe Crawfish::Node do
4
+ let(:root_node) {Crawfish::Node.new(Author)}
5
+ let(:child_node) {Crawfish::Node.new(Post, ref_key: :posts, parent: root_node)}
6
+ let(:grandchild_node) {Crawfish::Node.new(Comment, ref_key: :comments, parent: child_node)}
7
+ let(:belongs_to_node) {
8
+ Crawfish::Node.new(Post, reflection: Post.reflections[:author])
9
+ }
10
+ let(:has_one_node) {
11
+ Crawfish::Node.new(Author, reflection: Author.reflections[:author_detail])
12
+ }
13
+ let(:has_one_through_node) {
14
+ Crawfish::Node.new(Author, reflection: Author.reflections[:address])
15
+ }
16
+ let(:has_many_node) {
17
+ Crawfish::Node.new(Author, reflection: Author.reflections[:posts])
18
+ }
19
+ let(:has_many_through_node) {
20
+ Crawfish::Node.new(Author, reflection: Author.reflections[:comments])
21
+ }
22
+ let(:has_and_belongs_to_many_node) {
23
+ Crawfish::Node.new(Post, reflection: Post.reflections[:tags])
24
+ }
25
+
26
+ describe "#reflection" do
27
+ it "should be nil when a ref_key not present" do
28
+ root_node.reflection.must_equal nil
29
+ end
30
+
31
+ it "should be the reflection in the model's reflection hash when ref_key present" do
32
+ belongs_to_node.reflection.must_equal Post.reflections[:author]
33
+ end
34
+ end
35
+
36
+ describe "#path" do
37
+ it "should be the model when it is a root/entity" do
38
+ root_node.path.must_equal "Author"
39
+ end
40
+
41
+ it "should be the root model joined with associations using a / separator if not a root" do
42
+ child_node.path.must_equal "Author/posts"
43
+ end
44
+
45
+ it "should work with arbitrarily nested associations" do
46
+ grandchild_node.path.must_equal "Author/posts/comments"
47
+ end
48
+ end
49
+
50
+ describe "#associated_nodes" do
51
+ subject {Crawfish::Node.new(Author)}
52
+ let(:associated_node_paths) {subject.associated_nodes.map(&:path)}
53
+
54
+ it "should include has_one nodes" do
55
+ associated_node_paths.must_include "Author/author_detail"
56
+ end
57
+
58
+ it "should include has_one through nodes" do
59
+ associated_node_paths.must_include "Author/address"
60
+ end
61
+
62
+ it "should include has_many nodes" do
63
+ associated_node_paths.must_include "Author/posts"
64
+ end
65
+
66
+ it "should include has_many through nodes" do
67
+ associated_node_paths.must_include "Author/comments"
68
+ end
69
+
70
+ it "should include has_and_belongs_to_many nodes" do
71
+ Crawfish::Node.new(Post).associated_nodes.map(&:path).must_include "Post/tags"
72
+ end
73
+
74
+ it "should decorate associated nodes with the node decorator, if nay" do
75
+ class NodeDecorator
76
+ def self.decorate(node)
77
+ node.define_singleton_method(:foo) {"bar"}
78
+ node
79
+ end
80
+ end
81
+ node = Crawfish::Node.new(Post, node_decorator: NodeDecorator)
82
+ node.associated_nodes.each {|node| node.respond_to?(:foo).must_equal true}
83
+ end
84
+ end
85
+
86
+ describe "#nodes_above" do
87
+ it "should only include nodes for belongs_to relationships" do
88
+ Crawfish::Node.new(Post).nodes_above.map(&:path).must_equal ["Post/author"]
89
+ end
90
+ end
91
+
92
+ describe "#nodes_below" do
93
+ it "should include only nodes for the has_many relationships of the node's model" do
94
+ paths = Crawfish::Node.new(Author).nodes_below.map(&:path)
95
+ paths.must_include "Author/posts"
96
+ paths.must_include "Author/comments"
97
+ paths.wont_include "Author/author_detail"
98
+ paths.wont_include "Author/address"
99
+ end
100
+ end
101
+
102
+ describe "#nodes_aside" do
103
+ it "should include only nodes for the has_one and has_and_belongs_to_many relationships" do
104
+ paths = Crawfish::Node.new(Author).nodes_aside.map(&:path)
105
+ paths.must_include "Author/author_detail"
106
+ paths.must_include "Author/address"
107
+ paths.wont_include "Author/posts"
108
+ paths.wont_include "Author/comments"
109
+ end
110
+ end
111
+
112
+ describe "#locate" do
113
+ it "should be able to find a root node" do
114
+ root_node.locate('Author').model.must_equal Author
115
+ end
116
+
117
+ it "should be able to find a node directly beneath a root" do
118
+ root_node.locate('Author/posts').model.must_equal Post
119
+ end
120
+
121
+ it "should be able to find a node arbitrarily away from a root" do
122
+ root_node.locate('Author/posts/comments').model.must_equal Comment
123
+ end
124
+ end
125
+
126
+ describe "#flatten" do
127
+ it "should return all nodes reachable from the root with no filter" do
128
+ paths = root_node.flatten.map(&:path)
129
+ paths.must_include "Author"
130
+ paths.must_include "Author/author_detail"
131
+ paths.must_include "Author/address"
132
+ paths.must_include "Author/posts"
133
+ paths.must_include "Author/comments"
134
+ paths.must_include "Author/author_detail/address"
135
+ paths.must_include "Author/posts/comments"
136
+ paths.must_include "Author/posts/tags"
137
+ paths.size.must_equal 8
138
+ end
139
+
140
+ it "should allow flatten to be filtered via lambda" do
141
+ root = Crawfish::Node.new(Post)
142
+ filter = lambda{|n| !n.above?}
143
+ paths = root.flatten(filter).map(&:path)
144
+ paths.must_include "Post"
145
+ paths.must_include "Post/comments"
146
+ paths.must_include "Post/tags"
147
+ paths.wont_include "Post/author"
148
+ end
149
+
150
+ it "should work with non-root nodes, too" do
151
+ node = Crawfish::Node.new(Author).locate("Author/posts")
152
+ paths = node.flatten.map(&:path)
153
+ paths.must_include "Author/posts"
154
+ paths.must_include "Author/posts/comments"
155
+ paths.must_include "Author/posts/tags"
156
+ end
157
+
158
+ it "should work with models within module namespaces" do
159
+ root = Crawfish::Node.new(Acme::Product)
160
+ paths = root.flatten.map(&:path)
161
+ paths.must_include "Acme::Product"
162
+ paths.must_include "Acme::Product/features"
163
+ end
164
+ end
165
+
166
+ describe "#root?" do
167
+ it "should be true when ref_key not present" do
168
+ root_node.root?.must_equal true
169
+ end
170
+
171
+ it "should be false when ref_key present" do
172
+ child_node.root?.must_equal false
173
+ end
174
+ end
175
+
176
+ describe "#other_model" do
177
+ it "should result in the model on the other side of a reflection" do
178
+ has_one_node.other_model.must_equal AuthorDetail
179
+ end
180
+
181
+ it "should result to nil if a root node" do
182
+ root_node.other_model.must_be_nil
183
+ end
184
+ end
185
+
186
+ describe "#has_one?" do
187
+ it "should be true for has_one associations" do
188
+ has_one_node.has_one?.must_equal true
189
+ end
190
+
191
+ it "should be true for has_one through associations" do
192
+ has_one_through_node.has_one?.must_equal true
193
+ end
194
+
195
+ it "should be false for belongs_to associations" do
196
+ belongs_to_node.has_one?.must_equal false
197
+ end
198
+
199
+ it "should be false for has_many associations" do
200
+ has_many_node.has_one?.must_equal false
201
+ end
202
+
203
+ it "should be false for has_many_through_associations" do
204
+ has_many_through_node.has_one?.must_equal false
205
+ end
206
+
207
+ it "should be false for has_and_belongs_to_many_associations" do
208
+ has_and_belongs_to_many_node.has_one?.must_equal false
209
+ end
210
+ end
211
+
212
+ describe "#belongs_to?" do
213
+ it "should be true for belongs_to associations" do
214
+ belongs_to_node.belongs_to?.must_equal true
215
+ end
216
+
217
+ it "should be false for has_many associations" do
218
+ has_many_node.belongs_to?.must_equal false
219
+ end
220
+
221
+ it "should be false for has_many through associations" do
222
+ has_many_through_node.belongs_to?.must_equal false
223
+ end
224
+
225
+ it "should be false for has_one associations" do
226
+ has_one_node.belongs_to?.must_equal false
227
+ end
228
+
229
+ it "should be false for has_one through associations" do
230
+ has_one_through_node.belongs_to?.must_equal false
231
+ end
232
+
233
+ it "should be false for has_and_belongs_to_many associations" do
234
+ has_and_belongs_to_many_node.belongs_to?.must_equal false
235
+ end
236
+ end
237
+
238
+ describe "#has_many?" do
239
+ it "should be false for has_one associations" do
240
+ has_one_node.has_many?.must_equal false
241
+ end
242
+
243
+ it "should be false for has_one through associations" do
244
+ has_one_through_node.has_many?.must_equal false
245
+ end
246
+
247
+ it "should be false for belongs_to associations" do
248
+ belongs_to_node.has_many?.must_equal false
249
+ end
250
+
251
+ it "should be true for has_many associations" do
252
+ has_many_node.has_many?.must_equal true
253
+ end
254
+
255
+ it "should be true for has_many through associations" do
256
+ has_many_through_node.has_many?.must_equal true
257
+ end
258
+
259
+ it "should be false for has_and_belongs_to_many associations" do
260
+ has_and_belongs_to_many_node.has_many?.must_equal false
261
+ end
262
+ end
263
+
264
+ describe "#has_and_belongs_to_many?" do
265
+ it "should be false for has_one associations" do
266
+ has_one_node.has_and_belongs_to_many?.must_equal false
267
+ end
268
+
269
+ it "should be false for has_one through associations" do
270
+ has_one_through_node.has_and_belongs_to_many?.must_equal false
271
+ end
272
+
273
+ it "should be false for belongs_to associations" do
274
+ belongs_to_node.has_and_belongs_to_many?.must_equal false
275
+ end
276
+
277
+ it "should be false for has_many associations" do
278
+ has_many_node.has_and_belongs_to_many?.must_equal false
279
+ end
280
+
281
+ it "should be false for has_many through associations" do
282
+ has_many_through_node.has_and_belongs_to_many?.must_equal false
283
+ end
284
+
285
+ it "should be true for has_and_belongs_to_many associations" do
286
+ has_and_belongs_to_many_node.has_and_belongs_to_many?.must_equal true
287
+ end
288
+ end
289
+
290
+ describe "#through?" do
291
+ it "should be false for has_one associations" do
292
+ has_one_node.through?.must_equal false
293
+ end
294
+
295
+ it "should be true for has_one through associations" do
296
+ has_one_through_node.through?.must_equal true
297
+ end
298
+
299
+ it "should be false for belongs_to associations" do
300
+ belongs_to_node.through?.must_equal false
301
+ end
302
+
303
+ it "should be false for has_many associations" do
304
+ has_many_node.through?.must_equal false
305
+ end
306
+
307
+ it "should be true for has_many through associations" do
308
+ has_many_through_node.through?.must_equal true
309
+ end
310
+
311
+ it "should be false for has_and_belongs_to_many associations" do
312
+ has_and_belongs_to_many_node.through?.must_equal false
313
+ end
314
+ end
315
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crawfish
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Lance Woodson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
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: sqlite3
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: pry
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
+ - !ruby/object:Gem::Dependency
70
+ name: activerecord
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ description: Tree and flattened array data structures for traversing an ActiveRecord
84
+ model graph
85
+ email:
86
+ - lance@webmaneuvers.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - crawfish.gemspec
98
+ - lib/crawfish.rb
99
+ - lib/crawfish/node.rb
100
+ - lib/crawfish/version.rb
101
+ - test/model.rb
102
+ - test/test_crawfish.rb
103
+ - test/test_helper.rb
104
+ - test/test_node.rb
105
+ homepage: https://github.com/lwoodson/crawfish
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.2.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Crawfishing for active record models. Yeehaw"
129
+ test_files:
130
+ - test/model.rb
131
+ - test/test_crawfish.rb
132
+ - test/test_helper.rb
133
+ - test/test_node.rb
134
+ has_rdoc: