attr_bucket 0.1.2 → 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/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
|