Permalink
Browse files

Merge pull request #17 from pyromaniac/master

Completele removed AR serialization
  • Loading branch information...
2 parents 66c7bfb + a93000b commit 9208f01ca2b5ee923a370b9cc6779f6973566f0b @devinfoley devinfoley committed Nov 7, 2012
View
1 .rspec
@@ -1,3 +1,4 @@
--colour
--format documentation
--backtrace
+--order random
View
36 .travis.yml
@@ -9,27 +9,27 @@ rvm:
#- rbx-19mode
env:
- - RAILS_VERSION='~>3.1.0'
- - RAILS_VERSION='~>3.1.0' DB=mysql
- - RAILS_VERSION='~>3.1.0' DB=postgresql
- - RAILS_VERSION='~>3.2.0'
- - RAILS_VERSION='~>3.2.0' DB=mysql
- - RAILS_VERSION='~>3.2.0' DB=postgresql
- - RAILS_VERSION=3-1-stable
- - RAILS_VERSION=3-1-stable DB=mysql
- - RAILS_VERSION=3-1-stable DB=postgresql
- - RAILS_VERSION=3-2-stable
- - RAILS_VERSION=3-2-stable DB=mysql
- - RAILS_VERSION=3-2-stable DB=postgresql
- - RAILS_VERSION=master
- - RAILS_VERSION=master DB=mysql
- - RAILS_VERSION=master DB=postgresql
+ - ACTIVERECORD_VERSION='~>3.1.0'
+ - ACTIVERECORD_VERSION='~>3.1.0' DB=mysql
+ - ACTIVERECORD_VERSION='~>3.1.0' DB=postgresql
+ - ACTIVERECORD_VERSION='~>3.2.0'
+ - ACTIVERECORD_VERSION='~>3.2.0' DB=mysql
+ - ACTIVERECORD_VERSION='~>3.2.0' DB=postgresql
+ - ACTIVERECORD_VERSION=3-1-stable
+ - ACTIVERECORD_VERSION=3-1-stable DB=mysql
+ - ACTIVERECORD_VERSION=3-1-stable DB=postgresql
+ - ACTIVERECORD_VERSION=3-2-stable
+ - ACTIVERECORD_VERSION=3-2-stable DB=mysql
+ - ACTIVERECORD_VERSION=3-2-stable DB=postgresql
+ - ACTIVERECORD_VERSION=master
+ - ACTIVERECORD_VERSION=master DB=mysql
+ - ACTIVERECORD_VERSION=master DB=postgresql
matrix:
exclude:
- rvm: 1.9.2
- env: RAILS_VERSION=master
+ env: ACTIVERECORD_VERSION=master
- rvm: 1.9.2
- env: RAILS_VERSION=master DB=mysql
+ env: ACTIVERECORD_VERSION=master DB=mysql
- rvm: 1.9.2
- env: RAILS_VERSION=master DB=postgresql
+ env: ACTIVERECORD_VERSION=master DB=postgresql
View
46 README.mkd
@@ -1,6 +1,8 @@
+[![Build Status](https://secure.travis-ci.org/jashmenn/activeuuid.png)](http://travis-ci.org/jashmenn/activeuuid)
+
# activeuuid
-Add `binary(16)` UUIDs to ActiveRecord.
+Add `binary(16)` UUIDs to ActiveRecord.
## Example
@@ -48,20 +50,15 @@ require 'spec_helper'
describe Email do
context "when using uuid's as keys" do
- before(:each) do
- Email.delete_all
- @guid = "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
- @e = Email.new(:subject => "hello", :body => "world") {|e| e.id = UUIDTools::UUID.parse(@guid) }
- @e.save
- end
+ let(:guid) { "1dd74dd0-d116-11e0-99c7-5ac5d975667e" }
+ let(:email) { Fabricate :email }
it "the id guid should be equal to the uuid" do
- @e.id.to_s.should eql(@guid)
+ email.id.to_s.should eql(guid)
end
it "should be able to find an email by the uuid" do
- f = Email.find(UUIDTools::UUID.parse(@guid))
- f.id.to_s.should eql(@guid)
+ Email.find(guid).id.to_s.should == guid
end
end
@@ -73,9 +70,9 @@ end
From [2]:
> [Here is a] UUID: 1e8ef774-581c-102c-bcfe-f1ab81872213
->
+>
> A UUID like the one above is 36 characters long, including dashes. If you store this VARCHAR(36), you're going to decrease compare performance dramatically. This is your primary key, you don't want it to be slow.
->
+>
> At its bit level, a UUID is 128 bits, which means it will fit into
> 16 bytes, note this is not very human readable, but it will keep
> storage low, and is only 4 times larger than a 32-bit int, or 2
@@ -98,10 +95,9 @@ application the keys are represented by a UUIDTools::UUID object.
* more transparent support for natural and composite keys
* support for MySQLs `INSERT ... ON DUPLICATE KEY UPDATE` syntax
* support a primary column name other than `id`
-* work on other databases (Postgres, etc)
* tests
-## Inspiration
+## Inspiration
James Golick's `friendly` is a great gem for NoSQL on MySQL. It's
a great gateway drug to systems like Cassandra for teams that are
already familiar with the ins-and-outs of MySQL.
@@ -114,35 +110,17 @@ Add this to your `Gemfile`
Or get the code here: https://github.com/jashmenn/activeuuid
-## Notes
-
-### Fixtures
-
-You can use `activeuuid` in fixtures by using the `!!binary` directive in YAML.
-The trick is to base64 encode the raw bytes of the hexdigest.
-
-```yaml
- devin_ifttt_new_tweet:
- id: !!binary "<%= Base64.encode64(UUIDTools::UUID.parse_hexdigest('2D79B402CBA811E1AA7C14DAE903E06A').raw) %>"
- data:
- user_id: 1
- source_id: 1
- recipe_id: 1
- created_at: Mon, 09 Jul 2012 14:06:21 -0700
- body: Nice blog entry!
-```
## References
* [1] http://bret.appspot.com/entry/how-friendfeed-uses-mysql
-* [2] http://kekoav.com/blog/36-computers/58-uuids-as-primary-keys-in-mysql.html
+* [2] http://kekoav.com/blog/36-computers/58-uuids-as-primary-keys-in-mysql.html
* [3] https://gist.github.com/937739
* [4] http://www.codinghorror.com/blog/2007/03/primary-keys-ids-versus-guids.html
* [5] http://krow.livejournal.com/497839.html
* [6] https://github.com/jamesgolick/friendly
## Dependencies
-Rails ~> 3.1.0 - It uses the custom column serialization Aaron
-Patterson introduced in Rails 3.1.
+Rails ~> 3.1.0
## Author
View
2 activeuuid.gemspec
@@ -27,5 +27,5 @@ Gem::Specification.new do |s|
s.add_development_dependency "mysql2"
s.add_runtime_dependency "uuidtools"
- s.add_runtime_dependency "activerecord"
+ s.add_runtime_dependency "activerecord", '>= 3.1'
end
View
62 lib/activeuuid/patches.rb
@@ -1,27 +1,65 @@
module ActiveUUID
module Patches
module Migrations
- def uuid(*args)
- options = args.extract_options!
- column_names = args
+ def uuid(*column_names)
+ options = column_names.extract_options!
column_names.each do |name|
type = @base.adapter_name.downcase == 'postgresql' ? 'uuid' : 'binary(16)'
column(name, "#{type}#{' PRIMARY KEY' if options.delete(:primary_key)}", options)
end
end
end
+ module Column
+ extend ActiveSupport::Concern
+
+ included do
+ def uuid?
+ sql_type == 'binary(16)'
+ end
+
+ def type_cast_with_uuid(value)
+ if uuid?
+ UUIDTools::UUID.serialize(value)
+ else
+ type_cast_without_uuid(value)
+ end
+ end
+
+ def type_cast_code_with_uuid(var_name)
+ if uuid?
+ "UUIDTools::UUID.serialize(#{var_name})"
+ else
+ type_cast_code_without_uuid(var_name)
+ end
+ end
+
+ alias_method_chain :type_cast, :uuid
+ alias_method_chain :type_cast_code, :uuid
+ end
+ end
+
+ module PostgreSQLColumn
+ extend ActiveSupport::Concern
+
+ included do
+ def uuid?
+ sql_type == 'uuid'
+ end
+ end
+ end
+
module Quoting
extend ActiveSupport::Concern
included do
def quote_with_visiting(value, column = nil)
- value = ActiveUUID::UUIDSerializer.new.load(value) if column && column.sql_type == 'binary(16)'
+ value = UUIDTools::UUID.serialize(value) if column && column.uuid?
quote_without_visiting(value, column)
end
def type_cast_with_visiting(value, column = nil)
- value = ActiveUUID::UUIDSerializer.new.load(value) if column && column.sql_type == 'binary(16)'
+ value = UUIDTools::UUID.serialize(value) if column && column.uuid?
type_cast_without_visiting(value, column)
end
@@ -35,12 +73,18 @@ module PostgreSQLQuoting
included do
def quote_with_visiting(value, column = nil)
- value = ActiveUUID::UUIDSerializer.new.load(value).to_s if column && column.sql_type == 'uuid'
+ if column && column.uuid?
+ value = UUIDTools::UUID.serialize(value)
+ value = value.to_s if value.is_a? UUIDTools::UUID
+ end
quote_without_visiting(value, column)
end
def type_cast_with_visiting(value, column = nil)
- value = ActiveUUID::UUIDSerializer.new.load(value).to_s if column && column.sql_type == 'uuid'
+ if column && column.uuid?
+ value = UUIDTools::UUID.serialize(value)
+ value = value.to_s if value.is_a? UUIDTools::UUID
+ end
type_cast_without_visiting(value, column)
end
@@ -52,6 +96,10 @@ def type_cast_with_visiting(value, column = nil)
def self.apply!
ActiveRecord::ConnectionAdapters::Table.send :include, Migrations if defined? ActiveRecord::ConnectionAdapters::Table
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Migrations if defined? ActiveRecord::ConnectionAdapters::TableDefinition
+
+ ActiveRecord::ConnectionAdapters::Column.send :include, Column
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn.send :include, PostgreSQLColumn if defined? ActiveRecord::ConnectionAdapters::PostgreSQLColumn
+
ActiveRecord::ConnectionAdapters::Mysql2Adapter.send :include, Quoting if defined? ActiveRecord::ConnectionAdapters::Mysql2Adapter
ActiveRecord::ConnectionAdapters::SQLite3Adapter.send :include, Quoting if defined? ActiveRecord::ConnectionAdapters::SQLite3Adapter
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :include, PostgreSQLQuoting if defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
View
119 lib/activeuuid/uuid.rb
@@ -5,6 +5,9 @@ module UUIDTools
class UUID
alias_method :id, :raw
+ # duck typing activerecord 3.1 dirty hack )
+ def gsub *; self; end
+
def quoted_id
s = raw.unpack("H*")[0]
"x'#{s}'"
@@ -18,8 +21,29 @@ def to_param
hexdigest.upcase
end
- # duck typing activerecord 3.1 dirty hack )
- def gsub *; self; end
+ def self.serialize(value)
+ case value
+ when self
+ value
+ when String
+ parse_string value
+ else
+ nil
+ end
+ end
+
+ private
+
+ def self.parse_string(str)
+ return nil if str.length == 0
+ if str.length == 36
+ parse str
+ elsif str.length == 32
+ parse_hexdigest str
+ else
+ parse_raw str
+ end
+ end
end
end
@@ -52,89 +76,53 @@ def visit_UUIDTools_UUID(o)
end
module ActiveUUID
- class UUIDSerializer
- def load(binary)
- case binary
- when UUIDTools::UUID
- binary
- when String
- parse_string(binary)
- else
- nil
- end
- end
-
- def dump(uuid)
- case uuid
- when UUIDTools::UUID
- uuid.raw
- when String
- parse_string(uuid).try(:raw)
- else
- nil
- end
- end
-
- private
-
- def parse_string str
- return nil if str.length == 0
- if str.length == 36
- UUIDTools::UUID.parse str
- elsif str.length == 32
- UUIDTools::UUID.parse_hexdigest str
- else
- UUIDTools::UUID.parse_raw str
- end
- end
- end
-
module UUID
extend ActiveSupport::Concern
included do
- class_attribute :uuid_attributes, :instance_writer => true
- uuids :id
+ class_attribute :_natural_key, instance_writer: false
+ class_attribute :_uuid_generator, instance_writer: false
+ self._uuid_generator = :random
+
+ singleton_class.alias_method_chain :instantiate, :uuid
before_create :generate_uuids_if_needed
end
module ClassMethods
- def natural_key_attributes
- @_activeuuid_natural_key_attributes
- end
-
def natural_key(*attributes)
- @_activeuuid_natural_key_attributes = attributes
+ self._natural_key = attributes
end
- def uuid_generator(generator_name=nil)
- @_activeuuid_kind = generator_name if generator_name
- @_activeuuid_kind || :random
+ def uuid_generator(generator_name)
+ self._uuid_generator = generator_name
end
def uuids(*attributes)
- self.uuid_attributes = attributes.collect(&:intern).each do |attribute|
- serialize attribute, ActiveUUID::UUIDSerializer.new
- # serializing attributes on the fly
- define_method "#{attribute}=" do |value|
- write_attribute attribute, serialized_attributes[attribute.to_s].load(value)
- end
+ ActiveSupport::Deprecation.warn <<-EOS
+ ActiveUUID detects uuid columns independently.
+ There is no more need to use uuid method.
+ EOS
+ end
+
+ def instantiate_with_uuid(record)
+ uuid_columns.each do |uuid_column|
+ record[uuid_column] = UUIDTools::UUID.serialize(record[uuid_column]).to_s if record[uuid_column]
end
- #class_eval <<-eos
- # # def #{@association_name}
- # # @_#{@association_name} ||= self.class.associations[:#{@association_name}].new_proxy(self)
- # # end
- #eos
+ instantiate_without_uuid(record)
+ end
+
+ def uuid_columns
+ @uuid_columns ||= columns.select(&:uuid?).map(&:name)
end
end
def create_uuid
- if nka = self.class.natural_key_attributes
+ if _natural_key
# TODO if all the attributes return nil you might want to warn about this
- chained = nka.collect{|a| self.send(a).to_s}.join("-")
+ chained = _natural_key.map { |attribute| self.send(attribute) }.join('-')
UUIDTools::UUID.sha1_create(UUIDTools::UUID_OID_NAMESPACE, chained)
else
- case self.class.uuid_generator
+ case _uuid_generator
when :random
UUIDTools::UUID.random_create
when :time
@@ -144,8 +132,9 @@ def create_uuid
end
def generate_uuids_if_needed
- (uuid_attributes & [self.class.primary_key.intern]).each do |attr|
- self.send("#{attr}=", create_uuid) unless self.send(attr)
+ primary_key = self.class.primary_key
+ if self.class.columns_hash[primary_key].uuid?
+ send("#{primary_key}=", create_uuid) unless send("#{primary_key}?")
end
end
View
66 spec/lib/activerecord_spec.rb
@@ -18,18 +18,19 @@
end
context '.where' do
- specify { model.where(:id => id).first.should == article }
+ specify { model.where(id: id).first.should == article }
end
- context '.destroy' do
- specify { article.delete.should be_true }
- specify { article.destroy.should be_true }
+ context '#destroy' do
+ subject { article }
+ its(:delete) { should be_true }
+ its(:destroy) { should be_true }
end
end
describe UuidArticle do
let!(:article) { Fabricate :uuid_article }
- let(:id) { article.id }
+ let!(:id) { article.id }
let(:model) { UuidArticle }
specify { model.primary_key.should == 'id' }
@@ -50,14 +51,55 @@
end
context '.where' do
- specify { model.where(:id => article).first.should == article }
- specify { model.where(:id => id).first.should == article }
- specify { model.where(:id => id.to_s).first.should == article }
- specify { model.where(:id => id.raw).first.should == article }
+ specify { model.where(id: article).first.should == article }
+ specify { model.where(id: id).first.should == article }
+ specify { model.where(id: id.to_s).first.should == article }
+ specify { model.where(id: id.raw).first.should == article }
+ end
+
+ context '#destroy' do
+ subject { article }
+ its(:delete) { should be_true }
+ its(:destroy) { should be_true }
+ end
+
+ context '#reload' do
+ subject { article }
+ its(:'reload.id') { should == id }
+ specify { subject.reload(:select => :another_uuid).id.should == id }
end
- context '.destroy' do
- specify { article.delete.should be_true }
- specify { article.destroy.should be_true }
+ context 'typecasting' do
+ let(:uuid) { UUIDTools::UUID.random_create }
+ let(:string) { uuid.to_s }
+ context 'primary' do
+ before { article.id = string }
+ specify do
+ article.id.should == uuid
+ article.id_before_type_cast.should == string
+ end
+ specify do
+ article.id_before_type_cast.should == string
+ article.id.should == uuid
+ end
+ end
+
+ context 'non-primary' do
+ before { article.another_uuid = string }
+ specify do
+ article.another_uuid.should == uuid
+ article.another_uuid_before_type_cast.should == string
+ end
+ specify do
+ article.another_uuid_before_type_cast.should == string
+ article.another_uuid.should == uuid
+ end
+ specify do
+ article.save
+ article.reload
+ article.another_uuid_before_type_cast.should == string
+ article.another_uuid.should == uuid
+ end
+ end
end
end
View
76 spec/lib/serializer_spec.rb
@@ -1,76 +0,0 @@
-require 'spec_helper'
-
-describe ActiveUUID::UUIDSerializer do
-
- before do
- @input = "2d79b402-cba8-11e1-aa7c-14dae903e06a"
- @hex = "2D79B402CBA811E1AA7C14DAE903E06A"
- @uuid = UUIDTools::UUID.parse @input
- @raw = @uuid.raw
- @serializer = ActiveUUID::UUIDSerializer.new
- end
-
- describe '#load' do
- it 'returns a UUID type verbatim' do
- @serializer.load(@uuid).should eql(@uuid)
- end
-
- describe 'loads a given uuid' do
- it 'handles uuid string' do
- @serializer.load(@input).should eql(@uuid)
- end
-
- it 'handles uuid hexdigest string' do
- @serializer.load(@hex).should eql(@uuid)
- end
-
- it 'handles raw uuid data' do
- @serializer.load(@raw).should eql(@uuid)
- end
- end
-
- it 'returns nil for nil' do
- @serializer.load(nil).should be_nil
- end
-
- it 'returns nil for ""' do
- @serializer.load('').should be_nil
- end
-
- it 'returns nil for other types' do
- @serializer.load(5).should be_nil
- end
- end
-
- describe '#dump' do
- it 'returns the raw value of a passed uuid' do
- @serializer.dump(@uuid).should eql(@raw)
- end
-
- describe 'loads a given uuid' do
- it 'handles uuid string' do
- @serializer.dump(@input).should eql(@raw)
- end
-
- it 'handles uuid hexdigest string' do
- @serializer.dump(@hex).should eql(@raw)
- end
-
- it 'handles raw uuid data' do
- @serializer.dump(@raw).should eql(@raw)
- end
- end
-
- it 'returns nil for nil' do
- @serializer.dump(nil).should be_nil
- end
-
- it 'returns nil for ""' do
- @serializer.dump('').should be_nil
- end
-
- it 'returns nil for other types' do
- @serializer.dump(5).should be_nil
- end
- end
-end
View
48 spec/lib/uuid_mokeypatch_spec.rb
@@ -1,37 +1,29 @@
require 'spec_helper'
describe UUIDTools::UUID do
+ let(:input) { "e4618518-cb9f-11e1-aa7c-14dae903e06a" }
+ let(:hex) { "E4618518CB9F11E1AA7C14DAE903E06A" }
+ let(:uuid) { described_class.parse input }
- before do
- input = "e4618518-cb9f-11e1-aa7c-14dae903e06a"
- @sql_out = "x'e4618518cb9f11e1aa7c14dae903e06a'"
- @param_out = "E4618518CB9F11E1AA7C14DAE903E06A"
+ context 'instance methods' do
+ subject { uuid }
+ let(:sql_out) { "x'e4618518cb9f11e1aa7c14dae903e06a'" }
- @uuid = UUIDTools::UUID.parse input
+ its(:quoted_id) {should == sql_out}
+ its(:as_json) {should == hex}
+ its(:to_param) {should == hex}
end
- it 'adds methods to the UUID class' do
- [:quoted_id, :as_json, :to_param].each do |meth|
- @uuid.should respond_to(meth)
- end
+ describe '.serialize' do
+ subject { described_class }
+ let(:raw) { uuid.raw }
+
+ specify { subject.serialize(uuid).should == uuid }
+ specify { subject.serialize(input).should == uuid }
+ specify { subject.serialize(hex).should == uuid }
+ specify { subject.serialize(raw).should == uuid }
+ specify { subject.serialize(nil).should be_nil }
+ specify { subject.serialize('').should be_nil }
+ specify { subject.serialize(5).should be_nil }
end
-
- describe '#quoted_id' do
- it 'returns the SQL binary representation for the uuid' do
- @uuid.quoted_id.should eql(@sql_out)
- end
- end
-
- describe '#as_json' do
- it 'returns the uppercase hexdigest' do
- @uuid.as_json.should eql(@param_out)
- end
- end
-
- describe '#to_param' do
- it 'also returns the uppercase hexdigest' do
- @uuid.to_param.should eql(@param_out)
- end
- end
-
end
View
1 spec/support/migrate/20120817081813_create_uuid_articles.rb
@@ -4,6 +4,7 @@ def change
t.uuid :id, :primary_key => true
t.string :title
t.text :body
+ t.uuid :another_uuid
t.timestamps
end
View
1 spec/support/models/uuid_article.rb
@@ -1,4 +1,3 @@
class UuidArticle < ActiveRecord::Base
include ActiveUUID::UUID
-
end

0 comments on commit 9208f01

Please sign in to comment.