attribute_mapper 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,12 @@
1
1
  module AttributeMapper
2
-
2
+
3
3
  def self.included(model)
4
4
  model.extend ClassMethods
5
5
  model.send(:include, InstanceMethods)
6
6
  end
7
-
7
+
8
8
  module ClassMethods # @private
9
-
9
+
10
10
  # Map a column in your table to a human-friendly attribute on your
11
11
  # model. When +attribute+ is accessed, it will return the key
12
12
  # from the mapping hash. When the attribute is updated, the value
@@ -25,6 +25,7 @@ module AttributeMapper
25
25
  # Each attribute you map generates an options method, suitable for
26
26
  # use in form helpers. If you define an attribute +status+,
27
27
  # instances of your model will have a +status_options+ method
28
+ # (on the class and any instances)
28
29
  # that returns a sorted array of arrays containing
29
30
  # humanized-option-name/value pairs. By default this array is
30
31
  # sorted by the option name (closed/open/etc.) If you'd rather
@@ -42,16 +43,32 @@ module AttributeMapper
42
43
  # @option options :predicate_methods Generate methods for checking
43
44
  # whether an object has a certain attribute set
44
45
  def map_attribute(attribute, options)
45
- mapping = options[:to]
46
+ mapping = build_mapping(options)
46
47
  verify_existence_of attribute
47
48
  add_accessor_for attribute, mapping
48
49
  add_predicates_for attribute, mapping.keys if options.fetch(:predicate_methods) { true }
49
50
  override attribute
50
51
  add_options_helper_for attribute, mapping
52
+ add_options_helper_to_class attribute, self
51
53
  end
52
54
 
53
55
  private
54
-
56
+ def build_mapping(options)
57
+ case options[:to]
58
+ when Hash
59
+ options[:to]
60
+ when Array
61
+ build_mapping_from_array(options[:to])
62
+ end
63
+ end
64
+
65
+ # Transforms an array into a hash for the mapping.
66
+ # [:open, :closed] # => { :open => 1, :closed => 2 }
67
+ #
68
+ def build_mapping_from_array(array)
69
+ array.enum_for(:each_with_index).inject({}) { |h, (o, i)| h[o] = i+1; h }
70
+ end
71
+
55
72
  def add_accessor_for(attribute, mapping)
56
73
  class_eval(<<-EVAL, __FILE__, __LINE__)
57
74
  class << self
@@ -78,18 +95,30 @@ module AttributeMapper
78
95
  options = self.class.#{attribute.to_s.pluralize}.sort { |l, r|
79
96
  sort_by_keys ? l.first.to_s <=> r.first.to_s : l.last <=> r.last
80
97
  }.map { |f|
81
- [f[0].to_s.humanize, f[0]]
98
+ [f[0].to_s.capitalize, f[0]]
82
99
  }
83
- self.#{attribute}.present? ? [options, {:selected => self.#{attribute}}] : [options]
100
+ self.#{attribute}.present? ? [options, {:selected => self.#{attribute}}] : [options]
84
101
  end
85
102
  EVAL
86
103
  end
87
-
104
+
105
+ def add_options_helper_to_class(attribute, klass)
106
+ klass.instance_eval(<<-EVAL, __FILE__, __LINE__)
107
+ def #{attribute}_options(sort_by_keys=true)
108
+ options = #{attribute.to_s.pluralize}.sort { |l, r|
109
+ sort_by_keys ? l.first.to_s <=> r.first.to_s : l.last <=> r.last
110
+ }.map { |f|
111
+ [f[0].to_s.capitalize, f[0]]
112
+ }
113
+ end
114
+ EVAL
115
+ end
116
+
88
117
  def override(*args)
89
118
  override_getters *args
90
119
  override_setters *args
91
120
  end
92
-
121
+
93
122
  def override_getters(attribute)
94
123
  class_eval(<<-EVAL, __FILE__, __LINE__)
95
124
  def #{attribute}
@@ -97,7 +126,7 @@ module AttributeMapper
97
126
  end
98
127
  EVAL
99
128
  end
100
-
129
+
101
130
  def override_setters(attribute)
102
131
  class_eval(<<-EVAL, __FILE__, __LINE__)
103
132
  def #{attribute}=(raw_value)
@@ -106,15 +135,15 @@ module AttributeMapper
106
135
  end
107
136
  EVAL
108
137
  end
109
-
138
+
110
139
  def verify_existence_of(attribute)
111
140
  raise ArgumentError, "`#{attribute}' is not an attribute of `#{self}'" unless column_names.include?(attribute.to_s)
112
141
  end
113
-
142
+
114
143
  end
115
-
144
+
116
145
  module InstanceMethods
117
-
146
+
118
147
  private
119
148
 
120
149
  def resolve_value_of(attribute, raw_value)
@@ -126,5 +155,5 @@ module AttributeMapper
126
155
  end
127
156
 
128
157
  end
129
-
158
+
130
159
  end
@@ -1,17 +1,17 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class AttributeMapperTest < Test::Unit::TestCase
4
-
4
+
5
5
  context "Attribute Mapper" do
6
6
  setup do
7
7
  Ticket.map_attribute :status, :to => mapping
8
8
  end
9
-
9
+
10
10
  should "set mapping for each attribute" do
11
11
  assert_equal mapping[:open], Ticket.statuses[:open]
12
12
  assert_equal mapping[:closed], Ticket.statuses[:closed]
13
13
  end
14
-
14
+
15
15
  should "override getters and setters" do
16
16
  assert_nil ticket.status
17
17
  assert_nothing_raised do
@@ -19,11 +19,11 @@ class AttributeMapperTest < Test::Unit::TestCase
19
19
  end
20
20
  assert_equal :open, ticket.status
21
21
  assert_equal mapping[:open], ticket[:status]
22
-
22
+
23
23
  assert_nothing_raised do
24
24
  ticket.status = :closed
25
25
  end
26
-
26
+
27
27
  assert_equal :closed, ticket.status
28
28
  assert_equal mapping[:closed], ticket[:status]
29
29
  end
@@ -32,7 +32,7 @@ class AttributeMapperTest < Test::Unit::TestCase
32
32
  ticket.status = :open
33
33
  assert ticket.open?
34
34
  end
35
-
35
+
36
36
  should "allow indifferent access to setters" do
37
37
  assert_nothing_raised do
38
38
  ticket.status = :open
@@ -43,27 +43,27 @@ class AttributeMapperTest < Test::Unit::TestCase
43
43
  end
44
44
  assert_equal :open, ticket.status
45
45
  end
46
-
46
+
47
47
  should "raise an exception when trying to map to a non-existent column" do
48
48
  assert_raises(ArgumentError) do
49
49
  Ticket.map_attribute :this_column_does_not_exist, :to => {:i_will_fail => 1}
50
50
  end
51
51
  end
52
-
52
+
53
53
  should "raise an exception when setting an invalid value" do
54
54
  assert_raises(ArgumentError) do
55
55
  ticket.status = :non_existent_value
56
56
  end
57
57
  end
58
-
58
+
59
59
  should "allow setting a primitive value directly if the value is present in the mapping" do
60
60
  ticket = Ticket.new
61
61
  assert_nothing_raised do
62
62
  ticket.status = mapping[:open]
63
63
  end
64
-
64
+
65
65
  assert_equal :open, ticket.status
66
-
66
+
67
67
  assert_raises(ArgumentError) do
68
68
  ticket.status = 500
69
69
  end
@@ -76,10 +76,10 @@ class AttributeMapperTest < Test::Unit::TestCase
76
76
  should "work with attr_accessible" do
77
77
  new_ticket = Class.new(ActiveRecord::Base) do
78
78
  set_table_name "tickets"
79
-
79
+
80
80
  include AttributeMapper
81
81
  map_attribute :status, :to => {:open => 1, :closed => 2}
82
-
82
+
83
83
  attr_accessible :status
84
84
  end
85
85
 
@@ -95,6 +95,40 @@ class AttributeMapperTest < Test::Unit::TestCase
95
95
  ticket.status = :open
96
96
  assert_equal [[["Open", :open], ["Closed", :closed]], {:selected => :open}], ticket.status_options(false)
97
97
  end
98
+
99
+ should "provide a class level helper for forms" do
100
+ assert_equal [["Closed", :closed], ["Open", :open]], Ticket.status_options
101
+ assert_equal [["Open", :open], ["Closed", :closed]], Ticket.status_options(false)
102
+ end
103
+
104
+ should "not raise an error when setting to a blank value" do
105
+ assert_nothing_raised {
106
+ ticket.update_attributes(:status => "")
107
+ }
108
+ end
109
+
110
+ should "not raise an error when setting to a nil value" do
111
+ assert_nothing_raised {
112
+ ticket.update_attributes(:status => nil)
113
+ }
114
+ end
115
+
116
+ should "turn off generation of predicate methods" do
117
+ new_ticket = Class.new(ActiveRecord::Base) do
118
+ set_table_name "tickets"
119
+
120
+ include AttributeMapper
121
+ map_attribute :status, :to => {:open => 1, :closed => 2}, :predicate_methods => false
122
+ end
123
+
124
+ t = new_ticket.new
125
+ t.status = :open
126
+
127
+ assert_raises(NoMethodError) {
128
+ t.open?
129
+ }
130
+ end
131
+
98
132
 
99
133
  context "setting nil as a valid value in the mapping" do
100
134
  setup do
@@ -120,46 +154,44 @@ class AttributeMapperTest < Test::Unit::TestCase
120
154
 
121
155
  end
122
156
 
123
- should "not raise an error when setting to a blank value" do
124
- assert_nothing_raised {
125
- ticket.update_attributes(:status => "")
126
- }
127
- end
128
-
129
- should "not raise an error when setting to a nil value" do
130
- assert_nothing_raised {
131
- ticket.update_attributes(:status => nil)
132
- }
133
- end
134
-
135
- should "turn off generation of predicate methods" do
136
- new_ticket = Class.new(ActiveRecord::Base) do
157
+ should 'auto-expand arrays to a hash with the raw value being the index + 1' do
158
+ array_ticket = Class.new(ActiveRecord::Base) do
137
159
  set_table_name "tickets"
138
160
 
139
161
  include AttributeMapper
140
- map_attribute :status, :to => {:open => 1, :closed => 2}, :predicate_methods => false
162
+ map_attribute :status, :to => [:open, :closed]
141
163
  end
142
164
 
143
- t = new_ticket.new
144
- t.status = :open
165
+ ticket = array_ticket.new
166
+
167
+ assert_nil ticket.status
168
+ assert_nothing_raised do
169
+ ticket.status = :open
170
+ end
171
+ assert_equal :open, ticket.status
172
+ assert_equal mapping[:open], ticket[:status]
173
+
174
+ assert_nothing_raised do
175
+ ticket.status = :closed
176
+ end
177
+
178
+ assert_equal :closed, ticket.status
179
+ assert_equal mapping[:closed], ticket[:status]
145
180
 
146
- assert_raises(NoMethodError) {
147
- t.open?
148
- }
149
181
  end
150
-
182
+
151
183
  end
152
-
184
+
153
185
  #######
154
186
  private
155
187
  #######
156
-
188
+
157
189
  def mapping(options = {})
158
190
  {:open => 1, :closed => 2}.merge(options)
159
191
  end
160
-
192
+
161
193
  def ticket
162
194
  @ticket ||= Ticket.new
163
195
  end
164
-
196
+
165
197
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribute_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 3
9
+ - 1
10
+ version: 1.3.1
5
11
  platform: ruby
6
12
  authors:
7
13
  - Marcel Molina Jr.
@@ -38,21 +44,27 @@ rdoc_options:
38
44
  require_paths:
39
45
  - lib
40
46
  required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
41
48
  requirements:
42
49
  - - ">="
43
50
  - !ruby/object:Gem::Version
51
+ hash: 3
52
+ segments:
53
+ - 0
44
54
  version: "0"
45
- version:
46
55
  required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
47
57
  requirements:
48
58
  - - ">="
49
59
  - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
50
63
  version: "0"
51
- version:
52
64
  requirements: []
53
65
 
54
66
  rubyforge_project:
55
- rubygems_version: 1.3.5
67
+ rubygems_version: 1.3.7
56
68
  signing_key:
57
69
  specification_version: 3
58
70
  summary: Map symbolic types to primitive types and stash them in a column.