attr_bucket 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -0
- data/attr_bucket.gemspec +3 -1
- data/lib/attr_bucket.rb +22 -1
- data/lib/attr_bucket/version.rb +1 -1
- data/spec/attr_bucket_spec.rb +139 -0
- data/spec/helpers/attr_bucket_helper.rb +15 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/schema.rb +27 -0
- metadata +40 -5
data/Rakefile
CHANGED
data/attr_bucket.gemspec
CHANGED
@@ -12,7 +12,9 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{Your model can has a bucket (for its attributes).}
|
13
13
|
s.description = %q{Store a few extra (non-searchable) attributes away in a bucket. This is probably a horrible idea, but try it anyway.}
|
14
14
|
|
15
|
-
s.add_runtime_dependency
|
15
|
+
s.add_runtime_dependency 'activerecord', '~> 3.0.0'
|
16
|
+
s.add_development_dependency 'rspec', '~> 2.4.0'
|
17
|
+
s.add_development_dependency 'sqlite3'
|
16
18
|
|
17
19
|
s.rubyforge_project = "attr_bucket"
|
18
20
|
|
data/lib/attr_bucket.rb
CHANGED
@@ -14,6 +14,22 @@ module AttrBucket
|
|
14
14
|
read_attribute(name)
|
15
15
|
end
|
16
16
|
|
17
|
+
def valid_class(value, type)
|
18
|
+
case type
|
19
|
+
when :integer then Fixnum === value
|
20
|
+
when :float then Float === value
|
21
|
+
when :decimal then BigDecimal === value
|
22
|
+
when :datetime then Time === value
|
23
|
+
when :date then Date === value
|
24
|
+
when :timestamp then Time === value
|
25
|
+
when :time then Time === value
|
26
|
+
when :text, :string then String === value
|
27
|
+
when :binary then String === value
|
28
|
+
when :boolean then [TrueClass, FalseClass].grep value
|
29
|
+
else false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
17
33
|
# Swipe the nifty column typecasting from the column class
|
18
34
|
# underlying the bucket column, or use the call method of
|
19
35
|
# the object supplied for +type+ if it responds to call.
|
@@ -25,7 +41,7 @@ module AttrBucket
|
|
25
41
|
|
26
42
|
return type.call(value) if type.respond_to?(:call)
|
27
43
|
|
28
|
-
case type
|
44
|
+
typecasted = case type
|
29
45
|
when :string then value.to_s
|
30
46
|
when :text then value.to_s
|
31
47
|
when :integer then value.to_i rescue value ? 1 : 0
|
@@ -39,6 +55,10 @@ module AttrBucket
|
|
39
55
|
when :boolean then column_class.value_to_boolean(value)
|
40
56
|
else value
|
41
57
|
end
|
58
|
+
|
59
|
+
raise ArgumentError, "Unable to typecast #{value} to #{type}" unless valid_class(typecasted, type)
|
60
|
+
|
61
|
+
typecasted
|
42
62
|
end
|
43
63
|
|
44
64
|
module ClassMethods
|
@@ -107,6 +127,7 @@ module AttrBucket
|
|
107
127
|
define_method "#{attr_name}=" do |val|
|
108
128
|
# TODO: Make this more resilient/granular for multiple bucketed changes
|
109
129
|
send("#{bucket_name}_will_change!")
|
130
|
+
typecasted = explicitly_type_cast(val, attr_type, column_class)
|
110
131
|
get_attr_bucket(bucket_name)[attr_name] = explicitly_type_cast(val, attr_type, column_class)
|
111
132
|
end unless method_defined? "#{attr_name}="
|
112
133
|
end
|
data/lib/attr_bucket/version.rb
CHANGED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttrBucket do
|
4
|
+
|
5
|
+
it 'should define an private attr_bucket method in AR::Base' do
|
6
|
+
ActiveRecord::Base.private_methods.should include :attr_bucket
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should define an i_has_a_bucket alias in AR::Base' do
|
10
|
+
ActiveRecord::Base.private_methods.should include :i_has_a_bucket
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should raise an ArgumentError when defining a bucket on a non-text column' do
|
14
|
+
expect { a_class_with_bucket :name => :blah }.to raise_error ArgumentError
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'object with a bucketed attribute named "nickname"' do
|
18
|
+
before do
|
19
|
+
@o = an_object_with_bucket :bucket => :nickname
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should cast nickname to string' do
|
23
|
+
@o.nickname = 42
|
24
|
+
@o.nickname.should eq '42'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should support validations' do
|
28
|
+
@o.class.validates_presence_of :nickname
|
29
|
+
expect { @o.save! }.to raise_error ActiveRecord::RecordInvalid
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should maintain bucketed attributes when saved' do
|
33
|
+
@o.nickname = 'Headly von Headenstein'
|
34
|
+
@o.save
|
35
|
+
|
36
|
+
persisted_item.nickname.should eq 'Headly von Headenstein'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'object with bucketed attributes in array format' do
|
41
|
+
before do
|
42
|
+
@o = an_object_with_bucket :bucket => [:quest, :favorite_color]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should cast all values to strings' do
|
46
|
+
@o.quest = 1
|
47
|
+
@o.favorite_color = 2
|
48
|
+
|
49
|
+
[@o.quest, @o.favorite_color].should eq ['1', '2']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'object with bucketed attributes in hash format' do
|
54
|
+
before do
|
55
|
+
@o = an_object_with_bucket :bucket => {
|
56
|
+
:summary => :text,
|
57
|
+
:age => :integer,
|
58
|
+
:body_temp => :float,
|
59
|
+
:salary => :decimal,
|
60
|
+
:hired_at => :datetime,
|
61
|
+
:stamp => :timestamp,
|
62
|
+
:workday_starts_at => :time,
|
63
|
+
:birthday => :date,
|
64
|
+
:binary_data => :binary,
|
65
|
+
:is_awesome => :boolean,
|
66
|
+
:processed_name => proc { |val| "PROCESSED: #{val}" }
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'typecasts to String for :text' do
|
71
|
+
@o.summary = 12345
|
72
|
+
@o.summary.should be_a String
|
73
|
+
@o.summary.should eq '12345'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'typecasts to Fixnum for :integer' do
|
77
|
+
@o.age = '33'
|
78
|
+
@o.age.should be_a Fixnum
|
79
|
+
@o.age.should eq 33
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'typecasts to Float for :float' do
|
83
|
+
@o.body_temp = '98.6'
|
84
|
+
@o.body_temp.should be_a Float
|
85
|
+
@o.body_temp.should eq 98.6
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'typecasts to BigDecimal for :decimal' do
|
89
|
+
@o.salary = '50000.23'
|
90
|
+
@o.salary.should be_a BigDecimal
|
91
|
+
@o.salary.should eq 50000.23
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'typecasts to Time for :datetime' do
|
95
|
+
@o.hired_at = '2011-01-01 08:00:00'
|
96
|
+
@o.hired_at.should be_a Time
|
97
|
+
@o.hired_at.should eq Time.parse '2011-01-01 08:00:00'
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'typecasts to Time for :timestamp' do
|
101
|
+
@o.stamp = '2011-01-01 08:00:00'
|
102
|
+
@o.stamp.should be_a Time
|
103
|
+
@o.stamp.should eq Time.parse '2011-01-01 08:00:00'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'typecasts to dummy Time for :time' do
|
107
|
+
@o.workday_starts_at = '08:00:00'
|
108
|
+
@o.workday_starts_at.should be_a Time
|
109
|
+
@o.workday_starts_at.should eq Time.parse '2000-01-01 08:00:00'
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'typecasts to Date for :date in string format' do
|
113
|
+
@o.birthday = '1977/8/29'
|
114
|
+
@o.birthday.should be_a Date
|
115
|
+
@o.birthday.should eq Date.new(1977, 8, 29)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'typecasts to String for :binary' do
|
119
|
+
@o.binary_data = File.read(__FILE__)
|
120
|
+
@o.binary_data.should be_a String
|
121
|
+
@o.binary_data.should match /@o.binary_data.should match/ # ooh, meta.
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'typecasts to TrueClass/FalseClass for :boolean' do
|
125
|
+
@o.is_awesome = '1'
|
126
|
+
@o.should be_awesome
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'performs custom processing when an attribute has a proc "type"' do
|
130
|
+
@o.processed_name = 'Ernie'
|
131
|
+
@o.processed_name.should be_a String
|
132
|
+
@o.processed_name.should eq 'PROCESSED: Ernie'
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should raise ArgumentError if unable to typecast a value' do
|
136
|
+
expect { @o.stamp = 123456 }.to raise_error ArgumentError
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AttrBucketHelper
|
2
|
+
def a_class_with_bucket(opts)
|
3
|
+
Item.delete_all
|
4
|
+
Object.send :remove_const, :AttrBucketItem if Object.const_defined? :AttrBucketItem
|
5
|
+
Object.const_set :AttrBucketItem, Class.new(Item) { attr_bucket opts }
|
6
|
+
end
|
7
|
+
|
8
|
+
def an_object_with_bucket(opts)
|
9
|
+
a_class_with_bucket(opts).new
|
10
|
+
end
|
11
|
+
|
12
|
+
def persisted_item
|
13
|
+
AttrBucketItem.first
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'attr_bucket'
|
4
|
+
|
5
|
+
Dir[File.expand_path('../{helpers,support}/*.rb', __FILE__)].each do |f|
|
6
|
+
require f
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.before :suite do
|
11
|
+
Schema.create
|
12
|
+
end
|
13
|
+
|
14
|
+
config.include AttrBucketHelper
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection(
|
2
|
+
:adapter => 'sqlite3',
|
3
|
+
:database => ':memory:'
|
4
|
+
)
|
5
|
+
|
6
|
+
class Item < ActiveRecord::Base
|
7
|
+
def awesome?
|
8
|
+
respond_to?(:is_awesome) && is_awesome
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Schema
|
13
|
+
def self.create
|
14
|
+
ActiveRecord::Base.silence do
|
15
|
+
ActiveRecord::Migration.verbose = false
|
16
|
+
|
17
|
+
ActiveRecord::Schema.define do
|
18
|
+
create_table :items, :force => true do |t|
|
19
|
+
t.string :name
|
20
|
+
t.string :type
|
21
|
+
t.text :bucket
|
22
|
+
t.text :other_bucket
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ernie Miller
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-29 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -32,6 +32,34 @@ dependencies:
|
|
32
32
|
version: 3.0.0
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 2
|
45
|
+
- 4
|
46
|
+
- 0
|
47
|
+
version: 2.4.0
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: sqlite3
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
35
63
|
description: Store a few extra (non-searchable) attributes away in a bucket. This is probably a horrible idea, but try it anyway.
|
36
64
|
email:
|
37
65
|
- ernie@metautonomo.us
|
@@ -50,6 +78,10 @@ files:
|
|
50
78
|
- attr_bucket.gemspec
|
51
79
|
- lib/attr_bucket.rb
|
52
80
|
- lib/attr_bucket/version.rb
|
81
|
+
- spec/attr_bucket_spec.rb
|
82
|
+
- spec/helpers/attr_bucket_helper.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- spec/support/schema.rb
|
53
85
|
has_rdoc: true
|
54
86
|
homepage: http://metautonomo.us
|
55
87
|
licenses: []
|
@@ -82,5 +114,8 @@ rubygems_version: 1.3.7
|
|
82
114
|
signing_key:
|
83
115
|
specification_version: 3
|
84
116
|
summary: Your model can has a bucket (for its attributes).
|
85
|
-
test_files:
|
86
|
-
|
117
|
+
test_files:
|
118
|
+
- spec/attr_bucket_spec.rb
|
119
|
+
- spec/helpers/attr_bucket_helper.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
- spec/support/schema.rb
|