clear_empty_attributes 0.2.0
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.
- data/.gitignore +2 -0
- data/README.markdown +38 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/clear_empty_attributes.gemspec +55 -0
- data/init.rb +1 -0
- data/lib/clear_empty_attributes.rb +15 -0
- data/spec/clear_empty_attributes_spec.rb +88 -0
- data/spec/setup_test_model.rb +22 -0
- data/spec/spec_helper.rb +4 -0
- data/tasks/clear_empty_attributes.rake +16 -0
- metadata +76 -0
data/.gitignore
ADDED
data/README.markdown
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Problem
|
|
2
|
+
=======
|
|
3
|
+
When AR objects are saved, empty fields are saved as '' instead of nil.
|
|
4
|
+
|
|
5
|
+
- Complicates queries for empty fields (`WHERE field IS NULL OR field = ''`)
|
|
6
|
+
- Makes the use of `unless field.blank?` necessary (opposed to only `if field`)
|
|
7
|
+
- Can lead to late-detected bugs because most of the time strings were `filled or ''` and suddenly they are `nil`
|
|
8
|
+
- Some validations do not support `:allow_blank=>true`
|
|
9
|
+
- Databases can handle `NULL` better & faster than empty strings (especially when using `LIKE`)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Solution
|
|
13
|
+
========
|
|
14
|
+
Defines an AR `before_validation` that sets empty Strings to nil.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Install
|
|
18
|
+
=======
|
|
19
|
+
` script/plugin install git://github.com/collectiveidea/clear_empty_attributes.git `
|
|
20
|
+
OR
|
|
21
|
+
` sudo gem install clear_empty_attributes -s http://gemcutter.org `
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Migration
|
|
25
|
+
=========
|
|
26
|
+
When you are switching to `clear_empty_attributes`, run this task
|
|
27
|
+
to remove any `''` strings/texts from your database.
|
|
28
|
+
|
|
29
|
+
rake db:clear_empty_attributes
|
|
30
|
+
(only works when checked out or installed as plugin)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Authors
|
|
34
|
+
=======
|
|
35
|
+
- [Brandon Keepers](http://opensoul.org)
|
|
36
|
+
- [Michael Grosser](http://pragmatig.wordpress.com)
|
|
37
|
+
|
|
38
|
+
Hereby placed under public domain, do what you want, just do not hold anybody accountable...
|
data/Rakefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
desc "Run all specs in spec directory"
|
|
2
|
+
task :default do |t|
|
|
3
|
+
options = "--colour --format progress --loadby --reverse"
|
|
4
|
+
files = FileList['spec/**/*_spec.rb']
|
|
5
|
+
system("spec #{options} #{files}")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'jeweler'
|
|
10
|
+
project_name = 'clear_empty_attributes'
|
|
11
|
+
Jeweler::Tasks.new do |gem|
|
|
12
|
+
gem.name = project_name
|
|
13
|
+
gem.description = gem.summary = "Save empty strings as nil to avoid lots of problems"
|
|
14
|
+
gem.email = "brandon@opensoul.org"
|
|
15
|
+
gem.homepage = "http://github.com/grosser/#{project_name}"
|
|
16
|
+
gem.authors = ["Brandon Keepers"]
|
|
17
|
+
gem.add_dependency ['activerecord']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Jeweler::GemcutterTasks.new
|
|
21
|
+
rescue LoadError
|
|
22
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
|
23
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{clear_empty_attributes}
|
|
8
|
+
s.version = "0.2.0"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Brandon Keepers"]
|
|
12
|
+
s.date = %q{2009-11-13}
|
|
13
|
+
s.description = %q{Save empty strings as nil to avoid lots of problems}
|
|
14
|
+
s.email = %q{brandon@opensoul.org}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"README.markdown"
|
|
17
|
+
]
|
|
18
|
+
s.files = [
|
|
19
|
+
".gitignore",
|
|
20
|
+
"README.markdown",
|
|
21
|
+
"Rakefile",
|
|
22
|
+
"VERSION",
|
|
23
|
+
"clear_empty_attributes.gemspec",
|
|
24
|
+
"init.rb",
|
|
25
|
+
"lib/clear_empty_attributes.rb",
|
|
26
|
+
"spec/clear_empty_attributes_spec.rb",
|
|
27
|
+
"spec/setup_test_model.rb",
|
|
28
|
+
"spec/spec_helper.rb",
|
|
29
|
+
"tasks/clear_empty_attributes.rake"
|
|
30
|
+
]
|
|
31
|
+
s.homepage = %q{http://github.com/grosser/clear_empty_attributes}
|
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
33
|
+
s.require_paths = ["lib"]
|
|
34
|
+
s.rubygems_version = %q{1.3.5}
|
|
35
|
+
s.summary = %q{Save empty strings as nil to avoid lots of problems}
|
|
36
|
+
s.test_files = [
|
|
37
|
+
"spec/spec_helper.rb",
|
|
38
|
+
"spec/clear_empty_attributes_spec.rb",
|
|
39
|
+
"spec/setup_test_model.rb"
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
if s.respond_to? :specification_version then
|
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
44
|
+
s.specification_version = 3
|
|
45
|
+
|
|
46
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
47
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 0"])
|
|
48
|
+
else
|
|
49
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
data/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'clear_empty_attributes'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'activerecord'
|
|
2
|
+
|
|
3
|
+
class ActiveRecord::Base
|
|
4
|
+
|
|
5
|
+
before_validation :clear_empty_attrs
|
|
6
|
+
before_save :clear_empty_attrs # needed to work correctly with update_attribute too
|
|
7
|
+
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
def clear_empty_attrs
|
|
11
|
+
@attributes.each do |key,value|
|
|
12
|
+
self[key] = nil if value.is_a?(String) && value.blank?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "spec/spec_helper"
|
|
2
|
+
|
|
3
|
+
describe :clear_empty_attributes do
|
|
4
|
+
before do
|
|
5
|
+
@user = User.new(:name=>'Michael')
|
|
6
|
+
@user.save!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe :validations do
|
|
10
|
+
it "is not valid with a invalid string" do
|
|
11
|
+
@user.name = 'a'
|
|
12
|
+
@user.should_not be_valid
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "is valid with a allow_nil field set to empty string" do
|
|
16
|
+
@user.name = ''
|
|
17
|
+
@user.should be_valid
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe :update_attributes do
|
|
22
|
+
it "stores non-empty strings normally" do
|
|
23
|
+
@user.update_attributes(:name=>'Hans')
|
|
24
|
+
@user.reload.name.should == 'Hans'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "stores strings as nil" do
|
|
28
|
+
@user.update_attributes(:name=>'')
|
|
29
|
+
@user.reload.name.should == nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "does not affect booleans" do
|
|
33
|
+
@user.update_attributes(:sexy=>false)
|
|
34
|
+
@user.reload.sexy.should == false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe :attributes= do
|
|
39
|
+
it "stores non-empty strings normally" do
|
|
40
|
+
@user.attributes = {'name'=>'Hans'}
|
|
41
|
+
@user.save!
|
|
42
|
+
@user.reload.name.should == 'Hans'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "stores strings as nil" do
|
|
46
|
+
@user.attributes = {:name=>''}
|
|
47
|
+
@user.save!
|
|
48
|
+
@user.reload.name.should == nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe :update_attribute do
|
|
53
|
+
it "stores non-empty strings normally" do
|
|
54
|
+
@user.update_attribute(:name,'Hans')
|
|
55
|
+
@user.reload.name.should == 'Hans'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "stores strings as nil" do
|
|
59
|
+
@user.update_attribute(:name,'')
|
|
60
|
+
@user.reload.name.should == nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "does not affect booleans" do
|
|
64
|
+
@user.update_attribute(:sexy,false)
|
|
65
|
+
@user.reload.sexy.should == false
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe :write_attribute do
|
|
70
|
+
it "stores non-empty strings normally" do
|
|
71
|
+
@user.write_attribute(:name,'Hans')
|
|
72
|
+
@user.save!
|
|
73
|
+
@user.reload.name.should == 'Hans'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "stores strings as nil" do
|
|
77
|
+
@user.write_attribute(:name,'')
|
|
78
|
+
@user.save!
|
|
79
|
+
@user.reload.name.should == nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "does not affect booleans" do
|
|
83
|
+
@user.write_attribute(:sexy,false)
|
|
84
|
+
@user.save!
|
|
85
|
+
@user.reload.sexy.should == false
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# connect
|
|
2
|
+
ActiveRecord::Base.configurations = {"test" => {
|
|
3
|
+
:adapter => "sqlite3",
|
|
4
|
+
:database => ":memory:",
|
|
5
|
+
}.with_indifferent_access}
|
|
6
|
+
|
|
7
|
+
ActiveRecord::Base.establish_connection(:test)
|
|
8
|
+
|
|
9
|
+
# create table
|
|
10
|
+
ActiveRecord::Schema.define(:version => 1) do
|
|
11
|
+
create_table :users do |t|
|
|
12
|
+
t.string :name
|
|
13
|
+
t.boolean :sexy
|
|
14
|
+
t.integer :age
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# create model
|
|
20
|
+
class User < ActiveRecord::Base
|
|
21
|
+
validates_length_of :name, :within=>2..20, :allow_nil=>true
|
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
namespace :db do
|
|
2
|
+
desc "Clear all blank strings in the database. Specify TABLES=table1,table2 to limit to specific tables."
|
|
3
|
+
task :clear_empty_attributes => :environment do
|
|
4
|
+
connection = ActiveRecord::Base.connection
|
|
5
|
+
tables = ENV['TABLES'] ? ENV['TABLES'].split(/,/) : connection.tables
|
|
6
|
+
tables.each do |table|
|
|
7
|
+
puts "#{table}:"
|
|
8
|
+
connection.columns(table).select(&:text?).each do |column|
|
|
9
|
+
name = column.name
|
|
10
|
+
puts " #{name}"
|
|
11
|
+
column_name = connection.quote_column_name(column.name)
|
|
12
|
+
connection.update("UPDATE #{connection.quote_table_name(table)} SET #{column_name} = NULL WHERE #{column_name} = ''")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clear_empty_attributes
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brandon Keepers
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-11-13 00:00:00 +01:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: activerecord
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: "0"
|
|
24
|
+
version:
|
|
25
|
+
description: Save empty strings as nil to avoid lots of problems
|
|
26
|
+
email: brandon@opensoul.org
|
|
27
|
+
executables: []
|
|
28
|
+
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files:
|
|
32
|
+
- README.markdown
|
|
33
|
+
files:
|
|
34
|
+
- .gitignore
|
|
35
|
+
- README.markdown
|
|
36
|
+
- Rakefile
|
|
37
|
+
- VERSION
|
|
38
|
+
- clear_empty_attributes.gemspec
|
|
39
|
+
- init.rb
|
|
40
|
+
- lib/clear_empty_attributes.rb
|
|
41
|
+
- spec/clear_empty_attributes_spec.rb
|
|
42
|
+
- spec/setup_test_model.rb
|
|
43
|
+
- spec/spec_helper.rb
|
|
44
|
+
- tasks/clear_empty_attributes.rake
|
|
45
|
+
has_rdoc: true
|
|
46
|
+
homepage: http://github.com/grosser/clear_empty_attributes
|
|
47
|
+
licenses: []
|
|
48
|
+
|
|
49
|
+
post_install_message:
|
|
50
|
+
rdoc_options:
|
|
51
|
+
- --charset=UTF-8
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: "0"
|
|
59
|
+
version:
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: "0"
|
|
65
|
+
version:
|
|
66
|
+
requirements: []
|
|
67
|
+
|
|
68
|
+
rubyforge_project:
|
|
69
|
+
rubygems_version: 1.3.5
|
|
70
|
+
signing_key:
|
|
71
|
+
specification_version: 3
|
|
72
|
+
summary: Save empty strings as nil to avoid lots of problems
|
|
73
|
+
test_files:
|
|
74
|
+
- spec/spec_helper.rb
|
|
75
|
+
- spec/clear_empty_attributes_spec.rb
|
|
76
|
+
- spec/setup_test_model.rb
|