nested_has_many_through 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +94 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +61 -0
  6. data/Rakefile +12 -0
  7. data/init.rb +2 -0
  8. data/lib/nested_has_many_through/association.rb +132 -0
  9. data/lib/nested_has_many_through/reflection.rb +14 -0
  10. data/lib/nested_has_many_through/version.rb +3 -0
  11. data/lib/nested_has_many_through.rb +15 -0
  12. data/nested_has_many_through.gemspec +32 -0
  13. data/spec/models/author_spec.rb +84 -0
  14. data/spec/models/commenter_spec.rb +108 -0
  15. data/spec/rails_app/.gitignore +4 -0
  16. data/spec/rails_app/Gemfile +31 -0
  17. data/spec/rails_app/Gemfile.lock +74 -0
  18. data/spec/rails_app/README +13 -0
  19. data/spec/rails_app/Rakefile +7 -0
  20. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  21. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  22. data/spec/rails_app/app/models/author.rb +8 -0
  23. data/spec/rails_app/app/models/category.rb +3 -0
  24. data/spec/rails_app/app/models/comment.rb +4 -0
  25. data/spec/rails_app/app/models/post.rb +17 -0
  26. data/spec/rails_app/app/models/user.rb +7 -0
  27. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  28. data/spec/rails_app/config/application.rb +42 -0
  29. data/spec/rails_app/config/boot.rb +6 -0
  30. data/spec/rails_app/config/database.yml +22 -0
  31. data/spec/rails_app/config/environment.rb +5 -0
  32. data/spec/rails_app/config/environments/development.rb +26 -0
  33. data/spec/rails_app/config/environments/production.rb +49 -0
  34. data/spec/rails_app/config/environments/test.rb +35 -0
  35. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  36. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  37. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  38. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  39. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  40. data/spec/rails_app/config/locales/en.yml +5 -0
  41. data/spec/rails_app/config/routes.rb +58 -0
  42. data/spec/rails_app/config.ru +4 -0
  43. data/spec/rails_app/db/schema.rb +31 -0
  44. data/spec/rails_app/db/seeds.rb +7 -0
  45. data/spec/rails_app/script/delayed_job +5 -0
  46. data/spec/rails_app/script/gitr +14 -0
  47. data/spec/rails_app/script/rails +6 -0
  48. data/spec/spec_helper.rb +10 -0
  49. metadata +197 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .*
2
+ !.gitignore
3
+ pkg
4
+ doc/*
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ # gem "rails"
data/Gemfile.lock ADDED
@@ -0,0 +1,94 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nested_has_many_through (0.0.1)
5
+ rails (>= 3.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ abstract (1.0.0)
11
+ actionmailer (3.0.6)
12
+ actionpack (= 3.0.6)
13
+ mail (~> 2.2.15)
14
+ actionpack (3.0.6)
15
+ activemodel (= 3.0.6)
16
+ activesupport (= 3.0.6)
17
+ builder (~> 2.1.2)
18
+ erubis (~> 2.6.6)
19
+ i18n (~> 0.5.0)
20
+ rack (~> 1.2.1)
21
+ rack-mount (~> 0.6.14)
22
+ rack-test (~> 0.5.7)
23
+ tzinfo (~> 0.3.23)
24
+ activemodel (3.0.6)
25
+ activesupport (= 3.0.6)
26
+ builder (~> 2.1.2)
27
+ i18n (~> 0.5.0)
28
+ activerecord (3.0.6)
29
+ activemodel (= 3.0.6)
30
+ activesupport (= 3.0.6)
31
+ arel (~> 2.0.2)
32
+ tzinfo (~> 0.3.23)
33
+ activeresource (3.0.6)
34
+ activemodel (= 3.0.6)
35
+ activesupport (= 3.0.6)
36
+ activesupport (3.0.6)
37
+ arel (2.0.9)
38
+ builder (2.1.2)
39
+ diff-lcs (1.1.2)
40
+ erubis (2.6.6)
41
+ abstract (>= 1.0.0)
42
+ i18n (0.5.0)
43
+ mail (2.2.15)
44
+ activesupport (>= 2.3.6)
45
+ i18n (>= 0.4.0)
46
+ mime-types (~> 1.16)
47
+ treetop (~> 1.4.8)
48
+ mime-types (1.16)
49
+ polyglot (0.3.1)
50
+ rack (1.2.2)
51
+ rack-mount (0.6.14)
52
+ rack (>= 1.0.0)
53
+ rack-test (0.5.7)
54
+ rack (>= 1.0)
55
+ rails (3.0.6)
56
+ actionmailer (= 3.0.6)
57
+ actionpack (= 3.0.6)
58
+ activerecord (= 3.0.6)
59
+ activeresource (= 3.0.6)
60
+ activesupport (= 3.0.6)
61
+ bundler (~> 1.0)
62
+ railties (= 3.0.6)
63
+ railties (3.0.6)
64
+ actionpack (= 3.0.6)
65
+ activesupport (= 3.0.6)
66
+ rake (>= 0.8.7)
67
+ thor (~> 0.14.4)
68
+ rake (0.8.7)
69
+ rspec (2.5.0)
70
+ rspec-core (~> 2.5.0)
71
+ rspec-expectations (~> 2.5.0)
72
+ rspec-mocks (~> 2.5.0)
73
+ rspec-core (2.5.1)
74
+ rspec-expectations (2.5.0)
75
+ diff-lcs (~> 1.1.2)
76
+ rspec-mocks (2.5.0)
77
+ sqlite3 (1.3.3)
78
+ sqlite3-ruby (1.3.3)
79
+ sqlite3 (>= 1.3.3)
80
+ thor (0.14.6)
81
+ treetop (1.4.9)
82
+ polyglot (>= 0.3.1)
83
+ tzinfo (0.3.26)
84
+ yard (0.6.7)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ bundler (>= 1.0.0)
91
+ nested_has_many_through!
92
+ rspec (~> 2.5.0)
93
+ sqlite3-ruby
94
+ yard
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Ian White - ian.w.white@gmail.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = NestedHasManyThrough
2
+
3
+ This gem makes it possible to define has_many :through relationships that
4
+ go through other has_many :through relationships, possibly through an
5
+ arbitrarily deep hierarchy. This allows associations across any number of
6
+ tables to be constructed, without having to resort to find_by_sql (which isn't
7
+ a suitable solution if you need to do eager loading through :include as well).
8
+
9
+ NOTE: Rails 3.x supports only. Rails 2.3 users might want to try the original nested_has_many_through[https://github.com/ianwhite/nested_has_many_through] plugin
10
+
11
+ == Intallation
12
+
13
+ Install the gem:
14
+
15
+ gem install nested_has_many_through
16
+
17
+ Add it to your gem file::
18
+
19
+ gem "nested_has_many_through"
20
+
21
+ == Usage
22
+
23
+ class Author < User
24
+ has_many :posts
25
+ has_many :categories, :through => :posts, :uniq => true
26
+ has_many :similar_posts, :through => :categories, :source => :posts
27
+ has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
28
+ has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
29
+ has_many :commenters, :through => :posts, :uniq => true
30
+ end
31
+
32
+ class Post < ActiveRecord::Base
33
+ belongs_to :author
34
+ belongs_to :category
35
+ has_many :comments
36
+ has_many :commenters, :through => :comments, :source => :user, :uniq => true
37
+ end
38
+
39
+ The first two has_manys of Author are plain vanilla, the last four are what this plugin enables
40
+
41
+ # has_many through a has_many :through
42
+ has_many :similar_posts, :through => :categories, :source => :posts
43
+
44
+ # doubly nested has_many :through
45
+ has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
46
+
47
+ # whoah!
48
+ has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
49
+
50
+ # has_many through a has_many :through in another model
51
+ has_many :commenters, :through => :posts, :uniq => true
52
+
53
+ == Maintainers
54
+
55
+ * Roman V. Babenko (http://github.com/romanvbabenko)
56
+
57
+ == Copyrights
58
+
59
+ This gem based on code nested_has_many_through[https://github.com/ianwhite/nested_has_many_through] plugin
60
+
61
+ MIT License. Copyright (c) 2008 Ian White - ian.w.white@gmail.com
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ desc 'Default: run tests for nested_has_many_through'
7
+ task :default => :spec
8
+
9
+ require 'rspec/core/rake_task'
10
+ RSpec::Core::RakeTask.new(:spec)
11
+ task :default => :spec
12
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'nested_has_many_through'
2
+
@@ -0,0 +1,132 @@
1
+ module NestedHasManyThrough
2
+ module Association
3
+ def self.included(base)
4
+ base.class_eval do
5
+ def construct_conditions
6
+ @nested_join_attributes ||= construct_nested_join_attributes
7
+ if @reflection.through_reflection && @reflection.through_reflection.macro == :belongs_to
8
+ "#{@nested_join_attributes[:remote_key]} = #{belongs_to_quoted_key} #{@nested_join_attributes[:conditions]}"
9
+ else
10
+ "#{@nested_join_attributes[:remote_key]} = #{@owner.quoted_id} #{@nested_join_attributes[:conditions]}"
11
+ end
12
+ "#{@nested_join_attributes[:remote_key]} = #{@owner.quoted_id} #{@nested_join_attributes[:conditions]}"
13
+ end
14
+
15
+ def construct_joins(custom_joins = nil)
16
+ @nested_join_attributes ||= construct_nested_join_attributes
17
+ "#{@nested_join_attributes[:joins]} #{custom_joins}"
18
+ end
19
+ end
20
+ end
21
+
22
+ protected
23
+ # Given any belongs_to or has_many (including has_many :through) association,
24
+ # return the essential components of a join corresponding to that association, namely:
25
+ #
26
+ # * <tt>:joins</tt>: any additional joins required to get from the association's table
27
+ # (reflection.table_name) to the table that's actually joining to the active record's table
28
+ # * <tt>:remote_key</tt>: the name of the key in the join table (qualified by table name) which will join
29
+ # to a field of the active record's table
30
+ # * <tt>:local_key</tt>: the name of the key in the local table (not qualified by table name) which will
31
+ # take part in the join
32
+ # * <tt>:conditions</tt>: any additional conditions (e.g. filtering by type for a polymorphic association,
33
+ # or a :conditions clause explicitly given in the association), including a leading AND
34
+ def construct_nested_join_attributes(reflection = @reflection, association_class = reflection.klass,
35
+ table_ids = {association_class.table_name => 1})
36
+ if reflection.macro == :has_many && reflection.through_reflection
37
+ construct_has_many_through_attributes(reflection, table_ids)
38
+ else
39
+ construct_has_many_or_belongs_to_attributes(reflection, association_class, table_ids)
40
+ end
41
+ end
42
+
43
+ def construct_has_many_through_attributes(reflection, table_ids)
44
+ # Construct the join components of the source association, so that we have a path from
45
+ # the eventual target table of the association up to the table named in :through, and
46
+ # all tables involved are allocated table IDs.
47
+ source_attrs = construct_nested_join_attributes(reflection.source_reflection, reflection.klass, table_ids)
48
+
49
+ # Determine the alias of the :through table; this will be the last table assigned
50
+ # when constructing the source join components above.
51
+ through_table_alias = through_table_name = reflection.through_reflection.table_name
52
+ through_table_alias += "_#{table_ids[through_table_name]}" unless table_ids[through_table_name] == 1
53
+
54
+ # Construct the join components of the through association, so that we have a path to
55
+ # the active record's table.
56
+ through_attrs = construct_nested_join_attributes(reflection.through_reflection, reflection.through_reflection.klass, table_ids)
57
+
58
+ # Any subsequent joins / filters on owner attributes will act on the through association,
59
+ # so that's what we return for the conditions/keys of the overall association.
60
+ conditions = through_attrs[:conditions]
61
+ conditions += " AND #{interpolate_sql(reflection.klass.send(:sanitize_sql, reflection.options[:conditions]))}" if reflection.options[:conditions]
62
+
63
+ {
64
+ :joins => "%s INNER JOIN %s ON ( %s = %s.%s %s) %s %s" % [
65
+ source_attrs[:joins],
66
+ through_table_name == through_table_alias ? through_table_name : "#{through_table_name} #{through_table_alias}",
67
+ source_attrs[:remote_key],
68
+ through_table_alias, source_attrs[:local_key],
69
+ source_attrs[:conditions],
70
+ through_attrs[:joins],
71
+ reflection.options[:joins]
72
+ ],
73
+ :remote_key => through_attrs[:remote_key],
74
+ :local_key => through_attrs[:local_key],
75
+ :conditions => conditions
76
+ }
77
+ end
78
+
79
+ # reflection is not has_many :through; it's a standard has_many / belongs_to instead
80
+ # TODO: see if we can defer to rails code here a bit more
81
+ def construct_has_many_or_belongs_to_attributes(reflection, association_class, table_ids)
82
+ # Determine the alias used for remote_table_name, if any. In all cases this will already
83
+ # have been assigned an ID in table_ids (either through being involved in a previous join,
84
+ # or - if it's the first table in the query - as the default value of table_ids)
85
+ remote_table_alias = remote_table_name = association_class.table_name
86
+ remote_table_alias += "_#{table_ids[remote_table_name]}" unless table_ids[remote_table_name] == 1
87
+
88
+ # Assign a new alias for the local table.
89
+ local_table_alias = local_table_name = reflection.active_record.table_name
90
+ if table_ids[local_table_name]
91
+ table_id = table_ids[local_table_name] += 1
92
+ local_table_alias += "_#{table_id}"
93
+ else
94
+ table_ids[local_table_name] = 1
95
+ end
96
+
97
+ conditions = ''
98
+ # Add type_condition, if applicable
99
+ conditions += " AND #{association_class.send(:type_condition).to_sql}" if association_class.finder_needs_type_condition?
100
+ # Add custom conditions
101
+ conditions += " AND (#{interpolate_sql(association_class.send(:sanitize_sql, reflection.options[:conditions]))})" if reflection.options[:conditions]
102
+
103
+ if reflection.macro == :belongs_to
104
+ if reflection.options[:polymorphic]
105
+ conditions += " AND #{local_table_alias}.#{reflection.options[:foreign_type]} = #{reflection.active_record.quote_value(association_class.base_class.name.to_s)}"
106
+ end
107
+ {
108
+ :joins => reflection.options[:joins],
109
+ :remote_key => "#{remote_table_alias}.#{association_class.primary_key}",
110
+ :local_key => reflection.primary_key_name,
111
+ :conditions => conditions
112
+ }
113
+ else
114
+ # Association is has_many (without :through)
115
+ if reflection.options[:as]
116
+ conditions += " AND #{remote_table_alias}.#{reflection.options[:as]}_type = #{reflection.active_record.quote_value(reflection.active_record.base_class.name.to_s)}"
117
+ end
118
+ {
119
+ :joins => "#{reflection.options[:joins]}",
120
+ :remote_key => "#{remote_table_alias}.#{reflection.primary_key_name}",
121
+ :local_key => reflection.klass.primary_key,
122
+ :conditions => conditions
123
+ }
124
+ end
125
+ end
126
+ end
127
+
128
+ def belongs_to_quoted_key
129
+ attribute, col = @reflection.through_reflection.primary_key_name, @owner.column_for_attribute(attribute)
130
+ @owner.send(:quote_value, @owner.send(attribute), col)
131
+ end
132
+ end
@@ -0,0 +1,14 @@
1
+ module NestedHasManyThrough
2
+ module Reflection # :nodoc:
3
+ def self.included(base)
4
+ base.send :alias_method_chain, :check_validity!, :nested_has_many_through
5
+ end
6
+
7
+ def check_validity_with_nested_has_many_through!
8
+ check_validity_without_nested_has_many_through!
9
+ rescue ActiveRecord::HasManyThroughSourceAssociationMacroError => e
10
+ # now we permit has many through to a :though source
11
+ raise e unless source_reflection.options[:through]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module NestedHasManyThrough
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,15 @@
1
+ require "nested_has_many_through/association"
2
+ require "nested_has_many_through/reflection"
3
+
4
+ module NestedHasManyThrough
5
+ autoload :Association, 'nested_has_many_through/association'
6
+ autoload :Reflection, 'nested_has_many_through/reflection'
7
+ end
8
+
9
+ ActiveRecord::Associations::HasManyThroughAssociation.send :include, NestedHasManyThrough::Association
10
+
11
+ if defined?(ActiveRecord::Reflection::ThroughReflection)
12
+ ActiveRecord::Reflection::ThroughReflection.send :include, NestedHasManyThrough::Reflection
13
+ else
14
+ ActiveRecord::Reflection::AssociationReflection.send :include, NestedHasManyThrough::Reflection
15
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "nested_has_many_through/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nested_has_many_through"
7
+ s.version = NestedHasManyThrough::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ian White"]
10
+ s.email = ["ian.w.white@gmail.com"]
11
+ s.homepage = "http://twitter.com/i2w"
12
+ s.summary = %q{Rails gem that allows has_many :through to go through other has_many :throughs}
13
+ s.description = %q{This plugin makes it possible to define has_many :through relationships that
14
+ go through other has_many :through relationships, possibly through an arbitrarily deep hierarchy.
15
+ This allows associations across any number of tables to be constructed, without having to resort to
16
+ find_by_sql (which isn't a suitable solution if you need to do eager loading through :include as well).}
17
+
18
+ s.rubyforge_project = "nested_has_many_through"
19
+
20
+ s.add_dependency "rails", ">= 3.0.0"
21
+
22
+ s.add_development_dependency "bundler", ">= 1.0.0"
23
+ s.add_development_dependency "rspec", "~> 2.5.0"
24
+ s.add_development_dependency "yard"
25
+ s.add_development_dependency "sqlite3-ruby"
26
+
27
+ s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
30
+ s.require_paths = ["lib"]
31
+ end
32
+
@@ -0,0 +1,84 @@
1
+ require "spec_helper"
2
+
3
+ describe Author do
4
+ describe "(newly created)" do
5
+ before do
6
+ @category = Category.create!
7
+ @other_category = Category.create!
8
+ @author = Author.create!
9
+ end
10
+
11
+ it "#posts should == []" do
12
+ @author.posts.should == []
13
+ end
14
+
15
+ it "#categories should == []" do
16
+ @author.categories.should == []
17
+ end
18
+
19
+ it "#similar_posts should == []" do
20
+ @author.similar_posts.should be_empty
21
+ end
22
+
23
+ it "#similar_authors should == []" do
24
+ @author.similar_authors.should be_empty
25
+ end
26
+
27
+ it "#commenters should == []" do
28
+ @author.commenters.should == []
29
+ end
30
+
31
+ describe "who creates post with category" do
32
+ before do
33
+ @post = Post.create! :author => @author, :category => @category
34
+ end
35
+
36
+ it "#posts should == [post]" do
37
+ @author.posts.should == [@post]
38
+ end
39
+
40
+ it "#categories should == [category]" do
41
+ @author.categories.should == [@category]
42
+ end
43
+
44
+ describe "and @other_author creates post2 in category" do
45
+
46
+ before do
47
+ @other_author = Author.create!
48
+ @post2 = Post.create! :author => @other_author, :category => @category
49
+ end
50
+
51
+ it "#posts should == [post2]" do
52
+ @author.posts.should == [@post]
53
+ end
54
+
55
+ it "#categories should == [category]" do
56
+ @author.categories.should == [@category]
57
+ end
58
+
59
+ it "#similar_posts.should == [post, post2]" do
60
+ @author.similar_posts.should == [@post, @post2]
61
+ end
62
+
63
+ it "#similar_authors.should == [@author, @other_author]" do
64
+ @author.similar_authors.should == [@author, @other_author]
65
+ end
66
+
67
+ describe "and creates @other_post in @other_category" do
68
+ before do
69
+ @other_category = Category.create!
70
+ @other_post = Post.create! :author => @other_author, :category => @other_category
71
+ end
72
+
73
+ it "#similar_posts.should == [@post, @post2]" do
74
+ @author.similar_posts.should == [@post, @post2]
75
+ end
76
+
77
+ it "#posts_by_similar_authors.should == [@post, @post2, @other_post]" do
78
+ @author.posts_of_similar_authors.should == [@post, @post2, @other_post]
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,108 @@
1
+ require "spec_helper"
2
+
3
+ describe 'Commenter use case (a1: p1>c1, a2: p2>c1, p3>c2, a3: p4>c3)' do
4
+ before do
5
+ @c1 = Category.create!
6
+ @c2 = Category.create!
7
+ @c3 = Category.create!
8
+ @a1 = Author.create!
9
+ @a2 = Author.create!
10
+ @a3 = Author.create!
11
+ @p1 = @a1.posts.create! :category => @c1
12
+ @p2 = @a2.posts.create! :category => @c1
13
+ @p3 = @a2.posts.create! :category => @c2
14
+ @p4 = @a3.posts.create! :category => @c3
15
+ @a1.reload
16
+ @a2.reload
17
+ end
18
+
19
+ it "a1.posts should == [p1]" do
20
+ @a1.posts.should == [@p1]
21
+ end
22
+
23
+ it "a1.categories should == [c1]" do
24
+ @a1.categories.should == [@c1]
25
+ end
26
+
27
+ it "a2.posts should == [p2, p3]" do
28
+ @a2.posts.should == [@p2, @p3]
29
+ end
30
+
31
+ it "a2.categories should == [c1, c2]" do
32
+ @a2.categories.should == [@c1, @c2]
33
+ end
34
+
35
+ describe "u1 comments on p2" do
36
+ before do
37
+ @u1 = User.create!
38
+ @comment = @p2.comments.create! :user => @u1
39
+ end
40
+
41
+ it "u1.comments should == [comment]" do
42
+ @u1.comments.should == [@comment]
43
+ end
44
+
45
+ it "a1.commenters should be empty" do
46
+ @a1.commenters.should be_empty
47
+ end
48
+
49
+ it "a2.commenters should == [u1]" do
50
+ @a2.commenters.should == [@u1]
51
+ end
52
+
53
+ it "u1.commented_posts should == [p2]" do
54
+ @u1.commented_posts.should == [@p2]
55
+ end
56
+
57
+ it "u1.commented_posts.find_inflamatory(:all) should be empty" do
58
+ @u1.commented_posts.find_inflamatory(:all).should be_empty
59
+ end
60
+
61
+ if ActiveRecord::Base.respond_to?(:named_scope)
62
+ it "u1.commented_posts.inflamatory should be empty" do
63
+ @u1.commented_posts.inflamatory.should be_empty
64
+ end
65
+ end
66
+
67
+ it "u1.commented_authors should == [a2]" do
68
+ @u1.commented_authors.should == [@a2]
69
+ end
70
+
71
+ it "u1.posts_of_interest should == [p1, p2, p3]" do
72
+ @u1.posts_of_interest.should == [@p1, @p2, @p3]
73
+ end
74
+
75
+ it "u1.categories_of_interest should == [c1, c2]" do
76
+ @u1.categories_of_interest.should == [@c1, @c2]
77
+ end
78
+
79
+ describe "when p2 is inflamatory" do
80
+ before do
81
+ @p2.toggle!(:inflamatory)
82
+ end
83
+
84
+ it "p2 should be inflamatory" do
85
+ @p2.should be_inflamatory
86
+ end
87
+
88
+ it "u1.commented_posts.find_inflamatory(:all) should == [p2]" do
89
+ # uniq ids is here (and next spec) because eager loading changed behaviour 2.0.2 => edge
90
+ @u1.commented_posts.find_inflamatory(:all).collect(&:id).uniq.should == [@p2.id]
91
+ end
92
+
93
+ it "u1.posts_of_interest.find_inflamatory(:all).uniq should == [p2]" do
94
+ @u1.posts_of_interest.find_inflamatory(:all).collect(&:id).uniq.should == [@p2.id]
95
+ end
96
+
97
+ if ActiveRecord::Base.respond_to?(:named_scope)
98
+ it "u1.commented_posts.inflamatory should == [p2]" do
99
+ @u1.commented_posts.inflamatory.should == [@p2]
100
+ end
101
+
102
+ it "u1.posts_of_interest.inflamatory should == [p2]" do
103
+ @u1.posts_of_interest.inflamatory.should == [@p2]
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ db/*.sqlite3
3
+ log/*.log
4
+ tmp/
@@ -0,0 +1,31 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', '3.0.0'
4
+
5
+ # Bundle edge Rails instead:
6
+ # gem 'rails', :git => 'git://github.com/rails/rails.git'
7
+
8
+ gem 'sqlite3'
9
+
10
+ # Use unicorn as the web server
11
+ # gem 'unicorn'
12
+
13
+ # Deploy with Capistrano
14
+ # gem 'capistrano'
15
+
16
+ # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
17
+ # gem 'ruby-debug'
18
+ # gem 'ruby-debug19', :require => 'ruby-debug'
19
+
20
+ # Bundle the extra gems:
21
+ # gem 'bj'
22
+ # gem 'nokogiri'
23
+ # gem 'sqlite3-ruby', :require => 'sqlite3'
24
+ # gem 'aws-s3', :require => 'aws/s3'
25
+
26
+ # Bundle gems for the local environment. Make sure to
27
+ # put test-only gems in this group so their generators
28
+ # and rake tasks are available in development mode:
29
+ # group :development, :test do
30
+ # gem 'webrat'
31
+ # end