easy_model 1.0.5 → 2.0.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.
@@ -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
+