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.
- data/.gitignore +5 -0
- data/MIT-LICENSE +20 -0
- data/README +198 -0
- data/Rakefile +57 -0
- data/VERSION.yml +4 -0
- data/init.rb +5 -0
- data/install.rb +1 -0
- data/lib/selectable_attr_i18n.rb +38 -0
- data/lib/selectable_attr_rails/db_loadable.rb +65 -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/helpers.rb +7 -0
- data/lib/selectable_attr_rails/validatable/base.rb +33 -0
- data/lib/selectable_attr_rails/validatable/enum.rb +16 -0
- data/lib/selectable_attr_rails/validatable.rb +8 -0
- data/lib/selectable_attr_rails/version.rb +3 -0
- data/lib/selectable_attr_rails.rb +43 -0
- data/selectable_attr_rails.gemspec +74 -0
- data/spec/database.yml +6 -0
- data/spec/fixtures/.gitignore +0 -0
- data/spec/introduction_spec.rb +397 -0
- data/spec/schema.rb +30 -0
- data/spec/selectable_attr_i18n_spec.rb +293 -0
- data/spec/spec_helper.rb +30 -0
- data/uninstall.rb +1 -0
- metadata +123 -0
@@ -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
|
@@ -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,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
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
|
+
|