crawfish 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: