akm-selectable_attr_rails 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|
+
|