easy_model 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
 
3
- require 'active_record'
3
+ require_relative 'column_for_active_model'
4
+ require_relative 'column_for_active_record'
4
5
 
5
6
  #
6
7
  # テーブルに存在しないカラムを定義する機能を提供する.
@@ -11,47 +12,10 @@ require 'active_record'
11
12
  module EasyModel::Column
12
13
 
13
14
  def self.included(base)
14
- base.extend(EasyModel::Column::ClassMethods)
15
- end
16
-
17
- end
18
-
19
- module EasyModel::Column::ClassMethods
20
-
21
- protected
22
-
23
- #
24
- # テーブルに存在しないカラムを定義する.
25
- #
26
- # ==== 引数
27
- # name::
28
- # カラム名.
29
- #
30
- # type::
31
- # データ型.
32
- #
33
- # options [:default]::
34
- # デフォルト値.
35
- # 省略した場合は nil.
36
- #
37
- # ==== 戻り値
38
- # 定義したカラムを表す ActiveRecord::ConnectionAdapters::Column オブジェクト.
39
- #
40
- def column(name, type, options={})
41
- ActiveRecord::ConnectionAdapters::Column.new(name, options[:default], type, true).tap do |column|
42
- define_method("#{name}=") do |value|
43
- value = nil if column.number? and value.kind_of?(String) and value.blank?
44
- instance_variable_set("@#{name}_before_type_cast", value)
45
- instance_variable_set("@#{name}", column.type_cast(value))
46
- end
47
- define_method("#{name}_before_type_cast") do
48
- instance_variable_set("@#{name}_before_type_cast", column.default) unless instance_variable_defined?("@#{name}_before_type_cast")
49
- instance_variable_get("@#{name}_before_type_cast")
50
- end
51
- define_method(name) do
52
- instance_variable_set("@#{name}", column.default) unless instance_variable_defined?("@#{name}")
53
- instance_variable_get("@#{name}")
54
- end
15
+ if base < ActiveRecord::Base
16
+ base.send(:include, EasyModel::ColumnForActiveRecord)
17
+ else
18
+ base.send(:include, EasyModel::ColumnForActiveModel)
55
19
  end
56
20
  end
57
21
 
@@ -0,0 +1,116 @@
1
+ # coding: utf-8
2
+
3
+ require 'active_model'
4
+ require 'active_record'
5
+
6
+ #
7
+ # テーブルに存在しないカラムを定義する機能を提供する.
8
+ #
9
+ # ==== 詳細
10
+ # attr_accessor による属性定義とは異なり, データ型及びデフォルト値を指定することが可能.
11
+ #
12
+ module EasyModel::ColumnForActiveModel
13
+
14
+ def self.included(base)
15
+ base.send(:include, ActiveModel::Model)
16
+ base.send(:include, ActiveModel::Dirty)
17
+ base.send(:include, ActiveModel::Serializers::Xml)
18
+ base.send(:include, ActiveRecord::AttributeAssignment)
19
+ base.extend(EasyModel::ColumnForActiveModel::ClassMethods)
20
+ end
21
+
22
+ #
23
+ # 属性名の配列.
24
+ #
25
+ # ==== 戻り値
26
+ # EasyModel::ColumnForActiveModel::ClassMethods#column で定義した属性名を文字列配列として返す.
27
+ #
28
+ def easy_model_attribute_names
29
+ self.class.easy_model_attribute_names
30
+ end
31
+ alias_method :attribute_names, :easy_model_attribute_names
32
+
33
+ #
34
+ # 属性名と値を保持するハッシュ.
35
+ #
36
+ # ==== 戻り値
37
+ # {属性名 => 値} であるハッシュ.
38
+ #
39
+ def easy_model_attributes
40
+ self.class.easy_model_attribute_names.reduce({}) do |map, name|
41
+ map.tap{map[name] = send(name)}
42
+ end
43
+ end
44
+ alias_method :attributes, :easy_model_attributes
45
+
46
+ #
47
+ # 属性の変更情報をリセットする.
48
+ #
49
+ # ==== 詳細
50
+ # 属性の変更情報をリセットすると, #changed? は false を返すようになる.
51
+ #
52
+ def reset_changes
53
+ @previously_changed = {}
54
+ @changed_attributes = {}
55
+ end
56
+
57
+ end
58
+
59
+ module EasyModel::ColumnForActiveModel::ClassMethods
60
+
61
+ #
62
+ # #column メソッドで定義した属性名.
63
+ #
64
+ # ==== 戻り値
65
+ # #column メソッドで定義した属性名 String の配列として返す.
66
+ #
67
+ def easy_model_attribute_names
68
+ (@easy_model_attribute_names || []).dup
69
+ end
70
+ alias_method :attribute_names, :easy_model_attribute_names
71
+
72
+ #
73
+ # ロケールファイルからルックアップするときのキー.
74
+ #
75
+ def i18n_scope
76
+ :easy_model
77
+ end
78
+
79
+ protected
80
+
81
+ #
82
+ # テーブルに存在しないカラムを定義する.
83
+ #
84
+ # ==== 引数
85
+ # name:: カラム名.
86
+ # type:: データ型.
87
+ # options [:default]:: デフォルト値. 省略した場合は nil.
88
+ #
89
+ # ==== 戻り値
90
+ # 定義したカラムを表す ActiveRecord::ConnectionAdapters::Column オブジェクト.
91
+ #
92
+ def column(name, type, options={})
93
+ define_attribute_method(name)
94
+ (@easy_model_attribute_names ||= []) << name.to_s
95
+
96
+ ActiveRecord::ConnectionAdapters::Column.new(name, options[:default], type, true).tap do |column|
97
+ define_method("#{name}=") do |value|
98
+ value = nil if column.number? and value.kind_of?(String) and value.blank?
99
+ return if value == send(name)
100
+ send("#{name}_will_change!")
101
+ instance_variable_set("@#{name}_before_type_cast", value)
102
+ instance_variable_set("@#{name}", column.type_cast(value))
103
+ end
104
+ define_method("#{name}_before_type_cast") do
105
+ instance_variable_set("@#{name}_before_type_cast", column.default) unless instance_variable_defined?("@#{name}_before_type_cast")
106
+ instance_variable_get("@#{name}_before_type_cast")
107
+ end
108
+ define_method(name) do
109
+ instance_variable_set("@#{name}", column.default) unless instance_variable_defined?("@#{name}")
110
+ instance_variable_get("@#{name}")
111
+ end
112
+ end
113
+ end
114
+
115
+ end
116
+
@@ -0,0 +1,95 @@
1
+ # coding: utf-8
2
+
3
+ require 'active_record'
4
+
5
+ #
6
+ # テーブルに存在しないカラムを定義する機能を提供する.
7
+ #
8
+ # ==== 詳細
9
+ # attr_accessor による属性定義とは異なり, データ型及びデフォルト値を指定することが可能.
10
+ #
11
+ module EasyModel::ColumnForActiveRecord
12
+
13
+ def self.included(base)
14
+ base.extend(EasyModel::ColumnForActiveRecord::ClassMethods)
15
+ end
16
+
17
+ #
18
+ # 属性名の配列.
19
+ #
20
+ # ==== 戻り値
21
+ # EasyModel::ColumnForActiveRecord::ClassMethods#column で定義した属性名を文字列配列として返す.
22
+ #
23
+ def easy_model_attribute_names
24
+ self.class.easy_model_attribute_names
25
+ end
26
+
27
+ #
28
+ # 属性名と値を保持するハッシュ.
29
+ #
30
+ # ==== 戻り値
31
+ # {属性名 => 値} であるハッシュ.
32
+ #
33
+ def easy_model_attributes
34
+ self.class.easy_model_attribute_names.reduce({}) do |map, name|
35
+ map.tap{map[name] = send(name)}
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ module EasyModel::ColumnForActiveRecord::ClassMethods
42
+
43
+ #
44
+ # #column メソッドで定義した属性名.
45
+ #
46
+ # ==== 戻り値
47
+ # #column メソッドで定義した属性名 String の配列として返す.
48
+ #
49
+ def easy_model_attribute_names
50
+ (@easy_model_attribute_names || []).dup
51
+ end
52
+
53
+ #
54
+ # ロケールファイルからルックアップするときのキー.
55
+ #
56
+ def i18n_scope
57
+ :easy_model
58
+ end
59
+
60
+ protected
61
+
62
+ #
63
+ # テーブルに存在しないカラムを定義する.
64
+ #
65
+ # ==== 引数
66
+ # name:: カラム名.
67
+ # type:: データ型.
68
+ # options [:default]:: デフォルト値. 省略した場合は nil.
69
+ #
70
+ # ==== 戻り値
71
+ # 定義したカラムを表す ActiveRecord::ConnectionAdapters::Column オブジェクト.
72
+ #
73
+ def column(name, type, options={})
74
+ (@easy_model_attribute_names ||= []) << name.to_s
75
+
76
+ ActiveRecord::ConnectionAdapters::Column.new(name, options[:default], type, true).tap do |column|
77
+ define_method("#{name}=") do |value|
78
+ value = nil if column.number? and value.kind_of?(String) and value.blank?
79
+ return if value == send(name)
80
+ instance_variable_set("@#{name}_before_type_cast", value)
81
+ instance_variable_set("@#{name}", column.type_cast(value))
82
+ end
83
+ define_method("#{name}_before_type_cast") do
84
+ instance_variable_set("@#{name}_before_type_cast", column.default) unless instance_variable_defined?("@#{name}_before_type_cast")
85
+ instance_variable_get("@#{name}_before_type_cast")
86
+ end
87
+ define_method(name) do
88
+ instance_variable_set("@#{name}", column.default) unless instance_variable_defined?("@#{name}")
89
+ instance_variable_get("@#{name}")
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+
@@ -8,7 +8,9 @@
8
8
  # 派生クラスは ActiveRecord::Relation を返す scoped メソッドを定義しなければならない.
9
9
  # scoped メソッドの戻り値には all や exists? などの処理が delegate される.
10
10
  #
11
- class EasyModel::SearchForm < EasyModel::Base
11
+ class EasyModel::SearchForm
12
+
13
+ include EasyModel::Column
12
14
 
13
15
  # from active_record/querying.rb
14
16
  delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped
@@ -0,0 +1,355 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'helper')
4
+
5
+ class EasyModel::TestColumnForActiveModel < Test::Unit::TestCase
6
+
7
+ def setup
8
+ Time.zone = 'Hawaii'
9
+ I18n.load_path = [File.join(File.dirname(__FILE__), 'ja.yml')]
10
+ I18n.default_locale = :ja
11
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'test.sqlite3')
12
+ ActiveRecord::Base.connection.execute('BEGIN')
13
+ ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS users')
14
+ ActiveRecord::Base.connection.execute('CREATE TABLE users (id INTEGER PRIMARY KEY)')
15
+ end
16
+
17
+ def teardown
18
+ ActiveRecord::Base.connection.execute('ROLLBACK')
19
+ end
20
+
21
+ def new_model
22
+ Class.new do
23
+ include EasyModel::Column
24
+ column :name, :string
25
+ column :email, :string
26
+ column :age, :integer
27
+ def self.name; 'User'; end
28
+ end
29
+ end
30
+
31
+ def test_initialize
32
+ user = new_model.new(:name => 'taro', :email => 'taro@example.com', :age => 20)
33
+ assert_equal 'taro', user.name
34
+ assert_equal 'taro@example.com', user.email
35
+ assert_equal 20, user.age
36
+ end
37
+
38
+ def test_easy_model_attribute_names_class_method
39
+ assert_equal %w(name email age), new_model.easy_model_attribute_names
40
+ end
41
+
42
+ def test_easy_model_attribute_names_instance_method
43
+ assert_equal %w(name email age), new_model.new.easy_model_attribute_names
44
+ end
45
+
46
+ def test_attribute_names_class_method
47
+ assert_equal %w(name email age), new_model.attribute_names
48
+ end
49
+
50
+ def test_attribute_names_instance_method
51
+ assert_equal %w(name email age), new_model.new.attribute_names
52
+ end
53
+
54
+ def test_easy_model_attributes
55
+ attributes = {:name => 'taro', :email => 'taro@example.com', :age => 20}
56
+ assert_equal attributes.stringify_keys, new_model.new(attributes).easy_model_attributes
57
+ end
58
+
59
+ def test_attributes
60
+ attributes = {:name => 'taro', :email => 'taro@example.com', :age => 20}
61
+ assert_equal attributes.stringify_keys, new_model.new(attributes).attributes
62
+ end
63
+
64
+ def test_attributes=
65
+ object = new_model.new
66
+ object.attributes = {:name => 'taro', :email => 'taro@example.com', :age => 20}
67
+ assert_equal 'taro', object.name
68
+ assert_equal 'taro@example.com', object.email
69
+ assert_equal 20, object.age
70
+ end
71
+
72
+ def test_dirty
73
+ object = new_model.new(:name => 'taro', :email => 'taro@example.com', :age => 20)
74
+ assert_equal true, object.changed?
75
+ assert_equal true, object.name_changed?
76
+ assert_equal nil, object.name_was
77
+ assert_equal [nil, 'taro'], object.name_change
78
+ assert_equal true, object.email_changed?
79
+ assert_equal nil, object.email_was
80
+ assert_equal [nil, 'taro@example.com'], object.email_change
81
+ assert_equal true, object.age_changed?
82
+ assert_equal nil, object.age_was
83
+ assert_equal [nil, 20], object.age_change
84
+
85
+ object.reset_changes
86
+ assert_equal false, object.changed?
87
+
88
+ object.age = 21
89
+ assert_equal true, object.changed?
90
+ assert_equal false, object.name_changed?
91
+ assert_equal 'taro', object.name_was
92
+ assert_equal nil, object.name_change
93
+ assert_equal false, object.email_changed?
94
+ assert_equal 'taro@example.com', object.email_was
95
+ assert_equal nil, object.email_change
96
+ assert_equal true, object.age_changed?
97
+ assert_equal 20, object.age_was
98
+ assert_equal [20, 21], object.age_change
99
+ end
100
+
101
+ def test_i18n_scope
102
+ model = new_model
103
+ assert_equal :easy_model, model.i18n_scope
104
+ assert_equal 'ユーザ', model.model_name.human
105
+ assert_equal '名前', model.human_attribute_name(:name)
106
+ assert_equal 'メールアドレス', model.human_attribute_name(:email)
107
+ assert_equal '年齢', model.human_attribute_name(:age)
108
+ end
109
+
110
+ def test_persisted?
111
+ assert_equal false, new_model.new.persisted?
112
+ end
113
+
114
+ def test_has_column_method
115
+ model = new_model
116
+ model.send(:column, :integer_column, :integer)
117
+ assert model.protected_methods.include?(:column)
118
+
119
+ object = model.new
120
+ assert object.respond_to?(:integer_column)
121
+ assert object.respond_to?(:integer_column=)
122
+ assert object.respond_to?(:integer_column_before_type_cast)
123
+ end
124
+
125
+ def test_before_type_cast
126
+ model = new_model
127
+ model.send(:column, :integer_column, :integer)
128
+ object = model.new
129
+
130
+ object.integer_column = ''
131
+ assert_nil object.integer_column
132
+ assert_nil object.integer_column_before_type_cast
133
+
134
+ object.integer_column = '777'
135
+ assert_equal 777, object.integer_column
136
+ assert_equal '777', object.integer_column_before_type_cast
137
+
138
+ object.integer_column = true
139
+ assert_equal 1, object.integer_column
140
+ assert_equal true, object.integer_column_before_type_cast
141
+
142
+ object.integer_column = false
143
+ assert_equal 0, object.integer_column
144
+ assert_equal false, object.integer_column_before_type_cast
145
+ end
146
+
147
+ def test_string_column
148
+ model = new_model
149
+ model.send(:column, :column_with_default, :string, :default => 'default value')
150
+ model.send(:column, :column_without_default, :string)
151
+ object = model.new
152
+
153
+ # 未代入の場合にデフォルト値が取得できること.
154
+ assert_equal 'default value', object.column_with_default
155
+ assert_nil object.column_without_default
156
+
157
+ # 代入した値が取得できること.
158
+ object.column_with_default = 'other value'
159
+ assert_equal 'other value', object.column_with_default
160
+ end
161
+
162
+ def test_text_column
163
+ model = new_model
164
+ model.send(:column, :column_with_default, :text, :default => 'default value')
165
+ model.send(:column, :column_without_default, :text)
166
+ object = model.new
167
+
168
+ # 未代入の場合にデフォルト値が取得できること.
169
+ assert_equal 'default value', object.column_with_default
170
+ assert_nil object.column_without_default
171
+
172
+ # 代入した値が取得できること.
173
+ object.column_with_default = 'other value'
174
+ assert_equal 'other value', object.column_with_default
175
+ end
176
+
177
+ def test_integer_column
178
+ model = new_model
179
+ model.send(:column, :column_with_default, :integer, :default => 777)
180
+ model.send(:column, :column_without_default, :integer)
181
+ object = model.new
182
+
183
+ # 未代入の場合にデフォルト値が取得できること.
184
+ assert_nil object.column_without_default
185
+ assert_equal 777, object.column_with_default
186
+
187
+ # 代入した値が取得できること.
188
+ object.column_with_default = 123
189
+ assert_equal 123, object.column_with_default
190
+
191
+ # 値の変換が行われること.
192
+ object.column_with_default = '789'
193
+ assert_equal 789, object.column_with_default
194
+ end
195
+
196
+ def test_float_column
197
+ model = new_model
198
+ model.send(:column, :column_with_default, :float, :default => 1.5)
199
+ model.send(:column, :column_without_default, :float)
200
+ object = model.new
201
+
202
+ # 未代入の場合にデフォルト値が取得できること.
203
+ assert_nil object.column_without_default
204
+ assert_equal 1.5, object.column_with_default
205
+
206
+ # 代入した値が取得できること.
207
+ object.column_with_default = 2.5
208
+ assert_equal 2.5, object.column_with_default
209
+
210
+ # 値の変換が行われること.
211
+ object.column_with_default = '3.5'
212
+ assert_equal 3.5, object.column_with_default
213
+ object.column_with_default = 3
214
+ assert_equal 3.0, object.column_with_default
215
+ end
216
+
217
+ def test_decimal_column
218
+ model = new_model
219
+ model.send(:column, :column_with_default, :decimal, :default => 1.5)
220
+ model.send(:column, :column_without_default, :decimal)
221
+ object = model.new
222
+
223
+ # 未代入の場合にデフォルト値が取得できること.
224
+ assert_nil object.column_without_default
225
+ assert_equal 1.5, object.column_with_default
226
+
227
+ # 代入した値が取得できること.
228
+ object.column_with_default = 2.5
229
+ assert_equal 2.5, object.column_with_default
230
+
231
+ # 値の変換が行われること.
232
+ object.column_with_default = '3.5'
233
+ assert_equal 3.5, object.column_with_default
234
+ object.column_with_default = 3
235
+ assert_equal 3.0, object.column_with_default
236
+ end
237
+
238
+ def test_datetime_column
239
+ model = new_model
240
+ model.send(:column, :column_with_default, :datetime, :default => Time.zone.parse('2000-01-01 10:20:30'))
241
+ model.send(:column, :column_without_default, :datetime)
242
+ object = model.new
243
+
244
+ # 未代入の場合にデフォルト値が取得できること.
245
+ assert_nil object.column_without_default
246
+ assert_equal Time.zone.parse('2000-01-01 10:20:30'), object.column_with_default
247
+
248
+ # 代入した値が取得できること.
249
+ object.column_with_default = Time.zone.parse('2000-02-01 10:20:30')
250
+ assert_equal Time.zone.parse('2000-02-01 10:20:30'), object.column_with_default
251
+
252
+ # 値の変換が行われること.
253
+ object.column_with_default = Time.zone.parse('2000-03-01 10:20:30')
254
+ assert_equal Time.zone.parse('2000-03-01 10:20:30'), object.column_with_default
255
+ end
256
+
257
+ def test_timestamp_column
258
+ model = new_model
259
+ model.send(:column, :column_with_default, :timestamp, :default => Time.zone.parse('2000-01-01 10:20:30'))
260
+ model.send(:column, :column_without_default, :timestamp)
261
+ object = model.new
262
+
263
+ # 未代入の場合にデフォルト値が取得できること.
264
+ assert_nil object.column_without_default
265
+ assert_equal Time.zone.parse('2000-01-01 10:20:30'), object.column_with_default
266
+
267
+ # 代入した値が取得できること.
268
+ object.column_with_default = Time.zone.parse('2000-02-01 10:20:30')
269
+ assert_equal Time.zone.parse('2000-02-01 10:20:30'), object.column_with_default
270
+
271
+ # 値の変換が行われること.
272
+ object.column_with_default = Time.zone.parse('2000-03-01 10:20:30')
273
+ assert_equal Time.zone.parse('2000-03-01 10:20:30'), object.column_with_default
274
+ end
275
+
276
+ def test_time_column
277
+ model = new_model
278
+ model.send(:column, :column_with_default, :time, :default => Time.zone.parse('2000-01-01 10:20:30'))
279
+ model.send(:column, :column_without_default, :time)
280
+ object = model.new
281
+
282
+ # 未代入の場合にデフォルト値が取得できること.
283
+ assert_nil object.column_without_default
284
+ assert_equal 10, object.column_with_default.hour
285
+ assert_equal 20, object.column_with_default.min
286
+ assert_equal 30, object.column_with_default.sec
287
+
288
+ # 代入した値が取得できること.
289
+ object.column_with_default = Time.zone.parse('2000-02-01 20:30:40')
290
+ assert_equal 20, object.column_with_default.hour
291
+ assert_equal 30, object.column_with_default.min
292
+ assert_equal 40, object.column_with_default.sec
293
+ end
294
+
295
+ def test_date_column
296
+ model = new_model
297
+ model.send(:column, :column_with_default, :date, :default => Date.parse('2000-01-01'))
298
+ model.send(:column, :column_without_default, :date)
299
+ object = model.new
300
+
301
+ # 未代入の場合にデフォルト値が取得できること.
302
+ assert_nil object.column_without_default
303
+ assert_equal Date.parse('2000-01-01'), object.column_with_default
304
+
305
+ # 代入した値が取得できること.
306
+ object.column_with_default = Date.parse('2000-02-01')
307
+ assert_equal Date.parse('2000-02-01'), object.column_with_default
308
+
309
+ # 値の変換が行われること.
310
+ object.column_with_default = '2000-03-01'
311
+ assert_equal Date.parse('2000-03-01'), object.column_with_default
312
+ end
313
+
314
+ def test_binary_column
315
+ model = new_model
316
+ model.send(:column, :column_with_default, :binary, :default => 'default value')
317
+ model.send(:column, :column_without_default, :binary)
318
+ object = model.new
319
+
320
+ # 未代入の場合にデフォルト値が取得できること.
321
+ assert_equal 'default value', object.column_with_default
322
+ assert_nil object.column_without_default
323
+
324
+ # 代入した値が取得できること.
325
+ object.column_with_default = 'other value'
326
+ assert_equal 'other value', object.column_with_default
327
+ end
328
+
329
+ def test_boolean_column
330
+ model = new_model
331
+ model.send(:column, :column_with_default, :boolean, :default => true)
332
+ model.send(:column, :column_without_default, :boolean)
333
+ object = model.new
334
+
335
+ # 未代入の場合にデフォルト値が取得できること.
336
+ assert_nil object.column_without_default
337
+ assert_equal true, object.column_with_default
338
+
339
+ # 代入した値が取得できること.
340
+ object.column_with_default = false
341
+ assert_equal false, object.column_with_default
342
+
343
+ # 値の変換が行われること.
344
+ object.column_with_default = 'true'
345
+ assert_equal true, object.column_with_default
346
+ object.column_with_default = 'false'
347
+ assert_equal false, object.column_with_default
348
+ object.column_with_default = '1'
349
+ assert_equal true, object.column_with_default
350
+ object.column_with_default = '0'
351
+ assert_equal false, object.column_with_default
352
+ end
353
+
354
+ end
355
+