pluginaweek-polymorphic_identity 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ == master
2
+
3
+ == 0.1.0 / 2008-12-14
4
+
5
+ * Remove the PluginAWeek namespace
6
+
7
+ == 0.0.5 / 2008-06-22
8
+
9
+ * Remove log files from gems
10
+
11
+ == 0.0.4 / 2008-05-05
12
+
13
+ * Updated documentation
14
+
15
+ == 0.0.3 / 2007-09-26
16
+
17
+ * Move test fixtures out of the test application root directory
18
+ * Remove gem dependency on activerecord
19
+
20
+ == 0.0.2 / 2007-08-05
21
+
22
+ * Add api documentation
23
+ * Fix Rakefile syntax errors
24
+ * Refactor tests
25
+ * Use the plugin_test_helper plugin for helping create a Rails environment
26
+
27
+ == 0.0.1 / 2007-02-12
28
+
29
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2009 Aaron Pfeifer
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.
@@ -0,0 +1,76 @@
1
+ = polymorphic_identity
2
+
3
+ +polymorphic_identity+ dynamically generates aliases for polymorphic associations
4
+ based on the class names of those associations.
5
+
6
+ == Resources
7
+
8
+ API
9
+
10
+ * http://api.pluginaweek.org/polymorphic_identity
11
+
12
+ Bugs
13
+
14
+ * http://pluginaweek.lighthouseapp.com/projects/13285-polymorphic_identity
15
+
16
+ Development
17
+
18
+ * http://github.com/pluginaweek/polymorphic_identity
19
+
20
+ Source
21
+
22
+ * git://github.com/pluginaweek/polymorphic_identity.git
23
+
24
+ == Description
25
+
26
+ Polymorphic associations are not very descriptive when it comes to easily
27
+ knowing the type of model your interacting with. For example, a typical
28
+ polymorphic assocation looks like the following:
29
+
30
+ class Tag < ActiveRecord::Base
31
+ belongs_to :taggable, :polymorphic => true
32
+ end
33
+
34
+ When getting the taggable record, you would normally have to call
35
+ <tt>tag.taggable</tt>. However, if you know that the taggable record is just an
36
+ instance of the Article model, then it would feel more comfortable if you could
37
+ just call <tt>tag.article</tt>. polymoprhic_identity makes this possible by
38
+ dynamically checking the name of the polymorphic record's class and creating
39
+ methods that allow you to access the polymorphic association based on that
40
+ class name.
41
+
42
+ == Usage
43
+
44
+ === Example
45
+
46
+ class Comment < ActiveRecord::Base
47
+ belongs_to :commentable, :polymorphic => true
48
+ belongs_to :commenter, :polymorphic => true
49
+ end
50
+
51
+ class Article < ActiveRecord::Base
52
+ has_many :comments, :as => :commentable
53
+ end
54
+
55
+ class User < ActiveRecord::Base
56
+ has_many :comments, :as => :commenter
57
+ end
58
+
59
+ c = Comment.find(1) # => #<Tag id: 1, commentable_id: 1, commentable_type: "Article", commenter_id: 1, commenter_type: "User"}>
60
+ c.commentable # => #<Article id: 1>
61
+ c.article # => #<Article id: 1>
62
+ c.commenter # => #<User id: 1>
63
+ c.user # => #<User id: 1>
64
+
65
+ == Testing
66
+
67
+ Before you can run any tests, the following gem must be installed:
68
+ * plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
69
+
70
+ To run against a specific version of Rails:
71
+
72
+ rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
73
+
74
+ == Dependencies
75
+
76
+ * Rails 2.0 or later
@@ -0,0 +1,96 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = 'polymorphic_identity'
8
+ s.version = '0.1.0'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'Dynamically generates aliases for polymorphic ActiveRecord associations based on their class names'
11
+ s.description = s.summary
12
+
13
+ s.files = FileList['{lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
14
+ s.require_path = 'lib'
15
+ s.has_rdoc = true
16
+ s.test_files = Dir['test/**/*_test.rb']
17
+
18
+ s.author = 'Aaron Pfeifer'
19
+ s.email = 'aaron@pluginaweek.org'
20
+ s.homepage = 'http://www.pluginaweek.org'
21
+ s.rubyforge_project = 'pluginaweek'
22
+ end
23
+
24
+ desc 'Default: run all tests.'
25
+ task :default => :test
26
+
27
+ desc "Test the #{spec.name} plugin."
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << 'lib'
30
+ t.test_files = spec.test_files
31
+ t.verbose = true
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ namespace :test do
37
+ desc "Test the #{spec.name} plugin with Rcov."
38
+ Rcov::RcovTask.new(:rcov) do |t|
39
+ t.libs << 'lib'
40
+ t.test_files = spec.test_files
41
+ t.rcov_opts << '--exclude="^(?!lib/)"'
42
+ t.verbose = true
43
+ end
44
+ end
45
+ rescue LoadError
46
+ end
47
+
48
+ desc "Generate documentation for the #{spec.name} plugin."
49
+ Rake::RDocTask.new(:rdoc) do |rdoc|
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = spec.name
52
+ rdoc.template = '../rdoc_template.rb'
53
+ rdoc.options << '--line-numbers' << '--inline-source'
54
+ rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb')
55
+ end
56
+
57
+ desc 'Generate a gemspec file.'
58
+ task :gemspec do
59
+ File.open("#{spec.name}.gemspec", 'w') do |f|
60
+ f.write spec.to_ruby
61
+ end
62
+ end
63
+
64
+ Rake::GemPackageTask.new(spec) do |p|
65
+ p.gem_spec = spec
66
+ p.need_tar = true
67
+ p.need_zip = true
68
+ end
69
+
70
+ desc 'Publish the beta gem.'
71
+ task :pgem => [:package] do
72
+ Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
73
+ end
74
+
75
+ desc 'Publish the API documentation.'
76
+ task :pdoc => [:rdoc] do
77
+ Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
78
+ end
79
+
80
+ desc 'Publish the API docs and gem'
81
+ task :publish => [:pgem, :pdoc, :release]
82
+
83
+ desc 'Publish the release files to RubyForge.'
84
+ task :release => [:gem, :package] do
85
+ require 'rubyforge'
86
+
87
+ ruby_forge = RubyForge.new.configure
88
+ ruby_forge.login
89
+
90
+ %w(gem tgz zip).each do |ext|
91
+ file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
92
+ puts "Releasing #{File.basename(file)}..."
93
+
94
+ ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
95
+ end
96
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'polymorphic_identity'
@@ -0,0 +1,52 @@
1
+ # Adds dynamic attributes for polymorphic associations based on the name of
2
+ # the class for the polymorphic record. For example,
3
+ #
4
+ # class Tag < ActiveRecord::Base
5
+ # belongs_to :taggable, :polymorphic => true
6
+ # end
7
+ #
8
+ # class Article < ActiveRecord::Base
9
+ # has_many :tags, :as => :taggable
10
+ # end
11
+ #
12
+ # t = Tag.find(1) # => #<Tag id: 1, taggable_id: 1, taggable_type: "Article">
13
+ # t.taggable # => #<Article id: 1>
14
+ # t.article # => #<Article id: 1>
15
+ module PolymorphicIdentity
16
+ def self.included(base) #:nodoc:
17
+ base.class_eval do
18
+ alias_method_chain :method_missing, :polymorphic_identity
19
+ alias_method_chain :respond_to?, :polymorphic_identity
20
+ end
21
+ end
22
+
23
+ def method_missing_with_polymorphic_identity(method_id, *args, &block) #:nodoc:
24
+ if association_name = find_polymorphic_association_name(method_id)
25
+ send(association_name, *args, &block)
26
+ else
27
+ method_missing_without_polymorphic_identity(method_id, *args, &block)
28
+ end
29
+ end
30
+
31
+ # True if a polymorphic association can be found whose foreign type is set
32
+ # to the name of the method
33
+ def respond_to_with_polymorphic_identity?(method, include_priv = false) #:nodoc:
34
+ respond_to_without_polymorphic_identity?(method, include_priv) || !find_polymorphic_association_name(method).nil?
35
+ end
36
+
37
+ private
38
+ # Finds the name of the polymorphic association whose foreign type is set to
39
+ # the value specified.
40
+ def find_polymorphic_association_name(foreign_type_value)
41
+ foreign_type_value = foreign_type_value.to_s.camelize
42
+ reflection = self.class.reflections.values.find do |reflection|
43
+ reflection.options[:polymorphic] && self[reflection.options[:foreign_type]] == foreign_type_value
44
+ end
45
+
46
+ reflection ? reflection.name : nil
47
+ end
48
+ end
49
+
50
+ ActiveRecord::Base.class_eval do
51
+ include PolymorphicIdentity
52
+ end
@@ -0,0 +1,4 @@
1
+ class Article < ActiveRecord::Base
2
+ belongs_to :author
3
+ has_many :comments, :as => :commentable
4
+ end
@@ -0,0 +1,5 @@
1
+ class Author < ActiveRecord::Base
2
+ has_many :articles
3
+ has_many :pages
4
+ has_many :comments, :as => :commenter
5
+ end
@@ -0,0 +1,4 @@
1
+ class Comment < ActiveRecord::Base
2
+ belongs_to :commentable, :polymorphic => true
3
+ belongs_to :commenter, :polymorphic => true
4
+ end
@@ -0,0 +1,4 @@
1
+ class Page < ActiveRecord::Base
2
+ belongs_to :author
3
+ has_many :comments, :as => :commentable
4
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_many :comments, :as => :commenter
3
+ end
@@ -0,0 +1,11 @@
1
+ class CreateAuthors < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :authors do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :authors
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class CreateArticles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :articles do |t|
4
+ t.integer :author_id
5
+ t.text :content
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :articles
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class CreatePages < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :pages do |t|
4
+ t.integer :author_id
5
+ t.text :content
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :pages
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :users
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreateComments < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :comments do |t|
4
+ t.integer :commentable_id
5
+ t.string :commentable_type
6
+ t.string :commenter_id
7
+ t.string :commenter_type
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :comments
13
+ end
14
+ end
@@ -0,0 +1,66 @@
1
+ module Factory
2
+ # Build actions for the model
3
+ def self.build(model, &block)
4
+ name = model.to_s.underscore
5
+
6
+ define_method("#{name}_attributes", block)
7
+ define_method("valid_#{name}_attributes") {|*args| valid_attributes_for(model, *args)}
8
+ define_method("new_#{name}") {|*args| new_record(model, *args)}
9
+ define_method("create_#{name}") {|*args| create_record(model, *args)}
10
+ end
11
+
12
+ # Get valid attributes for the model
13
+ def valid_attributes_for(model, attributes = {})
14
+ name = model.to_s.underscore
15
+ send("#{name}_attributes", attributes)
16
+ attributes.stringify_keys!
17
+ attributes
18
+ end
19
+
20
+ # Build an unsaved record
21
+ def new_record(model, *args)
22
+ attributes = valid_attributes_for(model, *args)
23
+ record = model.new(attributes)
24
+ attributes.each {|attr, value| record.send("#{attr}=", value) if model.accessible_attributes && !model.accessible_attributes.include?(attr) || model.protected_attributes && model.protected_attributes.include?(attr)}
25
+ record
26
+ end
27
+
28
+ # Build and save/reload a record
29
+ def create_record(model, *args)
30
+ record = new_record(model, *args)
31
+ record.save!
32
+ record.reload
33
+ record
34
+ end
35
+
36
+ build Article do |attributes|
37
+ attributes[:author] = create_author unless attributes.include?(:author)
38
+ attributes.reverse_merge!(
39
+ :content => 'Thanks for visiting my blog!'
40
+ )
41
+ end
42
+
43
+ build Author do |attributes|
44
+ attributes.reverse_merge!(
45
+ :name => 'John Smith'
46
+ )
47
+ end
48
+
49
+ build Comment do |attributes|
50
+ attributes[:commentable] = create_article unless attributes.include?(:commentable)
51
+ attributes[:commenter] = create_user unless attributes.include?(:commenter)
52
+ end
53
+
54
+ build Page do |attributes|
55
+ attributes[:author] = create_author unless attributes.include?(:author)
56
+ attributes.reverse_merge!(
57
+ :content => 'Some information about me...'
58
+ )
59
+ end
60
+
61
+ build User do |attributes|
62
+ attributes.reverse_merge!(
63
+ :name => 'Mr. Bean'
64
+ )
65
+ end
66
+ end
@@ -0,0 +1,13 @@
1
+ # Load the plugin testing framework
2
+ $:.unshift("#{File.dirname(__FILE__)}/../../plugin_test_helper/lib")
3
+ require 'rubygems'
4
+ require 'plugin_test_helper'
5
+
6
+ # Run the migrations
7
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
8
+
9
+ # Mixin the factory helper
10
+ require File.expand_path("#{File.dirname(__FILE__)}/factory")
11
+ Test::Unit::TestCase.class_eval do
12
+ include Factory
13
+ end
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class PolymorphicIdentityTest < ActiveSupport::TestCase
4
+ def setup
5
+ @comment = Comment.new
6
+ @comment.commenter_type = 'Article'
7
+ end
8
+
9
+ def test_should_find_polymorphic_association_name_for_valid_symbolized_association
10
+ assert_equal :commenter, @comment.send(:find_polymorphic_association_name, :article)
11
+ end
12
+
13
+ def test_should_find_polymorphic_association_name_for_valid_stringified_association
14
+ assert_equal :commenter, @comment.send(:find_polymorphic_association_name, 'article')
15
+ end
16
+
17
+ def test_should_not_find_polymorphic_association_name_for_invalid_association
18
+ assert_equal nil, @comment.send(:find_polymorphic_association_name, 'page')
19
+ end
20
+ end
21
+
22
+ class PolymorphicAssociationWithNoValueTest < ActiveSupport::TestCase
23
+ def setup
24
+ @comment = create_comment(:commentable => nil, :commenter => nil)
25
+ end
26
+
27
+ def test_should_not_respond_to_polymorphic_association_name
28
+ assert !@comment.respond_to?(:article)
29
+ assert !@comment.respond_to?(:page)
30
+ assert !@comment.respond_to?(:author)
31
+ assert !@comment.respond_to?(:user)
32
+ end
33
+ end
34
+
35
+ class PolymorphicAssociationsWithValueTest < ActiveSupport::TestCase
36
+ def setup
37
+ @author = create_author
38
+ @article = create_article(:author => @author)
39
+ @comment = create_comment(:commentable => @article, :commenter => @author)
40
+ end
41
+
42
+ def test_should_respond_to_polymorphic_association_name_if_association_exists
43
+ assert @comment.respond_to?(:article)
44
+ assert @comment.respond_to?(:author)
45
+ assert_equal @article, @comment.article
46
+ assert_equal @author, @comment.author
47
+ end
48
+
49
+ def test_should_update_response_when_changing_polymorphic_association
50
+ page = create_page
51
+ user = create_user
52
+
53
+ @comment.commentable = page
54
+ @comment.commenter = user
55
+
56
+ assert !@comment.respond_to?(:article)
57
+ assert !@comment.respond_to?(:author)
58
+ assert @comment.respond_to?(:page)
59
+ assert @comment.respond_to?(:user)
60
+ assert_equal page, @comment.page
61
+ assert_equal user, @comment.user
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pluginaweek-polymorphic_identity
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Pfeifer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Dynamically generates aliases for polymorphic ActiveRecord associations based on their class names
17
+ email: aaron@pluginaweek.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/polymorphic_identity.rb
26
+ - test/unit
27
+ - test/unit/polymorphic_identity_test.rb
28
+ - test/factory.rb
29
+ - test/app_root
30
+ - test/app_root/app
31
+ - test/app_root/app/models
32
+ - test/app_root/app/models/comment.rb
33
+ - test/app_root/app/models/page.rb
34
+ - test/app_root/app/models/user.rb
35
+ - test/app_root/app/models/author.rb
36
+ - test/app_root/app/models/article.rb
37
+ - test/app_root/db
38
+ - test/app_root/db/migrate
39
+ - test/app_root/db/migrate/005_create_comments.rb
40
+ - test/app_root/db/migrate/004_create_users.rb
41
+ - test/app_root/db/migrate/001_create_authors.rb
42
+ - test/app_root/db/migrate/002_create_articles.rb
43
+ - test/app_root/db/migrate/003_create_pages.rb
44
+ - test/test_helper.rb
45
+ - CHANGELOG.rdoc
46
+ - init.rb
47
+ - LICENSE
48
+ - Rakefile
49
+ - README.rdoc
50
+ has_rdoc: true
51
+ homepage: http://www.pluginaweek.org
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project: pluginaweek
72
+ rubygems_version: 1.2.0
73
+ signing_key:
74
+ specification_version: 2
75
+ summary: Dynamically generates aliases for polymorphic ActiveRecord associations based on their class names
76
+ test_files:
77
+ - test/unit/polymorphic_identity_test.rb