forked from jeremyevans/sequel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add column_conflicts plugin to automatically handle columns that conf…
…lict with method names This makes it easy to have Sequel automatically handle column names that conflict with method names. Just load the plugin into the model, and it will override get_column_value and set_column_value appropariately to set the conflict values directly in the values hash instead of calling the method.
- Loading branch information
1 parent
aac7220
commit b3626bf
Showing
4 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
module Sequel | ||
module Plugins | ||
# The column_conflicts plugin overrides Model#get_column_value and #set_column_value | ||
# to automatically handle column names that conflict with Ruby/Sequel method names. | ||
# | ||
# By default, Model#get_column_value and #set_column_value just call send, this | ||
# plugin overrides the methods and gets/sets the value directly in the values | ||
# hash if the column name conflicts with an existing Sequel::Model instance | ||
# method name. | ||
# | ||
# Checking for column conflicts causes a performance hit, which is why Sequel | ||
# does not enable such checks by default. | ||
# | ||
# When using this plugin, you can manually update the columns used. This may be useful if | ||
# the columns conflict with one of your custom methods, instead of a method defined in | ||
# Sequel::Model: | ||
# | ||
# Album.plugin :column_conflicts | ||
# Album.get_column_conflict!(:column) | ||
# Album.set_column_conflict!(:other_column) | ||
# | ||
# Usage: | ||
# | ||
# # Make all model's handle column conflicts automatically (called before loading subclasses) | ||
# Sequel::Model.plugin :column_conflicts | ||
# | ||
# # Make the Album class handle column conflicts automatically | ||
# Album.plugin :column_conflicts | ||
module ColumnConflicts | ||
# Check for column conflicts on the current model if the model has a dataset. | ||
def self.configure(model) | ||
model.instance_eval do | ||
@get_column_conflicts = {} | ||
@set_column_conflicts = {} | ||
check_column_conflicts if @dataset | ||
end | ||
end | ||
|
||
module ClassMethods | ||
Plugins.after_set_dataset(self, :check_column_conflicts) | ||
Plugins.inherited_instance_variables(self, :@get_column_conflicts=>:dup, :@set_column_conflicts=>:dup) | ||
|
||
# Hash for columns where the getter method already exists. keys are column symbols/strings that | ||
# conflict with method names and should be looked up directly instead of calling a method, | ||
# values are the column symbol to lookup in the values hash. | ||
attr_reader :get_column_conflicts | ||
|
||
# Hash for columns where the setter method already exists. keys are column symbols/strings suffixed | ||
# with = that conflict with method names and should be set directly in the values hash, | ||
# values are the column symbol to set in the values hash. | ||
attr_reader :set_column_conflicts | ||
|
||
# Compare the column names for the model with the methods defined on Sequel::Model, and automatically | ||
# setup the column conflicts. | ||
def check_column_conflicts | ||
mod = Sequel::Model | ||
columns.find_all{|c| mod.method_defined?(c)}.each{|c| get_column_conflict!(c)} | ||
columns.find_all{|c| mod.method_defined?("#{c}=")}.each{|c| set_column_conflict!(c)} | ||
end | ||
|
||
# Set the given column as one with a getter method conflict. | ||
def get_column_conflict!(column) | ||
@get_column_conflicts[column.to_sym] = @get_column_conflicts[column.to_s] = column.to_sym | ||
end | ||
|
||
# Set the given column as one with a setter method conflict. | ||
def set_column_conflict!(column) | ||
@set_column_conflicts[:"#{column}="] = @set_column_conflicts["#{column}="] = column.to_sym | ||
end | ||
end | ||
|
||
module InstanceMethods | ||
# If the given column has a getter method conflict, lookup the value directly in the values hash. | ||
def get_column_value(c) | ||
if col = model.get_column_conflicts[c] | ||
self[col] | ||
else | ||
super | ||
end | ||
end | ||
|
||
# If the given column has a setter method conflict, set the value directly in the values hash. | ||
def set_column_value(c, v) | ||
if col = model.set_column_conflicts[c] | ||
self[col] = v | ||
else | ||
super | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper") | ||
|
||
describe "column_conflicts plugin" do | ||
before do | ||
@db = Sequel.mock | ||
@c = Class.new(Sequel::Model(@db[:test])) | ||
@c.columns :model, :use_transactions, :foo | ||
@c.plugin :column_conflicts | ||
@o = @c.load(:model=>1, :use_transactions=>2, :foo=>4) | ||
end | ||
|
||
it "should have mass assignment work correctly" do | ||
@o.set_fields({:use_transactions=>3}, [:use_transactions]) | ||
@o.get_column_value(:use_transactions).should == 3 | ||
end | ||
|
||
it "should handle both symbols and strings" do | ||
@o.get_column_value(:model).should == 1 | ||
@o.get_column_value("model").should == 1 | ||
@o.set_column_value(:use_transactions=, 3) | ||
@o.get_column_value(:use_transactions).should == 3 | ||
@o.set_column_value(:use_transactions=, 4) | ||
@o.get_column_value(:use_transactions).should == 4 | ||
end | ||
|
||
it "should allow manual setting of conflicted columns" do | ||
@c.send(:define_method, :foo){raise} | ||
@c.get_column_conflict!(:foo) | ||
@o.get_column_value(:foo).should == 4 | ||
|
||
@c.send(:define_method, :model=){raise} | ||
@c.set_column_conflict!(:model) | ||
@o.set_column_value(:model=, 2).should == 2 | ||
@o.get_column_value(:model).should == 2 | ||
end | ||
|
||
it "should work correctly in subclasses" do | ||
@o = Class.new(@c).load(:model=>1, :use_transactions=>2) | ||
@o.get_column_value(:model).should == 1 | ||
@o.get_column_value("model").should == 1 | ||
@o.set_column_value(:use_transactions=, 3) | ||
@o.get_column_value(:use_transactions).should == 3 | ||
@o.set_column_value(:use_transactions=, 4) | ||
@o.get_column_value(:use_transactions).should == 4 | ||
end | ||
|
||
it "should work correctly for dataset changes" do | ||
ds = @db[:test] | ||
def ds.columns; [:object_id] end | ||
@c.dataset = ds | ||
o = @c.load(:object_id=>3) | ||
o.get_column_value(:object_id).should == 3 | ||
o.object_id.should_not == 3 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters