1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
require 'cases/helper'
module ActiveRecord
class AttributeDecoratorsTest < ActiveRecord::TestCase
class Model < ActiveRecord::Base
self.table_name = 'attribute_decorators_model'
end
class StringDecorator < SimpleDelegator
def initialize(delegate, decoration = "decorated!")
@decoration = decoration
super(delegate)
end
def type_cast_from_user(value)
"#{super} #{@decoration}"
end
alias type_cast_from_database type_cast_from_user
end
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table :attribute_decorators_model, force: true do |t|
t.string :a_string
end
end
teardown do
return unless @connection
@connection.execute 'DROP TABLE IF EXISTS attribute_decorators_model'
Model.attribute_type_decorations.clear
Model.reset_column_information
end
test "attributes can be decorated" do
model = Model.new(a_string: 'Hello')
assert_equal 'Hello', model.a_string
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
model = Model.new(a_string: 'Hello')
assert_equal 'Hello decorated!', model.a_string
end
test "decoration does not eagerly load existing columns" do
assert_no_queries do
Model.reset_column_information
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
end
end
test "undecorated columns are not touched" do
Model.attribute :another_string, Type::String.new, default: 'something or other'
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
assert_equal 'something or other', Model.new.another_string
end
test "decorators can be chained" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :other) { |t| StringDecorator.new(t) }
model = Model.new(a_string: 'Hello!')
assert_equal 'Hello! decorated! decorated!', model.a_string
end
test "decoration of the same type multiple times is idempotent" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
model = Model.new(a_string: 'Hello')
assert_equal 'Hello decorated!', model.a_string
end
test "decorations occur in order of declaration" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :other) do |type|
StringDecorator.new(type, 'decorated again!')
end
model = Model.new(a_string: 'Hello!')
assert_equal 'Hello! decorated! decorated again!', model.a_string
end
test "decorating attributes does not modify parent classes" do
Model.attribute :another_string, Type::String.new, default: 'whatever'
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
child_class = Class.new(Model)
child_class.decorate_attribute_type(:another_string, :test) { |t| StringDecorator.new(t) }
child_class.decorate_attribute_type(:a_string, :other) { |t| StringDecorator.new(t) }
model = Model.new(a_string: 'Hello!')
child = child_class.new(a_string: 'Hello!')
assert_equal 'Hello! decorated!', model.a_string
assert_equal 'whatever', model.another_string
assert_equal 'Hello! decorated! decorated!', child.a_string
# We are round tripping the default, and we don't undo our decoration
assert_equal 'whatever decorated! decorated!', child.another_string
end
test "defaults are decorated on the column" do
Model.attribute :a_string, Type::String.new, default: 'whatever'
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
column = Model.columns_hash['a_string']
assert_equal 'whatever decorated!', column.default
end
class Multiplier < SimpleDelegator
def type_cast_from_user(value)
return if value.nil?
value * 2
end
alias type_cast_from_database type_cast_from_user
end
test "decorating with a proc" do
Model.attribute :an_int, Type::Integer.new
type_is_integer = proc { |_, type| type.type == :integer }
Model.decorate_matching_attribute_types type_is_integer, :multiplier do |type|
Multiplier.new(type)
end
model = Model.new(a_string: 'whatever', an_int: 1)
assert_equal 'whatever', model.a_string
assert_equal 2, model.an_int
end
end
end
|