jackdempsey-sequel_polymorphic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jack Dempsey
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 ADDED
@@ -0,0 +1,112 @@
1
+ Sequel Polymorphic
2
+ ==================
3
+
4
+ A simple plugin for Sequel::Model's that lets you easily create polymorphic associations.
5
+
6
+ ActiveRecord Style
7
+ ------------------
8
+
9
+ class Asset < ActiveRecord::Base
10
+ belongs_to :attachable, :polymorphic => true
11
+ end
12
+
13
+ class Post < ActiveRecord::Base
14
+ has_many :assets, :as => :attachable
15
+ end
16
+
17
+ class Note < ActiveRecord::Base
18
+ has_many :assets, :as => :attachable
19
+ end
20
+
21
+ @asset.attachable = @post
22
+ @asset.attachable = @note
23
+
24
+ In Sequel you would do the following:
25
+
26
+ class Asset < Sequel::Model
27
+ many_to_one :attachable, :reciprocal=>:assets, \
28
+ :dataset=>(proc do
29
+ klass = attachable_type.constantize
30
+ klass.filter(klass.primary_key=>attachable_id)
31
+ end), \
32
+ :eager_loader=>(proc do |key_hash, assets, associations|
33
+ id_map = {}
34
+ assets.each do |asset|
35
+ asset.associations[:attachable] = nil
36
+ ((id_map[asset.attachable_type] ||= {})[asset.attachable_id] ||= []) << asset
37
+ end
38
+ id_map.each do |klass_name, id_map|
39
+ klass = klass_name.constantize
40
+ klass.filter(klass.primary_key=>id_map.keys).all do |attach|
41
+ id_map[attach.pk].each do |asset|
42
+ asset.associations[:attachable] = attach
43
+ end
44
+ end
45
+ end
46
+ end)
47
+
48
+ private
49
+
50
+ def _attachable=(attachable)
51
+ self[:attachable_id] = (attachable.pk if attachable)
52
+ self[:attachable_type] = (attachable.class.name if attachable)
53
+ end
54
+ end
55
+
56
+ class Post < Sequel::Model
57
+ one_to_many :assets, :key=>:attachable_id do |ds|
58
+ ds.filter(:attachable_type=>'Post')
59
+ end
60
+
61
+ private
62
+
63
+ def _add_asset(asset)
64
+ asset.attachable_id = pk
65
+ asset.attachable_type = 'Post'
66
+ asset.save
67
+ end
68
+ def _remove_asset(asset)
69
+ asset.attachable_id = nil
70
+ asset.attachable_type = nil
71
+ asset.save
72
+ end
73
+ def _remove_all_assets
74
+ Asset.filter(:attachable_id=>pk, :attachable_type=>'Post')\
75
+ .update(:attachable_id=>nil, :attachable_type=>nil)
76
+ end
77
+ end
78
+
79
+ class Note < Sequel::Model
80
+ one_to_many :assets, :key=>:attachable_id do |ds|
81
+ ds.filter(:attachable_type=>'Note')
82
+ end
83
+
84
+ private
85
+
86
+ def _add_asset(asset)
87
+ asset.attachable_id = pk
88
+ asset.attachable_type = 'Note'
89
+ asset.save
90
+ end
91
+ def _remove_asset(asset)
92
+ asset.attachable_id = nil
93
+ asset.attachable_type = nil
94
+ asset.save
95
+ end
96
+ def _remove_all_assets
97
+ Asset.filter(:attachable_id=>pk, :attachable_type=>'Note')\
98
+ .update(:attachable_id=>nil, :attachable_type=>nil)
99
+ end
100
+ end
101
+
102
+ @asset.attachable = @post
103
+ @asset.attachable = @note
104
+
105
+
106
+ Thats quite a bit of code. With sequel_polymorphic you can now do:
107
+
108
+ class Note < Sequel::Model
109
+ is :polymorphic, :as => :attachable
110
+ end
111
+
112
+ voila!
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+
7
+ GEM = "sequel_polymorphic"
8
+ GEM_VERSION = "0.0.1"
9
+ AUTHOR = "Jack Dempsey"
10
+ EMAIL = "jack.dempsey@gmail.com"
11
+ HOMEPAGE = "http://jackndempsey.blogspot.com"
12
+ SUMMARY = "A gem that provides Sequel::Models with polymorphic association capabilities"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = GEM
16
+ s.version = GEM_VERSION
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = false
19
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
20
+ s.summary = SUMMARY
21
+ s.description = s.summary
22
+ s.author = AUTHOR
23
+ s.email = EMAIL
24
+ s.homepage = HOMEPAGE
25
+
26
+ # Uncomment this to add a dependency
27
+ # s.add_dependency "foo"
28
+
29
+ s.require_path = 'lib'
30
+ s.autorequire = GEM
31
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
32
+ end
33
+
34
+ task :default => :spec
35
+
36
+ desc "Run specs"
37
+ Spec::Rake::SpecTask.new do |t|
38
+ t.spec_files = FileList['spec/**/*_spec.rb']
39
+ t.spec_opts = %w(-fs --color)
40
+ end
41
+
42
+
43
+ Rake::GemPackageTask.new(spec) do |pkg|
44
+ pkg.gem_spec = spec
45
+ end
46
+
47
+ desc "install the gem locally"
48
+ task :install => [:package] do
49
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
50
+ end
51
+
52
+ desc "create a gemspec file"
53
+ task :make_spec do
54
+ File.open("#{GEM}.gemspec", "w") do |file|
55
+ file.puts spec.to_ruby
56
+ end
57
+ end
data/TODO ADDED
@@ -0,0 +1 @@
1
+ Add support for many_to_many polymorphic
@@ -0,0 +1,4 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'sequel_polymorphic/sequel_polymorphic'
@@ -0,0 +1,123 @@
1
+ module Sequel
2
+ module Plugins
3
+ module Polymorphic
4
+ # Apply the plugin to the model.
5
+ def self.apply(model, options = {})
6
+ end
7
+
8
+ module InstanceMethods
9
+
10
+ end
11
+
12
+ module ClassMethods
13
+ def many_to_one(*args, &block)
14
+ able, options = *args
15
+ options ||= {}
16
+ if options[:polymorphic]
17
+ model = self.class.to_s.downcase
18
+ plural_model = model.pluralize
19
+ singular_model = model.singularize
20
+ self.class_eval %{
21
+ associate(:many_to_one, :#{able}, :reciprocal=>:#{plural_model},
22
+ :dataset=>(proc { klass = #{able}_type.constantize; klass.filter(klass.primary_key=>#{able}_id) }),
23
+ :eager_loader=>(proc do |key_hash, #{plural_model}, associations|
24
+ id_map = {}
25
+ #{plural_model}.each do |#{singular_model}|
26
+ #{singular_model}.associations[:#{able}] = nil;
27
+ ((id_map[#{singular_model}.#{able}_type] ||= {})[#{singular_model}.#{able}_id] ||= []) << #{singular_model}
28
+ end
29
+ id_map.each do |klass_name, id_map|
30
+ klass = klass_name.constantize
31
+ klass.filter(klass.primary_key=>id_map.keys).all do |related_obj|
32
+ id_map[related_obj.pk].each { |#{singular_model}| #{singular_model}.associations[:#{able}] = related_obj }
33
+ end
34
+ end
35
+ end)
36
+ )
37
+
38
+ private
39
+
40
+ def _#{able}=(#{able})
41
+ self[:#{able}_id] = (#{able}.pk if #{able})
42
+ self[:#{able}_type] = (#{able}.class.name if #{able})
43
+ end
44
+ }
45
+ else
46
+ associate(:many_to_one, *args, &block)
47
+ end
48
+ end
49
+
50
+ alias :belongs_to :many_to_one
51
+
52
+ def one_to_many(*args, &block)
53
+ many_of_class, options = *args
54
+ options ||= {}
55
+ many_class = many_of_class.to_s.singularize
56
+ if able = options[:as]
57
+ associate(:one_to_many, many_of_class, :key=>"#{able}_id".to_sym) do |ds|
58
+ ds.filter("#{able}_type".to_sym => self.class.to_s)
59
+ end
60
+
61
+ method_definitions = %{
62
+ private
63
+
64
+ def _add_#{many_class}(#{many_class})
65
+ #{many_class}.#{able}_id = pk
66
+ #{many_class}.#{able}_type = '#{self}'
67
+ #{many_class}.save
68
+ end
69
+ def _remove_#{many_class}(#{many_class})
70
+ #{many_class}.#{able}_id = nil
71
+ #{many_class}.#{able}_type = nil
72
+ #{many_class}.save
73
+ end
74
+ def _remove_all_#{many_of_class}
75
+ #{many_class.capitalize}.filter(:#{able}_id=>pk, :#{able}_type=>'#{self}').update(:#{able}_id=>nil, :#{able}_type=>nil)
76
+ end
77
+ }
78
+ self.class_eval method_definitions
79
+ else
80
+ associate(:one_to_many, *args, &block)
81
+ end
82
+ end
83
+
84
+ alias :has_many :one_to_many
85
+
86
+ #example: many_to_many :tags, :through => :taggings, :as => :taggable
87
+ def many_to_many(*args, &block)
88
+ many_to_class, options = *args # => :tags, :through => :taggings, :as => :taggable
89
+ many_class = many_to_class.to_s.singularize # => tag
90
+ options ||= {}
91
+ if through = (options[:through] or options[:join_table]) and able = options[:as]
92
+ through_klass = through.to_s.singularize.capitalize # => Tagging
93
+ # self in the block passed to associate is an instance of the class, hence the self.class call
94
+ associate(:many_to_many, many_to_class,
95
+ :left_key => "#{able}_id".to_sym,
96
+ :join_table => through) { |ds| ds.filter("#{able}_type".to_sym => self.class.to_s) }
97
+
98
+ method_string = %{
99
+ private
100
+
101
+ def _add_#{many_class}(#{many_class})
102
+ #{through_klass}.create(:#{many_class}_id => #{many_class}.pk, :#{able}_id => pk, :#{able}_type => '#{self}')
103
+ end
104
+
105
+ def _remove_#{many_class}(#{many_class})
106
+ #{through_klass}.filter(:#{many_class}_id => #{many_class}.pk, :#{able}_id => pk, :#{able}_type => '#{self}').delete
107
+ end
108
+
109
+ def _remove_all_#{many_to_class}
110
+ #{through_klass}.filter(:#{able}_id=>pk, :#{able}_type=>'#{self}').delete
111
+ end
112
+ }
113
+ self.class_eval method_string
114
+ else
115
+ associate(:many_to_many, *args, &block)
116
+ end
117
+ end
118
+ end # ClassMethods
119
+ end # Polymorphic
120
+ end # Plugins
121
+ end # Sequel
122
+
123
+
@@ -0,0 +1,36 @@
1
+ DB = Sequel.sqlite
2
+
3
+ class Asset < Sequel::Model
4
+ set_schema do
5
+ primary_key :id
6
+ varchar :name
7
+ integer :attachable_id
8
+ varchar :attachable_type
9
+ end
10
+
11
+ is :polymorphic, :belongs_to => :attachable
12
+
13
+ end
14
+
15
+ class Post < Sequel::Model
16
+ set_schema do
17
+ primary_key :id
18
+ varchar :name
19
+ end
20
+
21
+ is :polymorphic, :has_many => :assets, :as => :attachable
22
+
23
+ end
24
+
25
+
26
+ class Note < Sequel::Model
27
+ set_schema do
28
+ primary_key :id
29
+ varchar :name
30
+ end
31
+
32
+ is :polymorphic, :has_many => :assets, :as => :attachable
33
+
34
+ end
35
+
36
+ [Asset, Post, Note].each {|klass| klass.create_table!}
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ # Models we have: Post(name), Note(name), Asset(name)
4
+ describe Sequel::Plugins::Polymorphic do
5
+ it "should add :as to has_many/one_to_many" do
6
+
7
+ end
8
+
9
+ it "should add :polymorphic => true to belongs_to" do
10
+
11
+ end
12
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,12 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'rubygems'
5
+ require 'spec'
6
+ require 'sequel'
7
+
8
+ require File.dirname(__FILE__) + '/sequel-setup'
9
+ require File.dirname(__FILE__) + '/../lib/sequel_polymorphic'
10
+
11
+
12
+
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jackdempsey-sequel_polymorphic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jack Dempsey
8
+ autorequire: sequel_polymorphic
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-08 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A gem that provides Sequel::Models with polymorphic association capabilities
17
+ email: jack.dempsey@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ - TODO
26
+ files:
27
+ - LICENSE
28
+ - README
29
+ - Rakefile
30
+ - TODO
31
+ - lib/sequel_polymorphic
32
+ - lib/sequel_polymorphic/sequel_polymorphic.rb
33
+ - lib/sequel_polymorphic.rb
34
+ - spec/sequel-setup.rb
35
+ - spec/sequel_polymorphic
36
+ - spec/sequel_polymorphic/sequel_polymorphic_spec.rb
37
+ - spec/spec.opts
38
+ - spec/spec_helper.rb
39
+ has_rdoc: false
40
+ homepage: http://jackndempsey.blogspot.com
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: A gem that provides Sequel::Models with polymorphic association capabilities
65
+ test_files: []
66
+