nested_has_many_through 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.
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