selectable_attr_rails 0.3.7

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.
@@ -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 << '&nbsp;'
48
+ result << builder.label
49
+ result << '&nbsp;'
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
@@ -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,33 @@
1
+ require 'selectable_attr_rails/validatable'
2
+
3
+ module SelectableAttrRails
4
+ module Validatable
5
+ module Base
6
+ def self.included(mod)
7
+ mod.extend(ClassMethods)
8
+ mod.instance_eval do
9
+ alias :define_enum_without_validatable :define_enum
10
+ alias :define_enum :define_enum_with_validatable
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def define_enum_with_validatable(context)
16
+ enum = context[:enum]
17
+ if options = enum.validates_format_options
18
+ options[:with] = Regexp.union(*enum.entries.map{|entry| /#{Regexp.escape(entry.id)}/})
19
+ entry_format = options.delete(:entry_format) || '#{entry.name}'
20
+ entries = enum.entries.map{|entry| instance_eval("\"#{entry_format}\"")}.join(', ')
21
+ message = options.delete(:message) || 'is invalid, must be one of #{entries}'
22
+ options[:message] = instance_eval("\"#{message}\"")
23
+ validates_format_of(context[:attr], options)
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ end
30
+ end
31
+ end
32
+
33
+
@@ -0,0 +1,16 @@
1
+ require 'selectable_attr_rails/validatable'
2
+
3
+ module SelectableAttrRails
4
+ module Validatable
5
+ module Enum
6
+ def validates_format_options
7
+ @validates_format_options
8
+ end
9
+ def validates_format(options = nil)
10
+ return @validates_format_options = nil if options == false
11
+ @validates_format_options = options || {}
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require 'selectable_attr_rails'
2
+
3
+ module SelectableAttrRails
4
+ module Validatable
5
+ autoload :Base, 'selectable_attr_rails/validatable/base'
6
+ autoload :Enum, 'selectable_attr_rails/validatable/enum'
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module SelectableAttrRails
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'selectable_attr'
3
+
4
+ module SelectableAttrRails
5
+ autoload :Helpers, 'selectable_attr_rails/helpers'
6
+ autoload :DbLoadable, 'selectable_attr_rails/db_loadable'
7
+ autoload :Validatable, 'selectable_attr_rails/validatable'
8
+
9
+ class << self
10
+
11
+ def add_features_to_active_record
12
+ puts "SelectableAttrRails.add_features_to_active_record"
13
+ ActiveRecord::Base.module_eval do
14
+ include ::SelectableAttr::Base
15
+ include ::SelectableAttrRails::Validatable::Base
16
+ end
17
+ SelectableAttr::Enum.module_eval do
18
+ include ::SelectableAttrRails::DbLoadable
19
+ include ::SelectableAttrRails::Validatable::Enum
20
+ end
21
+ end
22
+
23
+ def add_features_to_action_view
24
+ ActionView::Base.module_eval do
25
+ include ::SelectableAttrRails::Helpers::SelectHelper::Base
26
+ include ::SelectableAttrRails::Helpers::CheckBoxGroupHelper::Base
27
+ include ::SelectableAttrRails::Helpers::RadioButtonGroupHelper::Base
28
+ end
29
+ ActionView::Helpers::FormBuilder.module_eval do
30
+ include ::SelectableAttrRails::Helpers::SelectHelper::FormBuilder
31
+ include ::SelectableAttrRails::Helpers::CheckBoxGroupHelper::FormBuilder
32
+ include ::SelectableAttrRails::Helpers::RadioButtonGroupHelper::FormBuilder
33
+ end
34
+ end
35
+
36
+ def add_features_to_rails
37
+ puts "SelectableAttrRails.add_features_to_rails"
38
+ add_features_to_active_record
39
+ add_features_to_action_view
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{selectable_attr_rails}
5
+ s.version = "0.3.7"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Takeshi Akima"]
9
+ s.date = %q{2009-08-17}
10
+ s.description = %q{selectable_attr_rails makes possible to use selectable_attr in rails application}
11
+ s.email = %q{akima@gmail.com}
12
+ s.extra_rdoc_files = [
13
+ "README"
14
+ ]
15
+ s.files = [
16
+ ".gitignore",
17
+ "MIT-LICENSE",
18
+ "README",
19
+ "Rakefile",
20
+ "VERSION.yml",
21
+ "init.rb",
22
+ "install.rb",
23
+ "lib/selectable_attr_i18n.rb",
24
+ "lib/selectable_attr_rails.rb",
25
+ "lib/selectable_attr_rails/db_loadable.rb",
26
+ "lib/selectable_attr_rails/helpers.rb",
27
+ "lib/selectable_attr_rails/helpers/abstract_selection_helper.rb",
28
+ "lib/selectable_attr_rails/helpers/check_box_group_helper.rb",
29
+ "lib/selectable_attr_rails/helpers/radio_button_group_helper.rb",
30
+ "lib/selectable_attr_rails/helpers/select_helper.rb",
31
+ "lib/selectable_attr_rails/version.rb",
32
+ "selectable_attr_rails.gemspec",
33
+ "spec/database.yml",
34
+ "spec/fixtures/.gitignore",
35
+ "spec/introduction_spec.rb",
36
+ "spec/schema.rb",
37
+ "spec/selectable_attr_i18n_spec.rb",
38
+ "spec/spec_helper.rb",
39
+ "uninstall.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/akm/selectable_attr_rails/}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.4}
45
+ s.summary = %q{selectable_attr_rails makes possible to use selectable_attr in rails application}
46
+ s.test_files = [
47
+ "spec/introduction_spec.rb",
48
+ "spec/schema.rb",
49
+ "spec/selectable_attr_i18n_spec.rb",
50
+ "spec/spec_helper.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.0.2"])
59
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.0.2"])
60
+ s.add_runtime_dependency(%q<actionpack>, [">= 2.0.2"])
61
+ s.add_runtime_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
62
+ else
63
+ s.add_dependency(%q<activesupport>, [">= 2.0.2"])
64
+ s.add_dependency(%q<activerecord>, [">= 2.0.2"])
65
+ s.add_dependency(%q<actionpack>, [">= 2.0.2"])
66
+ s.add_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
67
+ end
68
+ else
69
+ s.add_dependency(%q<activesupport>, [">= 2.0.2"])
70
+ s.add_dependency(%q<activerecord>, [">= 2.0.2"])
71
+ s.add_dependency(%q<actionpack>, [">= 2.0.2"])
72
+ s.add_dependency(%q<akm-selectable_attr>, [">= 0.3.5"])
73
+ end
74
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,6 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :dbfile: selectable_attr_test.sqlite.db
4
+ sqlite3:
5
+ :adapter: sqlite3
6
+ :dbfile: selectable_attr_test.sqlite3.db
File without changes
@@ -0,0 +1,397 @@
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
+ validates_format :allow_nil => true, :message => 'は次のいずれかでなければなりません。 #{entries}'
97
+ end
98
+
99
+ def discount_price
100
+ (product_type_entry[:discount] * price).to_i
101
+ end
102
+ end
103
+
104
+ it "test_product1" do
105
+ assert_product_discount(Product1)
106
+ # 選択肢を表示するためのデータは以下のように取得できる
107
+ Product1.product_type_options.should ==
108
+ [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
109
+ end
110
+
111
+
112
+ # selectable_attrが定義するインスタンスメソッドの詳細
113
+ it "test_product_type_instance_methods" do
114
+ p1 = Product1.new
115
+ p1.product_type_cd.should be_nil
116
+ p1.product_type_key.should be_nil
117
+ p1.product_type_name.should be_nil
118
+ # idを変更すると得られるキーも名称も変わります
119
+ p1.product_type_cd = '02'
120
+ p1.product_type_cd.should == '02'
121
+ p1.product_type_key.should == :dvd
122
+ p1.product_type_name.should == 'DVD'
123
+ # キーを変更すると得られるidも名称も変わります
124
+ p1.product_type_key = :book
125
+ p1.product_type_cd.should == '01'
126
+ p1.product_type_key.should == :book
127
+ p1.product_type_name.should == '書籍'
128
+ # id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
129
+ p1.product_type_key = :cd
130
+ p1.product_type_entry[:discount].should == 0.5
131
+ end
132
+
133
+ # selectable_attrが定義するクラスメソッドの詳細
134
+ it "test_product_type_class_methods" do
135
+ # キーからid、名称を取得できます
136
+ Product1.product_type_id_by_key(:book).should == '01'
137
+ Product1.product_type_id_by_key(:dvd).should == '02'
138
+ Product1.product_type_id_by_key(:cd).should == '03'
139
+ Product1.product_type_id_by_key(:other).should == '09'
140
+ Product1.product_type_name_by_key(:book).should == '書籍'
141
+ Product1.product_type_name_by_key(:dvd).should == 'DVD'
142
+ Product1.product_type_name_by_key(:cd).should == 'CD'
143
+ Product1.product_type_name_by_key(:other).should == 'その他'
144
+ # 存在しないキーの場合はnilを返します
145
+ Product1.product_type_id_by_key(nil).should be_nil
146
+ Product1.product_type_name_by_key(nil).should be_nil
147
+ Product1.product_type_id_by_key(:unexist).should be_nil
148
+ Product1.product_type_name_by_key(:unexist).should be_nil
149
+
150
+ # idからキー、名称を取得できます
151
+ Product1.product_type_key_by_id('01').should == :book
152
+ Product1.product_type_key_by_id('02').should == :dvd
153
+ Product1.product_type_key_by_id('03').should == :cd
154
+ Product1.product_type_key_by_id('09').should == :other
155
+ Product1.product_type_name_by_id('01').should == '書籍'
156
+ Product1.product_type_name_by_id('02').should == 'DVD'
157
+ Product1.product_type_name_by_id('03').should == 'CD'
158
+ Product1.product_type_name_by_id('09').should == 'その他'
159
+ # 存在しないidの場合はnilを返します
160
+ Product1.product_type_key_by_id(nil).should be_nil
161
+ Product1.product_type_name_by_id(nil).should be_nil
162
+ Product1.product_type_key_by_id('99').should be_nil
163
+ Product1.product_type_name_by_id('99').should be_nil
164
+
165
+ # id、キー、名称の配列を取得できます
166
+ Product1.product_type_ids.should == ['01', '02', '03', '09']
167
+ Product1.product_type_keys.should == [:book, :dvd, :cd, :other]
168
+ Product1.product_type_names.should == ['書籍', 'DVD', 'CD', 'その他']
169
+ # 一部のものだけ取得することも可能です。
170
+ Product1.product_type_ids(:cd, :dvd).should == ['03', '02' ]
171
+ Product1.product_type_keys('02', '03').should == [:dvd, :cd ]
172
+ Product1.product_type_names('02', '03').should == ['DVD', 'CD']
173
+ Product1.product_type_names(:cd, :dvd).should == ['CD', 'DVD']
174
+
175
+ # select_tagなどのoption_tagsを作るための配列なんか一発っす
176
+ Product1.product_type_options.should ==
177
+ [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
178
+ end
179
+
180
+ it "validate with entries" do
181
+ p1 = Product1.new
182
+ p1.product_type_cd.should == nil
183
+ p1.valid?.should == true
184
+ p1.errors.empty?.should == true
185
+
186
+ p1.product_type_key = :book
187
+ p1.product_type_cd.should == '01'
188
+ p1.valid?.should == true
189
+ p1.errors.empty?.should == true
190
+
191
+ p1.product_type_cd = 'XX'
192
+ p1.product_type_cd.should == 'XX'
193
+ p1.valid?.should == false
194
+ p1.errors.on(:product_type_cd).should == "は次のいずれかでなければなりません。 書籍, DVD, CD, その他"
195
+ end
196
+
197
+ # selectable_attrのエントリ名をDB上に保持するためのモデル
198
+ class ItemMaster < ActiveRecord::Base
199
+ end
200
+
201
+ # selectable_attrを使った場合その2
202
+ # アクセス時に毎回アクセス時にDBから項目名を取得します。
203
+ class ProductWithDB1 < ActiveRecord::Base
204
+ set_table_name 'products'
205
+
206
+ selectable_attr :product_type_cd do
207
+ update_by(
208
+ "select item_cd, name from item_masters where category_name = 'product_type_cd' order by item_no",
209
+ :when => :everytime)
210
+ entry '01', :book, '書籍', :discount => 0.8
211
+ entry '02', :dvd, 'DVD', :discount => 0.2
212
+ entry '03', :cd, 'CD', :discount => 0.5
213
+ entry '09', :other, 'その他', :discount => 1
214
+ end
215
+
216
+ def discount_price
217
+ (product_type_entry[:discount] * price).to_i
218
+ end
219
+ end
220
+
221
+ it "test_update_entry_name" do
222
+ # DBに全くデータがなくてもコードで記述してあるエントリは存在します。
223
+ ItemMaster.delete_all("category_name = 'product_type_cd'")
224
+ ProductWithDB1.product_type_entries.length.should == 4
225
+ ProductWithDB1.product_type_name_by_key(:book).should == '書籍'
226
+ ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
227
+ ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
228
+ ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
229
+
230
+ assert_product_discount(ProductWithDB1)
231
+
232
+ # DBからエントリの名称を動的に変更できます
233
+ item_book = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '01', :name => '本')
234
+ ProductWithDB1.product_type_entries.length.should == 4
235
+ ProductWithDB1.product_type_name_by_key(:book).should == '本'
236
+ ProductWithDB1.product_type_name_by_key(:dvd).should == 'DVD'
237
+ ProductWithDB1.product_type_name_by_key(:cd).should == 'CD'
238
+ ProductWithDB1.product_type_name_by_key(:other).should == 'その他'
239
+ ProductWithDB1.product_type_options.should ==
240
+ [['本', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
241
+
242
+ # DBからエントリの並び順を動的に変更できます
243
+ item_book.item_no = 4;
244
+ item_book.save!
245
+ item_other = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 1, :item_cd => '09', :name => 'その他')
246
+ item_dvd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 2, :item_cd => '02') # nameは指定しなかったらデフォルトが使われます。
247
+ item_cd = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 3, :item_cd => '03') # nameは指定しなかったらデフォルトが使われます。
248
+ ProductWithDB1.product_type_options.should ==
249
+ [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01']]
250
+
251
+ # DBからエントリを動的に追加することも可能です。
252
+ item_toys = ItemMaster.create(:category_name => 'product_type_cd', :item_no => 5, :item_cd => '04', :name => 'おもちゃ')
253
+ ProductWithDB1.product_type_options.should ==
254
+ [['その他', '09'], ['DVD', '02'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04']]
255
+ ProductWithDB1.product_type_key_by_id('04').should == :entry_04
256
+
257
+ # DBからレコードを削除してもコードで定義したentryは削除されません。
258
+ # 順番はDBからの取得順で並び替えられたものの後になります
259
+ item_dvd.destroy
260
+ ProductWithDB1.product_type_options.should ==
261
+ [['その他', '09'], ['CD', '03'], ['本', '01'], ['おもちゃ', '04'], ['DVD', '02']]
262
+
263
+ # DB上で追加したレコードを削除すると、エントリも削除されます
264
+ item_toys.destroy
265
+ ProductWithDB1.product_type_options.should ==
266
+ [['その他', '09'], ['CD', '03'], ['本', '01'], ['DVD', '02']]
267
+
268
+ # 名称を指定していたDBのレコードを削除したら元に戻ります。
269
+ item_book.destroy
270
+ ProductWithDB1.product_type_options.should ==
271
+ [['その他', '09'], ['CD', '03'], ['書籍', '01'], ['DVD', '02']]
272
+
273
+ # エントリに該当するレコードを全部削除したら、元に戻ります。
274
+ ItemMaster.delete_all("category_name = 'product_type_cd'")
275
+ ProductWithDB1.product_type_options.should ==
276
+ [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
277
+
278
+ assert_product_discount(ProductWithDB1)
279
+ end
280
+
281
+
282
+
283
+
284
+ # Q: product_type_cd の'_cd'はどこにいっちゃったの?
285
+ # A: デフォルトでは、/(_cd$|_code$|_cds$|_codes$)/ を削除したものをbase_nameとして
286
+ # 扱い、それに_keyなどを付加してメソッド名を定義します。もしこのルールを変更したい場合、
287
+ # selectable_attrを使う前に selectable_attr_name_pattern で新たなルールを指定してください。
288
+ class Product2 < ActiveRecord::Base
289
+ set_table_name 'products'
290
+ self.selectable_attr_name_pattern = /^product_|_cd$/
291
+
292
+ selectable_attr :product_type_cd do
293
+ entry '01', :book, '書籍', :discount => 0.8
294
+ entry '02', :dvd, 'DVD', :discount => 0.2
295
+ entry '03', :cd, 'CD', :discount => 0.5
296
+ entry '09', :other, 'その他', :discount => 1
297
+ end
298
+
299
+ def discount_price
300
+ (type_entry[:discount] * price).to_i
301
+ end
302
+ end
303
+
304
+ it "test_product2" do
305
+ assert_product_discount(Product2)
306
+ # 選択肢を表示するためのデータは以下のように取得できる
307
+ Product2.type_options.should ==
308
+ [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
309
+
310
+ p2 = Product2.new
311
+ p2.product_type_cd.should be_nil
312
+ p2.type_key.should be_nil
313
+ p2.type_name.should be_nil
314
+ # idを変更すると得られるキーも名称も変わります
315
+ p2.product_type_cd = '02'
316
+ p2.product_type_cd.should == '02'
317
+ p2.type_key.should == :dvd
318
+ p2.type_name.should == 'DVD'
319
+ # キーを変更すると得られるidも名称も変わります
320
+ p2.type_key = :book
321
+ p2.product_type_cd.should == '01'
322
+ p2.type_key.should == :book
323
+ p2.type_name.should == '書籍'
324
+ # id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
325
+ p2.type_key = :cd
326
+ p2.type_entry[:discount].should == 0.5
327
+
328
+ Product2.type_id_by_key(:book).should == '01'
329
+ Product2.type_id_by_key(:dvd).should == '02'
330
+ Product2.type_name_by_key(:cd).should == 'CD'
331
+ Product2.type_name_by_key(:other).should == 'その他'
332
+ Product2.type_key_by_id('09').should == :other
333
+ Product2.type_name_by_id('01').should == '書籍'
334
+ Product2.type_keys.should == [:book, :dvd, :cd, :other]
335
+ Product2.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
336
+ Product2.type_keys('02', '03').should == [:dvd, :cd]
337
+ Product2.type_names(:cd, :dvd).should == ['CD', 'DVD']
338
+ end
339
+
340
+
341
+
342
+
343
+ # Q: selectable_attrの呼び出し毎にbase_bname(って言うの?)を指定したいんだけど。
344
+ # A: base_nameオプションを指定してください。
345
+ class Product3 < ActiveRecord::Base
346
+ set_table_name 'products'
347
+
348
+ selectable_attr :product_type_cd, :base_name => 'type' do
349
+ entry '01', :book, '書籍', :discount => 0.8
350
+ entry '02', :dvd, 'DVD', :discount => 0.2
351
+ entry '03', :cd, 'CD', :discount => 0.5
352
+ entry '09', :other, 'その他', :discount => 1
353
+ end
354
+
355
+ def discount_price
356
+ (type_entry[:discount] * price).to_i
357
+ end
358
+ end
359
+
360
+ it "test_product3" do
361
+ assert_product_discount(Product3)
362
+ # 選択肢を表示するためのデータは以下のように取得できる
363
+ Product3.type_options.should ==
364
+ [['書籍', '01'], ['DVD', '02'], ['CD', '03'], ['その他', '09']]
365
+
366
+ p3 = Product3.new
367
+ p3.product_type_cd.should be_nil
368
+ p3.type_key.should be_nil
369
+ p3.type_name.should be_nil
370
+ # idを変更すると得られるキーも名称も変わります
371
+ p3.product_type_cd = '02'
372
+ p3.product_type_cd.should == '02'
373
+ p3.type_key.should == :dvd
374
+ p3.type_name.should == 'DVD'
375
+ # キーを変更すると得られるidも名称も変わります
376
+ p3.type_key = :book
377
+ p3.product_type_cd.should == '01'
378
+ p3.type_key.should == :book
379
+ p3.type_name.should == '書籍'
380
+ # id、キー、名称以外の任意の属性は、entryの[]メソッドで取得します。
381
+ p3.type_key = :cd
382
+ p3.type_entry[:discount].should == 0.5
383
+
384
+ Product3.type_id_by_key(:book).should == '01'
385
+ Product3.type_id_by_key(:dvd).should == '02'
386
+ Product3.type_name_by_key(:cd).should == 'CD'
387
+ Product3.type_name_by_key(:other).should == 'その他'
388
+ Product3.type_key_by_id('09').should == :other
389
+ Product3.type_name_by_id('01').should == '書籍'
390
+ Product3.type_keys.should == [:book, :dvd, :cd, :other]
391
+ Product3.type_names.should == ['書籍', 'DVD', 'CD', 'その他']
392
+ Product3.type_keys('02', '03').should == [:dvd, :cd]
393
+ Product3.type_names(:cd, :dvd).should == ['CD', 'DVD']
394
+ end
395
+
396
+ end
397
+