akm-selectable_attr_rails 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +4 -0
- data/lib/selectable_attr_rails.rb +7 -0
- data/lib/selectable_attr_rails/db_loadable.rb +65 -0
- data/lib/selectable_attr_rails/helpers.rb +7 -0
- data/lib/selectable_attr_rails/helpers/abstract_selection_helper.rb +55 -0
- data/lib/selectable_attr_rails/helpers/check_box_group_helper.rb +63 -0
- data/lib/selectable_attr_rails/helpers/radio_button_group_helper.rb +55 -0
- data/lib/selectable_attr_rails/helpers/select_helper.rb +72 -0
- data/lib/selectable_attr_rails/version.rb +3 -0
- data/spec/database.yml +6 -0
- data/spec/introduction_spec.rb +379 -0
- data/spec/schema.rb +30 -0
- data/spec/selectable_attr_i18n_test.rb +276 -0
- data/spec/spec_helper.rb +30 -0
- metadata +71 -0
data/VERSION.yml
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module SelectableAttrRails
|
2
|
+
module DbLoadable
|
3
|
+
def update_by(*args, &block)
|
4
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
5
|
+
options = {:when => :first_time}.update(options)
|
6
|
+
@sql_to_update = block_given? ? block : args.first
|
7
|
+
@update_timing = options[:when]
|
8
|
+
self.extend(InstanceMethods) unless respond_to?(:update_entries)
|
9
|
+
end
|
10
|
+
|
11
|
+
module Entry
|
12
|
+
attr_accessor :name_from_db
|
13
|
+
def name
|
14
|
+
@name_from_db || super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
def entries
|
20
|
+
update_entries if must_be_updated?
|
21
|
+
@entries
|
22
|
+
end
|
23
|
+
|
24
|
+
def must_be_updated?
|
25
|
+
return false if @update_timing == :never
|
26
|
+
return true if @update_timing == :everytime
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_entries
|
30
|
+
unless @original_entries
|
31
|
+
@original_entries = @entries.dup
|
32
|
+
@original_entries.each do |entry|
|
33
|
+
entry.extend(SelectableAttrRails::DbLoadable::Entry)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
records = nil
|
37
|
+
if @sql_to_update.respond_to?(:call)
|
38
|
+
records = @sql_to_update.call
|
39
|
+
else
|
40
|
+
@connection ||= ActiveRecord::Base.connection
|
41
|
+
records = @connection.select_rows(@sql_to_update)
|
42
|
+
end
|
43
|
+
|
44
|
+
new_entries = []
|
45
|
+
records.each do |r|
|
46
|
+
if entry = @original_entries.detect{|entry| entry.id == r.first}
|
47
|
+
entry.name_from_db = r.last unless r.last.blank?
|
48
|
+
new_entries << entry
|
49
|
+
else
|
50
|
+
entry = SelectableAttr::Enum::Entry.new(self, r.first, "entry_#{r.first}".to_sym, r.last)
|
51
|
+
new_entries << entry
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@original_entries.each do |entry|
|
55
|
+
unless new_entries.include?(entry)
|
56
|
+
entry.name_from_db = nil
|
57
|
+
new_entries << entry if entry.defined_in_code
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@entries = new_entries
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module SelectableAttrRails
|
2
|
+
module Helpers
|
3
|
+
autoload :SelectHelper, 'selectable_attr_rails/helpers/select_helper'
|
4
|
+
autoload :CheckBoxGroupHelper, 'selectable_attr_rails/helpers/check_box_group_helper'
|
5
|
+
autoload :RadioButtonGroupHelper, 'selectable_attr_rails/helpers/radio_button_group_helper'
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SelectableAttrRails::Helpers
|
2
|
+
class AbstractSelectionBuilder
|
3
|
+
attr_reader :entry_hash
|
4
|
+
|
5
|
+
def initialize(object, object_name, method, options, template)
|
6
|
+
@object, @object_name, @method = object, object_name, method
|
7
|
+
@base_name = @object.class.enum_base_name(method.to_s)
|
8
|
+
@template = template
|
9
|
+
@entry_hash = nil
|
10
|
+
@options = options || {}
|
11
|
+
@entry_hash_array = @options[:entry_hash_array]
|
12
|
+
end
|
13
|
+
|
14
|
+
def enum_hash_array_from_object
|
15
|
+
base_name = @object.class.enum_base_name(@method.to_s)
|
16
|
+
@object.send("#{base_name}_hash_array")
|
17
|
+
end
|
18
|
+
|
19
|
+
def enum_hash_array_from_class
|
20
|
+
base_name = @object.class.enum_base_name(@method.to_s)
|
21
|
+
@object.class.send("#{base_name}_hash_array")
|
22
|
+
end
|
23
|
+
|
24
|
+
def tag_id(tag)
|
25
|
+
result = nil
|
26
|
+
tag.scan(/ id\=\"(.*?)\"/){|s|result = s}
|
27
|
+
return result
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_class_name(options, class_name)
|
31
|
+
(options ||= {}).stringify_keys!
|
32
|
+
(options['class'] ||= '') << ' ' << class_name
|
33
|
+
options
|
34
|
+
end
|
35
|
+
|
36
|
+
def camelize_keys(hash, first_letter = :lower)
|
37
|
+
result = {}
|
38
|
+
hash.each{|key, value|result[key.to_s.camelize(first_letter)] = value}
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_options(dest, *options_array)
|
43
|
+
result = dest || {}
|
44
|
+
options_array.each do |options|
|
45
|
+
next unless options
|
46
|
+
if class_name = options.delete(:class)
|
47
|
+
add_class_name(result, class_name)
|
48
|
+
end
|
49
|
+
result.update(options)
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'selectable_attr_rails/helpers/abstract_selection_helper'
|
2
|
+
module SelectableAttrRails::Helpers
|
3
|
+
module CheckBoxGroupHelper
|
4
|
+
class Builder < SelectableAttrRails::Helpers::AbstractSelectionBuilder
|
5
|
+
|
6
|
+
def initialize(object, object_name, method, options, template)
|
7
|
+
super(object, object_name, method, options, template)
|
8
|
+
@entry_hash_array ||= enum_hash_array_from_object
|
9
|
+
@param_name = "#{@base_name}_ids"
|
10
|
+
@check_box_options = @options.delete(:check_box) || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
@entry_hash_array.each do |@entry_hash|
|
15
|
+
@tag_value = @entry_hash[:id].to_s.gsub(/\s/, "_").gsub(/\W/, "")
|
16
|
+
@check_box_id = "#{@object_name}_#{@param_name}_#{@tag_value}"
|
17
|
+
yield(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_box(options = nil)
|
22
|
+
options = update_options({
|
23
|
+
:id => @check_box_id, :type => 'checkbox', :value => @tag_value,
|
24
|
+
:name => "#{@object_name}[#{@param_name}][]"
|
25
|
+
}, @check_box_options, options)
|
26
|
+
options[:checked] = 'checked' if @entry_hash[:select]
|
27
|
+
@template.content_tag("input", nil, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def label(text = nil, options = nil)
|
31
|
+
@template.content_tag("label", text || @entry_hash[:name],
|
32
|
+
update_options({:for => @check_box_id}, options))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Base
|
37
|
+
def check_box_group(object_name, method, options = nil, &block)
|
38
|
+
object = (options || {})[:object] || instance_variable_get("@#{object_name}")
|
39
|
+
builder = Builder.new(object, object_name, method, options, @template)
|
40
|
+
if block_given?
|
41
|
+
yield(builder)
|
42
|
+
return nil
|
43
|
+
else
|
44
|
+
result = ''
|
45
|
+
builder.each do
|
46
|
+
result << builder.check_box
|
47
|
+
result << ' '
|
48
|
+
result << builder.label
|
49
|
+
result << ' '
|
50
|
+
end
|
51
|
+
return result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module FormBuilder
|
57
|
+
def check_box_group(method, options = nil, &block)
|
58
|
+
@template.check_box_group(@object_name, method,
|
59
|
+
(options || {}).merge(:object => @object), &block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SelectableAttrRails::Helpers
|
2
|
+
module RadioButtonGroupHelper
|
3
|
+
class Builder < SelectableAttrRails::Helpers::AbstractSelectionBuilder
|
4
|
+
|
5
|
+
def initialize(object, object_name, method, options, template)
|
6
|
+
super(object, object_name, method, options, template)
|
7
|
+
@entry_hash_array ||= enum_hash_array_from_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
@entry_hash_array.each do |entry_hash|
|
12
|
+
@entry_hash = entry_hash
|
13
|
+
tag_value = @entry_hash[:id].to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
|
14
|
+
@radio_button_id = "#{@object_name}_#{@method}_#{tag_value}"
|
15
|
+
yield(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def radio_button(options = nil)
|
20
|
+
@template.radio_button(@object_name, @method, @entry_hash[:id],
|
21
|
+
update_options({:id => @radio_button_id}, options))
|
22
|
+
end
|
23
|
+
|
24
|
+
def label(text = nil, options = nil)
|
25
|
+
@template.content_tag("label", text || @entry_hash[:name],
|
26
|
+
update_options({:for => @radio_button_id}, options))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Base
|
31
|
+
def radio_button_group(object_name, method, options = nil, &block)
|
32
|
+
object = (options || {})[:object] || instance_variable_get("@#{object_name}")
|
33
|
+
builder = Builder.new(object, object_name, method, options, self)
|
34
|
+
if block_given?
|
35
|
+
yield(builder)
|
36
|
+
return nil
|
37
|
+
else
|
38
|
+
result = ''
|
39
|
+
builder.each do
|
40
|
+
result << builder.radio_button
|
41
|
+
result << builder.label
|
42
|
+
end
|
43
|
+
return result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module FormBuilder
|
49
|
+
def radio_button_group(method, options = nil, &block)
|
50
|
+
@template.radio_button_group(@object_name, method,
|
51
|
+
(options || {}).merge(:object => @object), &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module SelectableAttrRails::Helpers
|
2
|
+
module SelectHelper
|
3
|
+
module Base
|
4
|
+
def self.included(base)
|
5
|
+
base.module_eval do
|
6
|
+
alias_method_chain :select, :attr_enumeable
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# def select_with_attr_enumeable(object, method, choices, options = {}, html_options = {})
|
11
|
+
def select_with_attr_enumeable(object_name, method, *args, &block)
|
12
|
+
if args.length > 3
|
13
|
+
raise ArgumentError, "argument must be " <<
|
14
|
+
"(object, method, choices, options = {}, html_options = {}) or " <<
|
15
|
+
"(object, method, options = {}, html_options = {})"
|
16
|
+
end
|
17
|
+
return select_without_attr_enumeable(object_name, method, *args, &block) if args.length == 3
|
18
|
+
return select_without_attr_enumeable(object_name, method, *args, &block) if args.first.is_a?(Array)
|
19
|
+
options, html_options = *args
|
20
|
+
options = update_enum_select_options(options, object_name, method)
|
21
|
+
object, base_name = options[:object], options[:base_name]
|
22
|
+
return multi_enum_select(object_name, method, options, html_options, &block) if object.respond_to?("#{base_name}_hash_array")
|
23
|
+
return single_enum_select(object_name, method, options, html_options, &block) if object.class.respond_to?("#{base_name}_hash_array")
|
24
|
+
raise ArgumentError, "invaliad argument"
|
25
|
+
end
|
26
|
+
|
27
|
+
def single_enum_select(object_name, method, options = {}, html_options = {}, &block)
|
28
|
+
options = update_enum_select_options(options, object_name, method)
|
29
|
+
object = options.delete(:object)
|
30
|
+
base_name = options.delete(:base_name)
|
31
|
+
entry_hash_array = options.delete(:entry_hash_array) || object.class.send("#{base_name}_hash_array")
|
32
|
+
container = entry_hash_array.map{|hash| [hash[:name].to_s, hash[:id]]}
|
33
|
+
select_without_attr_enumeable(object_name, method, container, options, html_options || {}, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def multi_enum_select(object_name, method, options = {}, html_options = {}, &block)
|
37
|
+
html_options = {:size => 5, :multiple => 'multiple'}.update(html_options || {})
|
38
|
+
options = update_enum_select_options(options, object_name, method)
|
39
|
+
object = options.delete(:object)
|
40
|
+
base_name = options.delete(:base_name)
|
41
|
+
entry_hash_array = options.delete(:entry_hash_array) || object.send("#{base_name}_hash_array")
|
42
|
+
container = entry_hash_array.map{|hash| [hash[:name].to_s, hash[:id].to_s]}
|
43
|
+
attr = "#{base_name}_ids"
|
44
|
+
select_without_attr_enumeable(object_name, attr, container, options, html_options, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_enum_select_options(options, object_name, method)
|
48
|
+
options ||= {}
|
49
|
+
object = (options[:object] ||= instance_variable_get("@#{object_name}"))
|
50
|
+
options[:base_name] ||= object.class.enum_base_name(method.to_s)
|
51
|
+
options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module FormBuilder
|
56
|
+
def self.included(base)
|
57
|
+
base.module_eval do
|
58
|
+
alias_method_chain :select, :attr_enumeable
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# def select_with_attr_enumeable(method, choices, options = {}, html_options = {}, &block)
|
63
|
+
def select_with_attr_enumeable(method, *args, &block)
|
64
|
+
options = args.first.is_a?(Array) ? (args[1] ||= {}) : (args[0] ||= {})
|
65
|
+
object = (options || {}).delete(:object) || @object || instance_variable_get(@object_name) rescue nil
|
66
|
+
options.update({:object => object})
|
67
|
+
options[:selected] = object.send(method) if object
|
68
|
+
@template.select(@object_name, method, *args, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
describe SelectableAttr do
|
5
|
+
|
6
|
+
def assert_product_discount(klass)
|
7
|
+
# productsテーブルのデータから安売り用の価格は
|
8
|
+
# product_type_cd毎に決められた割合をpriceにかけて求めます。
|
9
|
+
p1 = klass.new(:name => '実践Rails', :product_type_cd => '01', :price => 3000)
|
10
|
+
p1.discount_price.should == 2400
|
11
|
+
p2 = klass.new(:name => '薔薇の名前', :product_type_cd => '02', :price => 1500)
|
12
|
+
p2.discount_price.should == 300
|
13
|
+
p3 = klass.new(:name => '未来派野郎', :product_type_cd => '03', :price => 3000)
|
14
|
+
p3.discount_price.should == 1500
|
15
|
+
end
|
16
|
+
|
17
|
+
# 定数をガンガン定義した場合
|
18
|
+
# 大文字が多くて読みにくいし、関連するデータ(ここではDISCOUNT)が増える毎に定数も増えていきます。
|
19
|
+
class LegacyProduct1 < ActiveRecord::Base
|
20
|
+
set_table_name 'products'
|
21
|
+
|
22
|
+
PRODUCT_TYPE_BOOK = '01'
|
23
|
+
PRODUCT_TYPE_DVD = '02'
|
24
|
+
PRODUCT_TYPE_CD = '03'
|
25
|
+
PRODUCT_TYPE_OTHER = '09'
|
26
|
+
|
27
|
+
PRODUCT_TYPE_OPTIONS = [
|
28
|
+
['書籍', PRODUCT_TYPE_BOOK],
|
29
|
+
['DVD', PRODUCT_TYPE_DVD],
|
30
|
+
['CD', PRODUCT_TYPE_CD],
|
31
|
+
['その他', PRODUCT_TYPE_OTHER]
|
32
|
+
]
|
33
|
+
|
34
|
+
DISCOUNT = {
|
35
|
+
PRODUCT_TYPE_BOOK => 0.8,
|
36
|
+
PRODUCT_TYPE_DVD => 0.2,
|
37
|
+
PRODUCT_TYPE_CD => 0.5,
|
38
|
+
PRODUCT_TYPE_OTHER => 1
|
39
|
+
}
|
40
|
+
|
41
|
+
def discount_price
|
42
|
+
(DISCOUNT[product_type_cd] * price).to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "test_legacy_product" do
|
47
|
+
assert_product_discount(LegacyProduct1)
|
48
|
+
|
49
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
50
|
+
LegacyProduct1::PRODUCT_TYPE_OPTIONS.should ==
|
51
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# できるだけ定数定義をまとめた場合
|
58
|
+
# 結構すっきりするけど、同じことをいろんなモデルで書くかと思うと気が重い。
|
59
|
+
class LegacyProduct2 < ActiveRecord::Base
|
60
|
+
set_table_name 'products'
|
61
|
+
|
62
|
+
PRODUCT_TYPE_DEFS = [
|
63
|
+
{:id => '01', :name => '書籍', :discount => 0.8},
|
64
|
+
{:id => '02', :name => 'DVD', :discount => 0.2},
|
65
|
+
{:id => '03', :name => 'CD', :discount => 0.5},
|
66
|
+
{:id => '09', :name => 'その他', :discount => 1}
|
67
|
+
]
|
68
|
+
|
69
|
+
PRODUCT_TYPE_OPTIONS = PRODUCT_TYPE_DEFS.map{|t| [t[:name], t[:id]]}
|
70
|
+
DISCOUNT = PRODUCT_TYPE_DEFS.inject({}){|dest, t|
|
71
|
+
dest[t[:id]] = t[:discount]; dest}
|
72
|
+
|
73
|
+
def discount_price
|
74
|
+
(DISCOUNT[product_type_cd] * price).to_i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "test_legacy_product" do
|
79
|
+
assert_product_discount(LegacyProduct2)
|
80
|
+
|
81
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
82
|
+
LegacyProduct2::PRODUCT_TYPE_OPTIONS.should ==
|
83
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
84
|
+
end
|
85
|
+
|
86
|
+
# selectable_attrを使った場合
|
87
|
+
# 定義は一カ所にまとめられて、任意の属性(ここでは:discount)も一緒に書くことができてすっきり〜
|
88
|
+
class Product1 < ActiveRecord::Base
|
89
|
+
set_table_name 'products'
|
90
|
+
|
91
|
+
selectable_attr :product_type_cd do
|
92
|
+
entry '01', :book, '書籍', :discount => 0.8
|
93
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
94
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
95
|
+
entry '09', :other, 'その他', :discount => 1
|
96
|
+
end
|
97
|
+
|
98
|
+
def discount_price
|
99
|
+
(product_type_entry[:discount] * price).to_i
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "test_product1" do
|
104
|
+
assert_product_discount(Product1)
|
105
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
106
|
+
Product1.product_type_options.should ==
|
107
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# selectable_attrが定義するインスタンスメソッドの詳細
|
112
|
+
it "test_product_type_instance_methods" do
|
113
|
+
p1 = Product1.new
|
114
|
+
p1.product_type_cd.should be_nil
|
115
|
+
p1.product_type_key.should be_nil
|
116
|
+
p1.product_type_name.should be_nil
|
117
|
+
# idを変更すると得られるキーも名称も変わります
|
118
|
+
p1.product_type_cd = '02'
|
119
|
+
p1.product_type_cd.should == '02'
|
120
|
+
p1.product_type_key.should == :dvd
|
121
|
+
p1.product_type_name.should == 'DVD'
|
122
|
+
# キーを変更すると得られるidも名称も変わります
|
123
|
+
p1.product_type_key = :book
|
124
|
+
p1.product_type_cd.should == '01'
|
125
|
+
p1.product_type_key.should == :book
|
126
|
+
p1.product_type_name.should == '書籍'
|
127
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
128
|
+
p1.product_type_key = :cd
|
129
|
+
p1.product_type_entry[:discount].should == 0.5
|
130
|
+
end
|
131
|
+
|
132
|
+
# selectable_attrが定義するクラスメソッドの詳細
|
133
|
+
it "test_product_type_class_methods" do
|
134
|
+
# キーからid、名称を取得できます
|
135
|
+
Product1.product_type_id_by_key(:book).should == '01'
|
136
|
+
Product1.product_type_id_by_key(:dvd).should == '02'
|
137
|
+
Product1.product_type_id_by_key(:cd).should == '03'
|
138
|
+
Product1.product_type_id_by_key(:other).should == '09'
|
139
|
+
Product1.product_type_name_by_key(:book).should == '書籍'
|
140
|
+
Product1.product_type_name_by_key(:dvd).should == 'DVD'
|
141
|
+
Product1.product_type_name_by_key(:cd).should == 'CD'
|
142
|
+
Product1.product_type_name_by_key(:other).should == 'その他'
|
143
|
+
# 存在しないキーの場合はnilを返します
|
144
|
+
Product1.product_type_id_by_key(nil).should be_nil
|
145
|
+
Product1.product_type_name_by_key(nil).should be_nil
|
146
|
+
Product1.product_type_id_by_key(:unexist).should be_nil
|
147
|
+
Product1.product_type_name_by_key(:unexist).should be_nil
|
148
|
+
|
149
|
+
# idからキー、名称を取得できます
|
150
|
+
Product1.product_type_key_by_id('01').should == :book
|
151
|
+
Product1.product_type_key_by_id('02').should == :dvd
|
152
|
+
Product1.product_type_key_by_id('03').should == :cd
|
153
|
+
Product1.product_type_key_by_id('09').should == :other
|
154
|
+
Product1.product_type_name_by_id('01').should == '書籍'
|
155
|
+
Product1.product_type_name_by_id('02').should == 'DVD'
|
156
|
+
Product1.product_type_name_by_id('03').should == 'CD'
|
157
|
+
Product1.product_type_name_by_id('09').should == 'その他'
|
158
|
+
# 存在しないidの場合はnilを返します
|
159
|
+
Product1.product_type_key_by_id(nil).should be_nil
|
160
|
+
Product1.product_type_name_by_id(nil).should be_nil
|
161
|
+
Product1.product_type_key_by_id('99').should be_nil
|
162
|
+
Product1.product_type_name_by_id('99').should be_nil
|
163
|
+
|
164
|
+
# id、キー、名称の配列を取得できます
|
165
|
+
Product1.product_type_ids.should == ['01', '02', '03', '09']
|
166
|
+
Product1.product_type_keys.should == [:book, :dvd, :cd, :other]
|
167
|
+
Product1.product_type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
168
|
+
# 一部のものだけ取得することも可能です。
|
169
|
+
Product1.product_type_ids(:cd, :dvd).should == ['03', '02' ]
|
170
|
+
Product1.product_type_keys('02', '03').should == [:dvd, :cd ]
|
171
|
+
Product1.product_type_names('02', '03').should == ['DVD', 'CD']
|
172
|
+
Product1.product_type_names(:cd, :dvd).should == ['CD', 'DVD']
|
173
|
+
|
174
|
+
# select_tagなどのoption_tagsを作るための配列なんか一発っす
|
175
|
+
Product1.product_type_options.should ==
|
176
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
177
|
+
end
|
178
|
+
|
179
|
+
# selectable_attrのエントリ名をDB上に保持するためのモデル
|
180
|
+
class ItemMaster < ActiveRecord::Base
|
181
|
+
end
|
182
|
+
|
183
|
+
# selectable_attrを使った場合その2
|
184
|
+
# アクセス時に毎回アクセス時にDBから項目名を取得します。
|
185
|
+
class ProductWithDB1 < ActiveRecord::Base
|
186
|
+
set_table_name 'products'
|
187
|
+
|
188
|
+
selectable_attr :product_type_cd do
|
189
|
+
update_by(
|
190
|
+
"select item_cd, name from item_masters where category_name = 'product_type_cd' order by item_no",
|
191
|
+
:when => :everytime)
|
192
|
+
entry '01', :book, '書籍', :discount => 0.8
|
193
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
194
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
195
|
+
entry '09', :other, 'その他', :discount => 1
|
196
|
+
end
|
197
|
+
|
198
|
+
def discount_price
|
199
|
+
(product_type_entry[:discount] * price).to_i
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it "test_update_entry_name" do
|
204
|
+
# DBに全くデータがなくてもコードで記述してあるエントリは存在します。
|
205
|
+
ItemMaster.delete_all("category_name = 'product_type_cd'")
|
206
|
+
ProductWithDB1.product_type_entries.length.should == 4
|
207
|
+
ProductWithDB1.product_type_name_by_key(:book).should == '書籍'
|
208
|
+
ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
|
209
|
+
ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
|
210
|
+
ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
|
211
|
+
|
212
|
+
assert_product_discount(ProductWithDB1)
|
213
|
+
|
214
|
+
# DBからエントリの名称を動的に変更できます
|
215
|
+
item_book = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '01', :name => '本')
|
216
|
+
ProductWithDB1.product_type_entries.length.should == 4
|
217
|
+
ProductWithDB1.product_type_name_by_key(:book).should == '本'
|
218
|
+
ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
|
219
|
+
ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
|
220
|
+
ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
|
221
|
+
ProductWithDB1.product_type_options.should ==
|
222
|
+
[['本', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
223
|
+
|
224
|
+
# DBからエントリの並び順を動的に変更できます
|
225
|
+
item_book.item_no = 4;
|
226
|
+
item_book.save!
|
227
|
+
item_other = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'その他')
|
228
|
+
item_dvd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 2, :item_cd => '02') # nameは指定しなかったらデフォルトが使われます。
|
229
|
+
item_cd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 3, :item_cd => '03') # nameは指定しなかったらデフォルトが使われます。
|
230
|
+
ProductWithDB1.product_type_options.should ==
|
231
|
+
[['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01']]
|
232
|
+
|
233
|
+
# DBからエントリを動的に追加することも可能です。
|
234
|
+
item_toys = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'おもちゃ')
|
235
|
+
ProductWithDB1.product_type_options.should ==
|
236
|
+
[['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']]
|
237
|
+
ProductWithDB1.product_type_key_by_id('04').should == :entry_04
|
238
|
+
|
239
|
+
# DBからレコードを削除してもコードで定義したentryは削除されません。
|
240
|
+
# 順番はDBからの取得順で並び替えられたものの後になります
|
241
|
+
item_dvd.destroy
|
242
|
+
ProductWithDB1.product_type_options.should ==
|
243
|
+
[['その他', '09'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04'], ['DVD', '02']]
|
244
|
+
|
245
|
+
# DB上で追加したレコードを削除すると、エントリも削除されます
|
246
|
+
item_toys.destroy
|
247
|
+
ProductWithDB1.product_type_options.should ==
|
248
|
+
[['その他', '09'], ['CD', '03'], ['本', '01'], ['DVD', '02']]
|
249
|
+
|
250
|
+
# 名称を指定していたDBのレコードを削除したら元に戻ります。
|
251
|
+
item_book.destroy
|
252
|
+
ProductWithDB1.product_type_options.should ==
|
253
|
+
[['その他', '09'], ['CD', '03'], ['書籍', '01'], ['DVD', '02']]
|
254
|
+
|
255
|
+
# エントリに該当するレコードを全部削除したら、元に戻ります。
|
256
|
+
ItemMaster.delete_all("category_name = 'product_type_cd'")
|
257
|
+
ProductWithDB1.product_type_options.should ==
|
258
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
259
|
+
|
260
|
+
assert_product_discount(ProductWithDB1)
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
# Q: product_type_cd の'_cd'はどこにいっちゃったの?
|
267
|
+
# A: デフォルトでは、/(_cd$|_code$|_cds$|_codes$)/ を削除したものをbase_nameとして
|
268
|
+
# 扱い、それに_keyなどを付加してメソッド名を定義します。もしこのルールを変更したい場合、
|
269
|
+
# selectable_attrを使う前に selectable_attr_name_pattern で新たなルールを指定してください。
|
270
|
+
class Product2 < ActiveRecord::Base
|
271
|
+
set_table_name 'products'
|
272
|
+
self.selectable_attr_name_pattern = /^product_|_cd$/
|
273
|
+
|
274
|
+
selectable_attr :product_type_cd do
|
275
|
+
entry '01', :book, '書籍', :discount => 0.8
|
276
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
277
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
278
|
+
entry '09', :other, 'その他', :discount => 1
|
279
|
+
end
|
280
|
+
|
281
|
+
def discount_price
|
282
|
+
(type_entry[:discount] * price).to_i
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
it "test_product2" do
|
287
|
+
assert_product_discount(Product2)
|
288
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
289
|
+
Product2.type_options.should ==
|
290
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
291
|
+
|
292
|
+
p2 = Product2.new
|
293
|
+
p2.product_type_cd.should be_nil
|
294
|
+
p2.type_key.should be_nil
|
295
|
+
p2.type_name.should be_nil
|
296
|
+
# idを変更すると得られるキーも名称も変わります
|
297
|
+
p2.product_type_cd = '02'
|
298
|
+
p2.product_type_cd.should == '02'
|
299
|
+
p2.type_key.should == :dvd
|
300
|
+
p2.type_name.should == 'DVD'
|
301
|
+
# キーを変更すると得られるidも名称も変わります
|
302
|
+
p2.type_key = :book
|
303
|
+
p2.product_type_cd.should == '01'
|
304
|
+
p2.type_key.should == :book
|
305
|
+
p2.type_name.should == '書籍'
|
306
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
307
|
+
p2.type_key = :cd
|
308
|
+
p2.type_entry[:discount].should == 0.5
|
309
|
+
|
310
|
+
Product2.type_id_by_key(:book).should == '01'
|
311
|
+
Product2.type_id_by_key(:dvd).should == '02'
|
312
|
+
Product2.type_name_by_key(:cd).should == 'CD'
|
313
|
+
Product2.type_name_by_key(:other).should == 'その他'
|
314
|
+
Product2.type_key_by_id('09').should == :other
|
315
|
+
Product2.type_name_by_id('01').should == '書籍'
|
316
|
+
Product2.type_keys.should == [:book, :dvd, :cd, :other]
|
317
|
+
Product2.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
318
|
+
Product2.type_keys('02', '03').should == [:dvd, :cd]
|
319
|
+
Product2.type_names(:cd, :dvd).should == ['CD', 'DVD']
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
|
324
|
+
|
325
|
+
# Q: selectable_attrの呼び出し毎にbase_bname(って言うの?)を指定したいんだけど。
|
326
|
+
# A: base_nameオプションを指定してください。
|
327
|
+
class Product3 < ActiveRecord::Base
|
328
|
+
set_table_name 'products'
|
329
|
+
|
330
|
+
selectable_attr :product_type_cd, :base_name => 'type' do
|
331
|
+
entry '01', :book, '書籍', :discount => 0.8
|
332
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
333
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
334
|
+
entry '09', :other, 'その他', :discount => 1
|
335
|
+
end
|
336
|
+
|
337
|
+
def discount_price
|
338
|
+
(type_entry[:discount] * price).to_i
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
it "test_product3" do
|
343
|
+
assert_product_discount(Product3)
|
344
|
+
# 選択肢を表示するためのデータは以下のように取得できる
|
345
|
+
Product3.type_options.should ==
|
346
|
+
[['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
|
347
|
+
|
348
|
+
p3 = Product3.new
|
349
|
+
p3.product_type_cd.should be_nil
|
350
|
+
p3.type_key.should be_nil
|
351
|
+
p3.type_name.should be_nil
|
352
|
+
# idを変更すると得られるキーも名称も変わります
|
353
|
+
p3.product_type_cd = '02'
|
354
|
+
p3.product_type_cd.should == '02'
|
355
|
+
p3.type_key.should == :dvd
|
356
|
+
p3.type_name.should == 'DVD'
|
357
|
+
# キーを変更すると得られるidも名称も変わります
|
358
|
+
p3.type_key = :book
|
359
|
+
p3.product_type_cd.should == '01'
|
360
|
+
p3.type_key.should == :book
|
361
|
+
p3.type_name.should == '書籍'
|
362
|
+
# id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
|
363
|
+
p3.type_key = :cd
|
364
|
+
p3.type_entry[:discount].should == 0.5
|
365
|
+
|
366
|
+
Product3.type_id_by_key(:book).should == '01'
|
367
|
+
Product3.type_id_by_key(:dvd).should == '02'
|
368
|
+
Product3.type_name_by_key(:cd).should == 'CD'
|
369
|
+
Product3.type_name_by_key(:other).should == 'その他'
|
370
|
+
Product3.type_key_by_id('09').should == :other
|
371
|
+
Product3.type_name_by_id('01').should == '書籍'
|
372
|
+
Product3.type_keys.should == [:book, :dvd, :cd, :other]
|
373
|
+
Product3.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
|
374
|
+
Product3.type_keys('02', '03').should == [:dvd, :cd]
|
375
|
+
Product3.type_names(:cd, :dvd).should == ['CD', 'DVD']
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
|
3
|
+
create_table "products", :force => true do |t|
|
4
|
+
t.string "product_type_cd"
|
5
|
+
t.integer "price"
|
6
|
+
t.string "name"
|
7
|
+
t.datetime "created_at"
|
8
|
+
t.datetime "updated_at"
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table "item_masters", :force => true do |t|
|
12
|
+
t.string "category_name", :limit => 20
|
13
|
+
t.integer "item_no"
|
14
|
+
t.string "item_cd", :limit => 2
|
15
|
+
t.string "name", :limit => 20
|
16
|
+
t.datetime "created_at"
|
17
|
+
t.datetime "updated_at"
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table "i18n_item_masters", :force => true do |t|
|
21
|
+
t.string "locale", :limit => 5
|
22
|
+
t.string "category_name", :limit => 20
|
23
|
+
t.integer "item_no"
|
24
|
+
t.string "item_cd", :limit => 2
|
25
|
+
t.string "name", :limit => 20
|
26
|
+
t.datetime "created_at"
|
27
|
+
t.datetime "updated_at"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
if defined?(I18n)
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
class SelectableAttrI18nTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
I18n.backend = I18n::Backend::Simple.new
|
9
|
+
I18n.backend.store_translations 'en', :selectable_attrs => {:enum1 => {
|
10
|
+
:entry1 => 'entry one',
|
11
|
+
:entry2 => 'entry two',
|
12
|
+
:entry3 => 'entry three'
|
13
|
+
} }
|
14
|
+
I18n.backend.store_translations 'ja', :selectable_attrs => {:enum1 => {
|
15
|
+
:entry1 => 'エントリ壱',
|
16
|
+
:entry2 => 'エントリ弐',
|
17
|
+
:entry3 => 'エントリ参'
|
18
|
+
} }
|
19
|
+
end
|
20
|
+
|
21
|
+
Enum1 = SelectableAttr::Enum.new do
|
22
|
+
i18n_scope(:selectable_attrs, :enum1)
|
23
|
+
entry 1, :entry1, "エントリ1"
|
24
|
+
entry 2, :entry2, "エントリ2"
|
25
|
+
entry 3, :entry3, "エントリ3"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_enum1_i18n
|
29
|
+
I18n.locale = nil
|
30
|
+
# assert_equal nil, I18n.locale
|
31
|
+
# assert_equal "エントリ1", Enum1.name_by_key(:entry1)
|
32
|
+
# assert_equal "エントリ2", Enum1.name_by_key(:entry2)
|
33
|
+
# assert_equal "エントリ3", Enum1.name_by_key(:entry3)
|
34
|
+
# assert_equal ["エントリ1", "エントリ2", "エントリ3"], Enum1.names
|
35
|
+
assert_equal :en, I18n.locale
|
36
|
+
assert_equal "entry one", Enum1.name_by_key(:entry1)
|
37
|
+
assert_equal "entry two", Enum1.name_by_key(:entry2)
|
38
|
+
assert_equal "entry three", Enum1.name_by_key(:entry3)
|
39
|
+
assert_equal ["entry one", "entry two", "entry three"], Enum1.names
|
40
|
+
|
41
|
+
I18n.locale = 'ja'
|
42
|
+
assert_equal "エントリ壱", Enum1.name_by_key(:entry1)
|
43
|
+
assert_equal "エントリ弐", Enum1.name_by_key(:entry2)
|
44
|
+
assert_equal "エントリ参", Enum1.name_by_key(:entry3)
|
45
|
+
assert_equal ["エントリ壱", "エントリ弐", "エントリ参"], Enum1.names
|
46
|
+
|
47
|
+
I18n.locale = 'en'
|
48
|
+
assert_equal "entry one", Enum1.name_by_key(:entry1)
|
49
|
+
assert_equal "entry two", Enum1.name_by_key(:entry2)
|
50
|
+
assert_equal "entry three", Enum1.name_by_key(:entry3)
|
51
|
+
assert_equal ["entry one", "entry two", "entry three"], Enum1.names
|
52
|
+
end
|
53
|
+
|
54
|
+
class EnumBase
|
55
|
+
include ::SelectableAttr::Base
|
56
|
+
end
|
57
|
+
|
58
|
+
class SelectableAttrMock1 < EnumBase
|
59
|
+
selectable_attr :attr1, :default => 2 do
|
60
|
+
i18n_scope(:selectable_attrs, :enum1)
|
61
|
+
entry 1, :entry1, "エントリ1"
|
62
|
+
entry 2, :entry2, "エントリ2"
|
63
|
+
entry 3, :entry3, "エントリ3"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_attr1_i18n
|
68
|
+
I18n.default_locale = 'ja'
|
69
|
+
I18n.locale = nil
|
70
|
+
# assert_equal nil, I18n.locale
|
71
|
+
# assert_equal "エントリ1", SelectableAttrMock1.attr1_name_by_key(:entry1)
|
72
|
+
# assert_equal "エントリ2", SelectableAttrMock1.attr1_name_by_key(:entry2)
|
73
|
+
# assert_equal "エントリ3", SelectableAttrMock1.attr1_name_by_key(:entry3)
|
74
|
+
assert_equal 'ja', I18n.locale
|
75
|
+
assert_equal "エントリ壱", SelectableAttrMock1.attr1_name_by_key(:entry1)
|
76
|
+
assert_equal "エントリ弐", SelectableAttrMock1.attr1_name_by_key(:entry2)
|
77
|
+
assert_equal "エントリ参", SelectableAttrMock1.attr1_name_by_key(:entry3)
|
78
|
+
assert_equal [["エントリ壱",1], ["エントリ弐",2], ["エントリ参",3]], SelectableAttrMock1.attr1_options
|
79
|
+
|
80
|
+
I18n.locale = 'ja'
|
81
|
+
assert_equal "エントリ壱", SelectableAttrMock1.attr1_name_by_key(:entry1)
|
82
|
+
assert_equal "エントリ弐", SelectableAttrMock1.attr1_name_by_key(:entry2)
|
83
|
+
assert_equal "エントリ参", SelectableAttrMock1.attr1_name_by_key(:entry3)
|
84
|
+
assert_equal [["エントリ壱",1], ["エントリ弐",2], ["エントリ参",3]], SelectableAttrMock1.attr1_options
|
85
|
+
|
86
|
+
I18n.locale = 'en'
|
87
|
+
assert_equal "entry one", SelectableAttrMock1.attr1_name_by_key(:entry1)
|
88
|
+
assert_equal "entry two", SelectableAttrMock1.attr1_name_by_key(:entry2)
|
89
|
+
assert_equal "entry three", SelectableAttrMock1.attr1_name_by_key(:entry3)
|
90
|
+
assert_equal [["entry one",1], ["entry two",2], ["entry three",3]], SelectableAttrMock1.attr1_options
|
91
|
+
end
|
92
|
+
|
93
|
+
class SelectableAttrMock2 < EnumBase
|
94
|
+
selectable_attr :enum1, :default => 2 do
|
95
|
+
i18n_scope(:selectable_attrs, :enum1)
|
96
|
+
entry 1, :entry1, "エントリ1"
|
97
|
+
entry 2, :entry2, "エントリ2"
|
98
|
+
entry 3, :entry3, "エントリ3"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_enum1_i18n
|
103
|
+
I18n.default_locale = 'ja'
|
104
|
+
I18n.locale = nil
|
105
|
+
# assert_equal nil, I18n.locale
|
106
|
+
# assert_equal "エントリ1", SelectableAttrMock2.enum1_name_by_key(:entry1)
|
107
|
+
# assert_equal "エントリ2", SelectableAttrMock2.enum1_name_by_key(:entry2)
|
108
|
+
# assert_equal "エントリ3", SelectableAttrMock2.enum1_name_by_key(:entry3)
|
109
|
+
assert_equal 'ja', I18n.locale
|
110
|
+
assert_equal "エントリ壱", SelectableAttrMock2.enum1_name_by_key(:entry1)
|
111
|
+
assert_equal "エントリ弐", SelectableAttrMock2.enum1_name_by_key(:entry2)
|
112
|
+
assert_equal "エントリ参", SelectableAttrMock2.enum1_name_by_key(:entry3)
|
113
|
+
assert_equal [["エントリ壱",1], ["エントリ弐",2], ["エントリ参",3]], SelectableAttrMock2.enum1_options
|
114
|
+
|
115
|
+
I18n.locale = 'ja'
|
116
|
+
assert_equal "エントリ壱", SelectableAttrMock2.enum1_name_by_key(:entry1)
|
117
|
+
assert_equal "エントリ弐", SelectableAttrMock2.enum1_name_by_key(:entry2)
|
118
|
+
assert_equal "エントリ参", SelectableAttrMock2.enum1_name_by_key(:entry3)
|
119
|
+
assert_equal [["エントリ壱",1], ["エントリ弐",2], ["エントリ参",3]], SelectableAttrMock2.enum1_options
|
120
|
+
|
121
|
+
I18n.locale = 'en'
|
122
|
+
assert_equal "entry one", SelectableAttrMock2.enum1_name_by_key(:entry1)
|
123
|
+
assert_equal "entry two", SelectableAttrMock2.enum1_name_by_key(:entry2)
|
124
|
+
assert_equal "entry three", SelectableAttrMock2.enum1_name_by_key(:entry3)
|
125
|
+
assert_equal [["entry one",1], ["entry two",2], ["entry three",3]], SelectableAttrMock2.enum1_options
|
126
|
+
end
|
127
|
+
|
128
|
+
# i18n用のlocaleカラムを持つselectable_attrのエントリ名をDB上に保持するためのモデル
|
129
|
+
class I18nItemMaster < ActiveRecord::Base
|
130
|
+
end
|
131
|
+
|
132
|
+
# selectable_attrを使った場合その3
|
133
|
+
# アクセス時に毎回アクセス時にDBから項目名を取得します。
|
134
|
+
# 対象となる項目名はi18n対応している名称です
|
135
|
+
class ProductWithI18nDB1 < ActiveRecord::Base
|
136
|
+
set_table_name 'products'
|
137
|
+
selectable_attr :product_type_cd do
|
138
|
+
# update_byメソッドには、エントリのidと名称を返すSELECT文を指定する代わりに、
|
139
|
+
# エントリのidと名称の配列の配列を返すブロックを指定することも可能です。
|
140
|
+
update_by(:when => :everytime) do
|
141
|
+
records = I18nItemMaster.find(:all,
|
142
|
+
:conditions => [
|
143
|
+
"category_name = 'product_type_cd' and locale = ? ", I18n.locale.to_s],
|
144
|
+
:order => "item_no")
|
145
|
+
records.map{|r| [r.item_cd, r.name]}
|
146
|
+
end
|
147
|
+
entry '01', :book, '書籍', :discount => 0.8
|
148
|
+
entry '02', :dvd, 'DVD', :discount => 0.2
|
149
|
+
entry '03', :cd, 'CD', :discount => 0.5
|
150
|
+
entry '09', :other, 'その他', :discount => 1
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_update_entry_name_with_i18n
|
156
|
+
I18n.locale = 'ja'
|
157
|
+
# DBに全くデータがなくてもコードで記述してあるエントリは存在します。
|
158
|
+
I18nItemMaster.delete_all("category_name = 'product_type_cd'")
|
159
|
+
assert_equal 4, ProductWithI18nDB1.product_type_entries.length
|
160
|
+
assert_equal '書籍', ProductWithI18nDB1.product_type_name_by_key(:book)
|
161
|
+
assert_equal 'DVD', ProductWithI18nDB1.product_type_name_by_key(:dvd)
|
162
|
+
assert_equal 'CD', ProductWithI18nDB1.product_type_name_by_key(:cd)
|
163
|
+
assert_equal 'その他', ProductWithI18nDB1.product_type_name_by_key(:other)
|
164
|
+
|
165
|
+
# assert_product_discount(ProductWithI18nDB1)
|
166
|
+
|
167
|
+
# DBからエントリの名称を動的に変更できます
|
168
|
+
item_book = I18nItemMaster.create(:locale => 'ja', :category_name => 'product_type_cd', :item_no => 1, :item_cd => '01', :name => '本')
|
169
|
+
assert_equal 4, ProductWithI18nDB1.product_type_entries.length
|
170
|
+
assert_equal '本', ProductWithI18nDB1.product_type_name_by_key(:book)
|
171
|
+
assert_equal 'DVD', ProductWithI18nDB1.product_type_name_by_key(:dvd)
|
172
|
+
assert_equal 'CD', ProductWithI18nDB1.product_type_name_by_key(:cd)
|
173
|
+
assert_equal 'その他', ProductWithI18nDB1.product_type_name_by_key(:other)
|
174
|
+
assert_equal [['本', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']], ProductWithI18nDB1.product_type_options
|
175
|
+
|
176
|
+
# DBからエントリの並び順を動的に変更できます
|
177
|
+
item_book.item_no = 4;
|
178
|
+
item_book.save!
|
179
|
+
item_other = I18nItemMaster.create(:locale => 'ja', :category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'その他')
|
180
|
+
item_dvd = I18nItemMaster.create(:locale => 'ja', :category_name => 'product_type_cd', :item_no => 2, :item_cd => '02') # nameは指定しなかったらデフォルトが使われます。
|
181
|
+
item_cd = I18nItemMaster.create(:locale => 'ja', :category_name => 'product_type_cd', :item_no => 3, :item_cd => '03') # nameは指定しなかったらデフォルトが使われます。
|
182
|
+
assert_equal [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01']], ProductWithI18nDB1.product_type_options
|
183
|
+
|
184
|
+
# DBからエントリを動的に追加することも可能です。
|
185
|
+
item_toys = I18nItemMaster.create(:locale => 'ja', :category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'おもちゃ')
|
186
|
+
assert_equal [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']], ProductWithI18nDB1.product_type_options
|
187
|
+
assert_equal :entry_04, ProductWithI18nDB1.product_type_key_by_id('04')
|
188
|
+
|
189
|
+
# 英語名を登録
|
190
|
+
item_book = I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 4, :item_cd => '01', :name => 'Book')
|
191
|
+
item_other = I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'Others')
|
192
|
+
item_dvd = I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 2, :item_cd => '02', :name => 'DVD')
|
193
|
+
item_cd = I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 3, :item_cd => '03', :name => 'CD')
|
194
|
+
item_toys = I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'Toy')
|
195
|
+
|
196
|
+
# 英語名が登録されていてもI18n.localeが変わらなければ、日本語のまま
|
197
|
+
assert_equal [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']], ProductWithI18nDB1.product_type_options
|
198
|
+
assert_equal :entry_04, ProductWithI18nDB1.product_type_key_by_id('04')
|
199
|
+
|
200
|
+
# I18n.localeを変更すると取得できるエントリの名称も変わります
|
201
|
+
I18n.locale = 'en'
|
202
|
+
assert_equal [['Others', '09'], ['DVD', '02'], ['CD', '03'], ['Book', '01'], ['Toy', '04']], ProductWithI18nDB1.product_type_options
|
203
|
+
assert_equal :entry_04, ProductWithI18nDB1.product_type_key_by_id('04')
|
204
|
+
|
205
|
+
I18n.locale = 'ja'
|
206
|
+
assert_equal [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']], ProductWithI18nDB1.product_type_options
|
207
|
+
assert_equal :entry_04, ProductWithI18nDB1.product_type_key_by_id('04')
|
208
|
+
|
209
|
+
I18n.locale = 'en'
|
210
|
+
assert_equal [['Others', '09'], ['DVD', '02'], ['CD', '03'], ['Book', '01'], ['Toy', '04']], ProductWithI18nDB1.product_type_options
|
211
|
+
assert_equal :entry_04, ProductWithI18nDB1.product_type_key_by_id('04')
|
212
|
+
|
213
|
+
# DBからレコードを削除してもコードで定義したentryは削除されません。
|
214
|
+
# 順番はDBからの取得順で並び替えられたものの後になります
|
215
|
+
item_dvd.destroy
|
216
|
+
assert_equal [['Others', '09'], ['CD', '03'], ['Book', '01'], ['Toy', '04'], ['DVD', '02']], ProductWithI18nDB1.product_type_options
|
217
|
+
|
218
|
+
# DB上で追加したレコードを削除すると、エントリも削除されます
|
219
|
+
item_toys.destroy
|
220
|
+
assert_equal [['Others', '09'], ['CD', '03'], ['Book', '01'], ['DVD', '02']], ProductWithI18nDB1.product_type_options
|
221
|
+
|
222
|
+
# 名称を指定していたDBのレコードを削除したら元に戻ります。
|
223
|
+
item_book.destroy
|
224
|
+
assert_equal [['Others', '09'], ['CD', '03'], ['書籍', '01'], ['DVD', '02']], ProductWithI18nDB1.product_type_options
|
225
|
+
|
226
|
+
# エントリに該当するレコードを全部削除したら、元に戻ります。
|
227
|
+
I18nItemMaster.delete_all("category_name = 'product_type_cd'")
|
228
|
+
assert_equal [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']], ProductWithI18nDB1.product_type_options
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_i18n_export
|
232
|
+
I18nItemMaster.delete_all("category_name = 'product_type_cd'")
|
233
|
+
|
234
|
+
I18n.locale = 'ja'
|
235
|
+
actual = SelectableAttr::Enum.i18n_export
|
236
|
+
assert_equal [:selectable_attrs], actual.keys
|
237
|
+
assert_equal true, actual[:selectable_attrs].keys.include?(:enum1)
|
238
|
+
assert_equal(
|
239
|
+
{:entry1=>"エントリ壱",
|
240
|
+
:entry2=>"エントリ弐",
|
241
|
+
:entry3=>"エントリ参"},
|
242
|
+
actual[:selectable_attrs][:enum1])
|
243
|
+
|
244
|
+
assert_equal true, actual[:selectable_attrs].keys.include?(:SelectableAttrI18nTest)
|
245
|
+
assert_equal(
|
246
|
+
{:ProductWithI18nDB1=>
|
247
|
+
{:product_type_cd=>
|
248
|
+
{:book=>"書籍", :dvd=>"DVD", :cd=>"CD", :other=>"その他"}}},
|
249
|
+
actual[:selectable_attrs][:SelectableAttrI18nTest])
|
250
|
+
|
251
|
+
|
252
|
+
I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'Others')
|
253
|
+
I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 2, :item_cd => '02', :name => 'DVD')
|
254
|
+
I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 3, :item_cd => '03', :name => 'CD')
|
255
|
+
I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 4, :item_cd => '01', :name => 'Book')
|
256
|
+
I18nItemMaster.create(:locale => 'en', :category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'Toy')
|
257
|
+
|
258
|
+
I18n.locale = 'en'
|
259
|
+
actual = SelectableAttr::Enum.i18n_export
|
260
|
+
assert_equal [:selectable_attrs], actual.keys
|
261
|
+
assert_equal true, actual[:selectable_attrs].keys.include?(:enum1)
|
262
|
+
assert_equal(
|
263
|
+
{:entry1=>"entry one",
|
264
|
+
:entry2=>"entry two",
|
265
|
+
:entry3=>"entry three"},
|
266
|
+
actual[:selectable_attrs][:enum1])
|
267
|
+
assert_equal true, actual[:selectable_attrs].keys.include?(:SelectableAttrI18nTest)
|
268
|
+
assert_equal(
|
269
|
+
{:ProductWithI18nDB1=>
|
270
|
+
{:product_type_cd=>
|
271
|
+
{:book=>"Book", :dvd=>"DVD", :cd=>"CD", :other=>"Others", :entry_04=>"Toy"}}},
|
272
|
+
actual[:selectable_attrs][:SelectableAttrI18nTest])
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
$KCODE='u'
|
2
|
+
|
3
|
+
FIXTURES_ROOT = File.join(File.dirname(__FILE__), 'fixtures') unless defined?(FIXTURES_ROOT)
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_record'
|
8
|
+
require 'active_record/fixtures'
|
9
|
+
require 'active_record/test_case'
|
10
|
+
require 'action_view'
|
11
|
+
|
12
|
+
require 'selectable_attr'
|
13
|
+
|
14
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
15
|
+
require File.join(File.dirname(__FILE__), '..', 'init')
|
16
|
+
|
17
|
+
require 'yaml'
|
18
|
+
config = YAML.load(IO.read(File.join(File.dirname(__FILE__), 'database.yml')))
|
19
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), 'debug.log'))
|
20
|
+
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
|
21
|
+
|
22
|
+
load(File.join(File.dirname(__FILE__), 'schema.rb'))
|
23
|
+
|
24
|
+
|
25
|
+
def assert_hash(expected, actual)
|
26
|
+
keys = (expected.keys + actual.keys).uniq
|
27
|
+
keys.each do |key|
|
28
|
+
assert_equal expected[key], actual[key], "unmatch value for #{key.inspect}"
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: akm-selectable_attr_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Takeshi Akima
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-27 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: selectable_attr_rails makes possible to use selectable_attr in rails application
|
17
|
+
email: akima@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/selectable_attr_rails
|
27
|
+
- lib/selectable_attr_rails/db_loadable.rb
|
28
|
+
- lib/selectable_attr_rails/helpers
|
29
|
+
- lib/selectable_attr_rails/helpers/abstract_selection_helper.rb
|
30
|
+
- lib/selectable_attr_rails/helpers/check_box_group_helper.rb
|
31
|
+
- lib/selectable_attr_rails/helpers/radio_button_group_helper.rb
|
32
|
+
- lib/selectable_attr_rails/helpers/select_helper.rb
|
33
|
+
- lib/selectable_attr_rails/helpers.rb
|
34
|
+
- lib/selectable_attr_rails/version.rb
|
35
|
+
- lib/selectable_attr_rails.rb
|
36
|
+
- spec/database.yml
|
37
|
+
- spec/debug.log
|
38
|
+
- spec/fixtures
|
39
|
+
- spec/introduction_spec.rb
|
40
|
+
- spec/schema.rb
|
41
|
+
- spec/selectable_attr_i18n_test.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/akm/selectable_attr_rails/
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --inline-source
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: selectable_attr_rails makes possible to use selectable_attr in rails application
|
70
|
+
test_files: []
|
71
|
+
|