acts_as_splittable 0.0.7 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +13 -0
- data/README.md +38 -0
- data/gemfiles/rails3.gemfile +5 -0
- data/gemfiles/rails4.gemfile +5 -0
- data/lib/acts_as_splittable.rb +87 -39
- data/lib/acts_as_splittable/attributes.rb +76 -0
- data/lib/acts_as_splittable/config.rb +8 -0
- data/lib/acts_as_splittable/splittable.rb +18 -10
- data/lib/acts_as_splittable/splitter.rb +22 -4
- data/lib/acts_as_splittable/utility.rb +26 -0
- data/lib/acts_as_splittable/version.rb +1 -1
- data/spec/models.rb +48 -16
- data/spec/models/splittable_spec.rb +179 -14
- data/spec/schema.rb +1 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98433cfcbbae56a6573fad6039f2ae0b9cceebe7
|
4
|
+
data.tar.gz: c25a0d2a1dbc32ce93aba8a29edff5d621fa3a7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 101239965067ab75d3334fd76c4b3ec078c3d1d718ae1565556236382293b2653be72f9c6fe8e54a80439ca34a301731c090b441c15e0d2c7321c2ae2db6d9d6
|
7
|
+
data.tar.gz: 5d7de21a6d5f6252209b25ec4c39d77ad29da455b28582d050ab3c5900ed7ab86b28cd12fd8a2ace50747e8897986a2a47e1427c5bf2135c21ec1ee136a5598f
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- ruby-head
|
6
|
+
bundler_args: --path vendor/bundle
|
7
|
+
gemfile:
|
8
|
+
- gemfiles/rails3.gemfile
|
9
|
+
- gemfiles/rails4.gemfile
|
10
|
+
before_script:
|
11
|
+
- cp spec/database.yml.sample spec/database.yml
|
12
|
+
script:
|
13
|
+
- bundle exec rspec spec
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
ActsAsSplittable
|
2
2
|
====================
|
3
3
|
|
4
|
+
[](https://travis-ci.org/tatat/acts_as_splittable)
|
5
|
+
|
4
6
|
Create virtual attributes.
|
5
7
|
|
6
8
|
Installation
|
@@ -190,6 +192,42 @@ class Splittable < ActiveRecord::Base
|
|
190
192
|
end
|
191
193
|
```
|
192
194
|
|
195
|
+
### With ActiveModel::Dirty
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class Splittable < ActiveRecord::Base
|
199
|
+
acts_as_splittable dirty: true
|
200
|
+
splittable :email, delimiter: '@', attributes: [:email_local, :email_domain]
|
201
|
+
end
|
202
|
+
|
203
|
+
record = Splittable.find(id)
|
204
|
+
record.email_local = 'nyan'
|
205
|
+
|
206
|
+
p record.email_local_changed? #=> true
|
207
|
+
p record.email_domain_changed? #=> false
|
208
|
+
|
209
|
+
record.save!
|
210
|
+
|
211
|
+
p record.email_local_changed? #=> false
|
212
|
+
p record.splittable_attributes.class.include?(ActiveModel::Dirty) #=> true
|
213
|
+
```
|
214
|
+
|
215
|
+
Options
|
216
|
+
--------------------
|
217
|
+
|
218
|
+
You can change default options
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
ActsAsSplittable.default_options = {
|
222
|
+
callbacks: true,
|
223
|
+
predicates: false,
|
224
|
+
join_on_change: false,
|
225
|
+
split_on_change: false,
|
226
|
+
allow_nil: false,
|
227
|
+
dirty: false,
|
228
|
+
}
|
229
|
+
```
|
230
|
+
|
193
231
|
Contributing
|
194
232
|
--------------------
|
195
233
|
|
data/lib/acts_as_splittable.rb
CHANGED
@@ -1,28 +1,47 @@
|
|
1
1
|
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
2
|
|
3
3
|
require 'active_record'
|
4
|
+
require 'acts_as_splittable/utility'
|
4
5
|
require 'acts_as_splittable/splittable'
|
6
|
+
require 'acts_as_splittable/attributes'
|
5
7
|
require 'acts_as_splittable/splitter'
|
6
8
|
require 'acts_as_splittable/config'
|
7
9
|
|
8
10
|
module ActsAsSplittable
|
9
|
-
|
10
|
-
|
11
|
-
options.reverse_merge!(
|
11
|
+
class << self
|
12
|
+
DEFAULT_OPTIONS = {
|
12
13
|
callbacks: true,
|
13
14
|
predicates: false,
|
14
15
|
join_on_change: false,
|
15
16
|
split_on_change: false,
|
16
|
-
|
17
|
+
allow_nil: false,
|
18
|
+
dirty: false,
|
19
|
+
}
|
20
|
+
|
21
|
+
def default_options(*args)
|
22
|
+
case args.length
|
23
|
+
when 0
|
24
|
+
@default_options ||= DEFAULT_OPTIONS
|
25
|
+
when 1
|
26
|
+
self.default_options = args.first
|
27
|
+
else
|
28
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0..1)"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_options=(options)
|
33
|
+
default_options.merge! options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def acts_as_splittable(options = {})
|
38
|
+
options = options.reverse_merge ActsAsSplittable.default_options
|
17
39
|
|
18
40
|
extend ClassMethods
|
19
41
|
include Splittable
|
42
|
+
include splittable_module
|
20
43
|
|
21
|
-
|
22
|
-
|
23
|
-
if splittable_options[:split_on_change]
|
24
|
-
include splittable_module
|
25
|
-
end
|
44
|
+
@splittable_options = options
|
26
45
|
|
27
46
|
if splittable_options[:callbacks]
|
28
47
|
after_initialize { new_record? or split_column_values! }
|
@@ -35,35 +54,49 @@ module ActsAsSplittable
|
|
35
54
|
end
|
36
55
|
|
37
56
|
module ClassMethods
|
38
|
-
attr_writer :splittable_options
|
39
|
-
|
40
57
|
def splittable_options
|
41
58
|
@splittable_options ||= {}
|
42
59
|
end
|
43
60
|
|
44
61
|
def with_splittable_options(other_options = {})
|
45
62
|
old = splittable_options
|
46
|
-
|
63
|
+
@splittable_options = old.merge(other_options)
|
47
64
|
Proc.new.(splittable_options)
|
48
65
|
ensure
|
49
|
-
|
66
|
+
@splittable_options = old
|
50
67
|
end
|
51
68
|
|
52
69
|
def splittable_config
|
53
70
|
@splittable_config ||= Config.new
|
54
71
|
end
|
55
72
|
|
73
|
+
def splittable_attributes_class
|
74
|
+
@splittable_attributes_class ||= Attributes.child
|
75
|
+
end
|
76
|
+
|
56
77
|
def splittable(column, options)
|
57
|
-
options.merge
|
58
|
-
splitter = Splitter.new(options)
|
78
|
+
options = options.merge(name: column.to_sym)
|
79
|
+
splitter = Splitter.new(self, options)
|
80
|
+
|
59
81
|
splittable_config.splitters << splitter
|
60
82
|
|
61
83
|
splitter.attributes.each do |attribute|
|
84
|
+
splittable_attributes_class.define attribute
|
85
|
+
|
62
86
|
define_splittable_getter(attribute)
|
63
87
|
define_splittable_setter(attribute, splitter)
|
64
|
-
define_splittable_predicator(attribute) if
|
88
|
+
define_splittable_predicator(attribute) if splitter.predicates?
|
89
|
+
define_splittable_dirty(attribute) if splitter.dirty?
|
90
|
+
|
91
|
+
if splitter.predicates? and not splitter.dirty?
|
92
|
+
Utility.alias_methods_with_warning_for splittable_module do
|
93
|
+
alias_method :"#{attribute}_changed?", :"#{attribute}_unsynced?"
|
94
|
+
end
|
95
|
+
end
|
65
96
|
end
|
66
97
|
|
98
|
+
define_splittable_dirty_callbacks if splitter.dirty?
|
99
|
+
|
67
100
|
if splittable_options[:split_on_change]
|
68
101
|
define_splittable_setter_hook(splitter.name)
|
69
102
|
end
|
@@ -71,35 +104,33 @@ module ActsAsSplittable
|
|
71
104
|
|
72
105
|
def inherited(child)
|
73
106
|
super
|
74
|
-
|
75
|
-
child.
|
76
|
-
|
77
|
-
|
78
|
-
child.splittable_module = splittable_module.dup
|
79
|
-
child.send(:include, child.splittable_module)
|
80
|
-
end
|
81
|
-
|
107
|
+
child.__send__ :instance_variable_set, :@splittable_options, splittable_options.dup
|
108
|
+
child.__send__ :instance_variable_set, :@splittable_module, splittable_module.dup
|
109
|
+
child.__send__ :instance_variable_set, :@splittable_attributes_class, splittable_attributes_class.dup
|
110
|
+
child.__send__ :include, child.splittable_module
|
82
111
|
child.splittable_config.inherit! splittable_config
|
83
112
|
end
|
84
113
|
|
85
114
|
protected
|
86
115
|
|
87
|
-
attr_writer :splittable_module
|
88
|
-
|
89
116
|
def splittable_module
|
90
117
|
@splittable_module ||= Module.new
|
91
118
|
end
|
92
119
|
|
93
120
|
private
|
94
121
|
|
122
|
+
def define_splittable_method(name, &block)
|
123
|
+
splittable_module.__send__ :define_method, name, &block
|
124
|
+
end
|
125
|
+
|
95
126
|
def define_splittable_getter(attribute)
|
96
|
-
|
127
|
+
define_splittable_method attribute do
|
97
128
|
splittable_attributes[attribute]
|
98
129
|
end
|
99
130
|
end
|
100
131
|
|
101
132
|
def define_splittable_setter(attribute, splitter)
|
102
|
-
|
133
|
+
define_splittable_method :"#{attribute}=" do |value|
|
103
134
|
splittable_attributes[attribute] = value
|
104
135
|
|
105
136
|
unless splittable_changed_attribute? attribute
|
@@ -115,27 +146,44 @@ module ActsAsSplittable
|
|
115
146
|
end
|
116
147
|
|
117
148
|
def define_splittable_predicator(attribute)
|
118
|
-
|
149
|
+
define_splittable_method :"#{attribute}_unsynced?" do
|
119
150
|
splittable_changed_attribute? attribute
|
120
151
|
end
|
152
|
+
|
153
|
+
define_splittable_method :"#{attribute}_synced?" do
|
154
|
+
not __send__(:"#{attribute}_unsynced?")
|
155
|
+
end
|
121
156
|
end
|
122
157
|
|
123
158
|
def define_splittable_setter_hook(name)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
159
|
+
define_splittable_method "#{name}=" do |value|
|
160
|
+
if defined?(super)
|
161
|
+
super(value)
|
162
|
+
elsif respond_to?(:write_attribute, true)
|
163
|
+
write_attribute name, value
|
164
|
+
end
|
131
165
|
|
132
|
-
|
133
|
-
|
134
|
-
end
|
166
|
+
self.class.with_splittable_options join_on_change: false do
|
167
|
+
split_column_values! name
|
135
168
|
end
|
136
169
|
end
|
137
170
|
end
|
138
171
|
|
172
|
+
def define_splittable_dirty(attribute)
|
173
|
+
splittable_attributes_class.define_dirty_attribute_method attribute
|
174
|
+
|
175
|
+
%w(change changed? was will_change!).each do |suffix|
|
176
|
+
name = :"#{attribute}_#{suffix}"
|
177
|
+
define_splittable_method(name) {|*args| splittable_attributes.__send__ name, *args }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def define_splittable_dirty_callbacks
|
182
|
+
@splittable_dirty_callbacks_defined ||= true.tap{
|
183
|
+
after_initialize { splittable_attributes.reset! }
|
184
|
+
after_save { splittable_attributes.changed! }
|
185
|
+
}
|
186
|
+
end
|
139
187
|
end
|
140
188
|
end
|
141
189
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ActsAsSplittable
|
2
|
+
class Attributes < Hash
|
3
|
+
class << self
|
4
|
+
def child
|
5
|
+
Class.new Attributes
|
6
|
+
end
|
7
|
+
|
8
|
+
def dirty!
|
9
|
+
unless dirty?
|
10
|
+
include ActiveModel::Dirty
|
11
|
+
include Dirty
|
12
|
+
end
|
13
|
+
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def dirty?
|
18
|
+
include? ActiveModel::Dirty
|
19
|
+
end
|
20
|
+
|
21
|
+
def define(name)
|
22
|
+
name = name.to_sym
|
23
|
+
define_method(name) { self[name] }
|
24
|
+
end
|
25
|
+
|
26
|
+
def define_dirty_attribute_method(name)
|
27
|
+
dirty!.define_attribute_method name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def []=(key, value)
|
32
|
+
key = key.to_sym
|
33
|
+
__send__ :"#{key}_will_change!" if dirty? and key?(key) and value != self[key]
|
34
|
+
|
35
|
+
super key, value
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](key)
|
39
|
+
super key.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def dirty?
|
43
|
+
@dirty = self.class.dirty? if @dirty.nil?
|
44
|
+
@dirty
|
45
|
+
end
|
46
|
+
|
47
|
+
def changed!; super if defined?(super) end
|
48
|
+
def reset!; super if defined?(super) end
|
49
|
+
|
50
|
+
module Dirty
|
51
|
+
def changed!
|
52
|
+
if respond_to? :changes_applied, true
|
53
|
+
changes_applied
|
54
|
+
else
|
55
|
+
@previously_changed = changes
|
56
|
+
@changed_attributes = changed_attributes.class.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def reset!
|
61
|
+
if respond_to? :reset_changes, true
|
62
|
+
reset_changes
|
63
|
+
else
|
64
|
+
@previously_changed = changed_attributes.class.new
|
65
|
+
@changed_attributes = changed_attributes.class.new
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize_copy(original)
|
70
|
+
super
|
71
|
+
@previously_changed = original.__send__(:instance_variable_get, :@previously_changed).dup
|
72
|
+
@changed_attributes = original.__send__(:instance_variable_get, :@changed_attributes).dup
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -10,6 +10,14 @@ module ActsAsSplittable
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
def splitter_has_attribute(attribute)
|
14
|
+
attribute = attribute.to_sym
|
15
|
+
|
16
|
+
splitters.find do |splitter|
|
17
|
+
splitter.attributes.include?(attribute) or splitter.name == attribute
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def inherit!(other)
|
14
22
|
splitters.replace (other.splitters + splitters).uniq(&:name)
|
15
23
|
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
module ActsAsSplittable
|
2
2
|
module Splittable
|
3
|
-
|
4
3
|
def splittable_attributes
|
5
|
-
@splittable_attributes ||=
|
4
|
+
@splittable_attributes ||= self.class.splittable_attributes_class.new
|
6
5
|
end
|
7
6
|
|
8
|
-
alias_method :splittable_partials, :splittable_attributes
|
9
|
-
|
10
7
|
def split_column_values!(columns = nil)
|
11
8
|
splittable_aggregate_columns(columns) do |column, splitter|
|
12
|
-
|
9
|
+
begin
|
10
|
+
value = __send__(column)
|
11
|
+
rescue ActiveModel::MissingAttributeError
|
12
|
+
next
|
13
|
+
end
|
14
|
+
|
15
|
+
next if not splitter.allow_nil? and value.nil?
|
13
16
|
|
14
17
|
values = splitter.split(value, self)
|
15
18
|
splitter.attributes.zip(values).each do |key, value|
|
@@ -23,7 +26,7 @@ module ActsAsSplittable
|
|
23
26
|
def join_column_values!(columns = nil)
|
24
27
|
splittable_aggregate_columns(columns) do |column, splitter|
|
25
28
|
values = splitter.attributes.map {|partial| __send__(partial) }
|
26
|
-
next if values.include?(nil)
|
29
|
+
next if not splitter.allow_nil? and values.include?(nil)
|
27
30
|
|
28
31
|
__send__ :"#{column}=", splitter.restore(values, self)
|
29
32
|
reset_splittable_changed_attributes splitter.attributes
|
@@ -48,11 +51,8 @@ module ActsAsSplittable
|
|
48
51
|
self.splittable_changed_attributes -= attributes
|
49
52
|
end
|
50
53
|
|
51
|
-
alias_method :splittable_changed_partials, :splittable_changed_attributes
|
52
|
-
alias_method :splittable_changed_partial?, :splittable_changed_attribute?
|
53
|
-
alias_method :reset_splittable_changed_partials, :reset_splittable_changed_attributes
|
54
|
-
|
55
54
|
private
|
55
|
+
|
56
56
|
def splittable_aggregate_columns(columns = nil)
|
57
57
|
config = self.class.splittable_config
|
58
58
|
columns = columns ? Array(columns) : config.splitters.collect(&:name)
|
@@ -71,5 +71,13 @@ module ActsAsSplittable
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
Utility.alias_methods_with_warning_for self do
|
75
|
+
alias_method :splittable_partials, :splittable_attributes
|
76
|
+
alias_method :splittable_changed_partials, :splittable_changed_attributes
|
77
|
+
alias_method :splittable_changed_partial?, :splittable_changed_attribute?
|
78
|
+
alias_method :reset_splittable_changed_partials, :reset_splittable_changed_attributes
|
79
|
+
|
80
|
+
protected :splittable_changed_partials, :splittable_changed_partial?, :reset_splittable_changed_partials
|
81
|
+
end
|
74
82
|
end
|
75
83
|
end
|
@@ -11,18 +11,21 @@ module ActsAsSplittable
|
|
11
11
|
partials: :attributes,
|
12
12
|
}.freeze
|
13
13
|
|
14
|
-
def initialize(options = {})
|
15
|
-
@options = options
|
16
|
-
|
14
|
+
def initialize(klass, options = {})
|
17
15
|
options = DEFAULTS.merge(options)
|
18
16
|
delimiter = options.delete(:delimiter)
|
17
|
+
|
18
|
+
@klass = klass
|
19
|
+
@options = {}
|
19
20
|
|
20
21
|
options.each do |key, value|
|
21
22
|
case key
|
22
23
|
when *ALIASES.keys
|
23
24
|
self[ALIASES[key]] = value
|
24
|
-
|
25
|
+
when *members.map(&:to_sym)
|
25
26
|
self[key] = value
|
27
|
+
else
|
28
|
+
@options[key.to_sym] = value
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
@@ -51,6 +54,11 @@ module ActsAsSplittable
|
|
51
54
|
delegation(delegate || self, on_join, values)
|
52
55
|
end
|
53
56
|
|
57
|
+
def method_missing(name, *args)
|
58
|
+
super unless name.to_s[-1] == '?'
|
59
|
+
define_predicate_method_for_options(name)
|
60
|
+
end
|
61
|
+
|
54
62
|
private
|
55
63
|
def delegation(target, method, *args)
|
56
64
|
if method.is_a?(Proc)
|
@@ -75,5 +83,15 @@ module ActsAsSplittable
|
|
75
83
|
Object.private_method_defined?(name)
|
76
84
|
end || values
|
77
85
|
end
|
86
|
+
|
87
|
+
def define_predicate_method_for_options(name)
|
88
|
+
key = name.to_s.chop.to_sym
|
89
|
+
|
90
|
+
self.class.__send__ :define_method, name do
|
91
|
+
!! (@options.key?(key) ? @options[key] : @klass.splittable_options[key])
|
92
|
+
end
|
93
|
+
|
94
|
+
__send__ name
|
95
|
+
end
|
78
96
|
end
|
79
97
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActsAsSplittable
|
2
|
+
module Utility
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def alias_methods_with_warning_for(mod, &block)
|
6
|
+
AliasWithWarning.new(mod).instance_exec &block
|
7
|
+
end
|
8
|
+
|
9
|
+
def deprecation_warning_of_method_name(old_name, new_name)
|
10
|
+
warn "DEPRECATION WARNING: `#{old_name}' is deprecated. Please use `#{new_name}' instead."
|
11
|
+
end
|
12
|
+
|
13
|
+
class AliasWithWarning < Struct.new(:mod)
|
14
|
+
def alias_method(new_name, original_name)
|
15
|
+
mod.__send__ :define_method, new_name do |*args|
|
16
|
+
Utility.deprecation_warning_of_method_name new_name, original_name
|
17
|
+
__send__ original_name, *args
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(name, *args)
|
22
|
+
mod.__send__ name, *args
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/models.rb
CHANGED
@@ -1,24 +1,33 @@
|
|
1
1
|
EMAIL_SPLIT_PATTERN = /\A(?<email_local>[^@]+)@(?<email_domain>[^@]+)\Z/
|
2
2
|
EMAIL_JOIN_PROCESS = Proc.new{|values| values.join('@') }
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
4
|
+
module SplittableDefiner
|
5
|
+
def self.include_to(mod, options = {})
|
6
|
+
mod.module_exec do
|
7
|
+
self.table_name = 'splittables'
|
8
|
+
|
9
|
+
acts_as_splittable ({predicates: true}).merge(options)
|
10
|
+
|
11
|
+
splittable :email, split: ['@', 2], attributes: [:email_local, :email_domain], on_join: EMAIL_JOIN_PROCESS
|
12
|
+
splittable :postal_code, pattern: /\A(?<postal_code1>[0-9]{3})(?<postal_code2>[0-9]{4})\Z/
|
13
|
+
splittable :phone_number, attributes: [:phone_number1, :phone_number2, :phone_number3], on_split: :split_phone_number, on_join: :join_phone_number
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def split_phone_number(value)
|
18
|
+
return if value.nil?
|
19
|
+
[value[0, 3], value[3, 4], value[7, 4]]
|
20
|
+
end
|
21
|
+
|
22
|
+
def join_phone_number(values)
|
23
|
+
values.join
|
24
|
+
end
|
25
|
+
end
|
17
26
|
end
|
27
|
+
end
|
18
28
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
29
|
+
class Splittable < ActiveRecord::Base
|
30
|
+
SplittableDefiner.include_to self
|
22
31
|
end
|
23
32
|
|
24
33
|
class SplittableInherited < Splittable; end
|
@@ -81,3 +90,26 @@ class SplittableUseTypeCasting < ActiveRecord::Base
|
|
81
90
|
|
82
91
|
acts_as_hasty_splittable
|
83
92
|
end
|
93
|
+
|
94
|
+
class SplittableBase < ActiveRecord::Base
|
95
|
+
self.table_name = 'splittables'
|
96
|
+
|
97
|
+
def self.define_splittable(options = {})
|
98
|
+
acts_as_splittable options
|
99
|
+
splittable :email, delimiter: '@', attributes: [:email_local, :email_domain]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class SplittableNotAllowNil < SplittableBase
|
104
|
+
define_splittable
|
105
|
+
splittable :email_sub, delimiter: '@', attributes: [:email_sub_local, :email_sub_domain], allow_nil: true
|
106
|
+
end
|
107
|
+
|
108
|
+
class SplittableAllowNil < SplittableBase
|
109
|
+
define_splittable allow_nil: true
|
110
|
+
splittable :email_sub, delimiter: '@', attributes: [:email_sub_local, :email_sub_domain], allow_nil: false
|
111
|
+
end
|
112
|
+
|
113
|
+
class SplittableDirty < ActiveRecord::Base
|
114
|
+
SplittableDefiner.include_to self, dirty: true
|
115
|
+
end
|
@@ -88,44 +88,51 @@ end
|
|
88
88
|
|
89
89
|
shared_examples_for 'splittable with callbacks' do
|
90
90
|
|
91
|
-
describe '#*
|
91
|
+
describe '#*_unsynced?' do
|
92
92
|
before do
|
93
93
|
@splittable = described_class.new
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'should return true when value is changed until #split_column_values! or #join_column_values! is called' do
|
97
|
-
@splittable.
|
98
|
-
@splittable.
|
97
|
+
@splittable.email_local_unsynced?.should_not be_true
|
98
|
+
@splittable.email_domain_unsynced?.should_not be_true
|
99
99
|
|
100
100
|
@splittable.email_local = 'splittable'
|
101
|
-
@splittable.
|
101
|
+
@splittable.email_local_unsynced?.should be_true
|
102
102
|
|
103
103
|
@splittable.email_domain = 'example.com'
|
104
|
-
@splittable.
|
104
|
+
@splittable.email_domain_unsynced?.should be_true
|
105
105
|
|
106
106
|
@splittable.join_column_values!
|
107
107
|
|
108
|
-
@splittable.
|
109
|
-
@splittable.
|
108
|
+
@splittable.email_local_unsynced?.should_not be_true
|
109
|
+
@splittable.email_domain_unsynced?.should_not be_true
|
110
110
|
|
111
111
|
@splittable.email_local = nil
|
112
|
-
@splittable.
|
112
|
+
@splittable.email_local_unsynced?.should be_true
|
113
113
|
|
114
114
|
@splittable.email_domain = nil
|
115
|
-
@splittable.
|
115
|
+
@splittable.email_domain_unsynced?.should be_true
|
116
116
|
|
117
117
|
@splittable.split_column_values!
|
118
118
|
|
119
|
-
@splittable.
|
120
|
-
@splittable.
|
119
|
+
@splittable.email_local_unsynced?.should_not be_true
|
120
|
+
@splittable.email_domain_unsynced?.should_not be_true
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
124
|
end
|
125
125
|
|
126
|
+
shared_examples_for 'selectable' do
|
127
|
+
it "should select columns" do
|
128
|
+
expect { described_class.select([:id]).first }.not_to raise_error
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
126
132
|
describe Splittable do
|
127
133
|
it_behaves_like 'splittable'
|
128
134
|
it_behaves_like 'splittable with callbacks'
|
135
|
+
it_behaves_like 'selectable'
|
129
136
|
|
130
137
|
context 'when was given a proc to callbacks' do
|
131
138
|
it 'should call in the record' do
|
@@ -137,12 +144,12 @@ describe Splittable do
|
|
137
144
|
splittable :birthday, {
|
138
145
|
partials: [:birthday_year, :birthday_month, :birthday_day],
|
139
146
|
|
140
|
-
on_split: ->
|
147
|
+
on_split: ->(value) {
|
141
148
|
year, month, day = value.chars.each_slice(2).map(&:join).map(&:to_i)
|
142
149
|
[year + birthday_base, month, day]
|
143
150
|
},
|
144
151
|
|
145
|
-
on_join: ->
|
152
|
+
on_join: ->(values) {
|
146
153
|
year, month, day = values.map(&:to_i)
|
147
154
|
'%02d%02d%02d' % [year - birthday_base, month, day]
|
148
155
|
}
|
@@ -175,16 +182,19 @@ end
|
|
175
182
|
describe SplittableInherited do
|
176
183
|
it_behaves_like 'splittable'
|
177
184
|
it_behaves_like 'splittable with callbacks'
|
185
|
+
it_behaves_like 'selectable'
|
178
186
|
end
|
179
187
|
|
180
188
|
|
181
189
|
describe SplittableInheritedInherited do
|
182
190
|
it_behaves_like 'splittable'
|
183
191
|
it_behaves_like 'splittable with callbacks'
|
192
|
+
it_behaves_like 'selectable'
|
184
193
|
end
|
185
194
|
|
186
195
|
|
187
196
|
describe SplittableSplitOrJoinOnChange do
|
197
|
+
it_behaves_like 'selectable'
|
188
198
|
|
189
199
|
it 'should join partials when one of partials is set and all of them are not nil' do
|
190
200
|
splittable = SplittableSplitOrJoinOnChange.new
|
@@ -239,6 +249,7 @@ describe SplittableSplitOrJoinOnChange do
|
|
239
249
|
end
|
240
250
|
|
241
251
|
describe SplittableSplitOrJoinOnChangeWithAliasAttribute do
|
252
|
+
it_behaves_like 'selectable'
|
242
253
|
|
243
254
|
it 'should split value when value is set' do
|
244
255
|
splittable1 = SplittableSplitOrJoinOnChangeWithAliasAttribute.new(
|
@@ -262,6 +273,7 @@ describe SplittableSplitOrJoinOnChangeWithAliasAttribute do
|
|
262
273
|
end
|
263
274
|
|
264
275
|
describe SplittableUseDelimiter do
|
276
|
+
it_behaves_like 'selectable'
|
265
277
|
|
266
278
|
let (:splittable) do
|
267
279
|
SplittableUseDelimiter.new(email: 'splittable@example.com')
|
@@ -281,6 +293,7 @@ describe SplittableUseDelimiter do
|
|
281
293
|
end
|
282
294
|
|
283
295
|
describe SplittableUseTypeCasting do
|
296
|
+
it_behaves_like 'selectable'
|
284
297
|
|
285
298
|
shared_examples_for 'splittable typecasting' do
|
286
299
|
|
@@ -327,4 +340,156 @@ describe SplittableUseTypeCasting do
|
|
327
340
|
it_behaves_like 'splittable typecasting'
|
328
341
|
end
|
329
342
|
|
330
|
-
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe SplittableNotAllowNil do
|
346
|
+
it_behaves_like 'selectable'
|
347
|
+
|
348
|
+
it "should join columns" do
|
349
|
+
splittable = described_class.new(email_local: 'splittable', email_domain: 'example.com').join_column_values!
|
350
|
+
splittable.email.should_not be_nil
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should not join columns" do
|
354
|
+
splittable = described_class.new(email_local: 'splittable', email_domain: nil).join_column_values!
|
355
|
+
splittable.email.should be_nil
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should split columns" do
|
359
|
+
splittable = described_class.new(email: 'splittable@example.com').split_column_values!
|
360
|
+
splittable.email_local.should == 'splittable'
|
361
|
+
splittable.email_domain.should == 'example.com'
|
362
|
+
end
|
363
|
+
|
364
|
+
it "should not split columns" do
|
365
|
+
splittable = described_class.new(email: nil, email_local: 'splittable', email_domain: 'example.com').split_column_values!
|
366
|
+
splittable.email_local.should == 'splittable'
|
367
|
+
splittable.email_domain.should == 'example.com'
|
368
|
+
end
|
369
|
+
|
370
|
+
context "when option `allow_nil' is overridden." do
|
371
|
+
it "should join columns" do
|
372
|
+
splittable = described_class.new(email_sub_local: 'splittable', email_sub_domain: nil).join_column_values!
|
373
|
+
splittable.email_sub.should == 'splittable@'
|
374
|
+
end
|
375
|
+
|
376
|
+
it "should split columns" do
|
377
|
+
splittable = described_class.new(email_sub: nil, email_sub_local: 'splittable', email_sub_domain: 'example.com').split_column_values!
|
378
|
+
splittable.email_sub_local.should be_nil
|
379
|
+
splittable.email_sub_domain.should be_nil
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe SplittableAllowNil do
|
385
|
+
it_behaves_like 'selectable'
|
386
|
+
|
387
|
+
it "should join columns" do
|
388
|
+
splittable = described_class.new(email_local: 'splittable', email_domain: nil).join_column_values!
|
389
|
+
splittable.email.should == 'splittable@'
|
390
|
+
end
|
391
|
+
|
392
|
+
it "should split columns" do
|
393
|
+
splittable = described_class.new(email: nil, email_local: 'splittable', email_domain: 'example.com').split_column_values!
|
394
|
+
splittable.email_local.should be_nil
|
395
|
+
splittable.email_domain.should be_nil
|
396
|
+
end
|
397
|
+
|
398
|
+
context "when option `allow_nil' is overridden." do
|
399
|
+
it "should not join columns" do
|
400
|
+
splittable = described_class.new(email_sub_local: 'splittable', email_sub_domain: nil).join_column_values!
|
401
|
+
splittable.email_sub.should be_nil
|
402
|
+
end
|
403
|
+
|
404
|
+
it "should not split columns" do
|
405
|
+
splittable = described_class.new(email_sub: nil, email_sub_local: 'splittable', email_sub_domain: 'example.com').split_column_values!
|
406
|
+
splittable.email_sub_local.should == 'splittable'
|
407
|
+
splittable.email_sub_domain.should == 'example.com'
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
describe SplittableDirty do
|
413
|
+
it_behaves_like 'splittable'
|
414
|
+
it_behaves_like 'selectable'
|
415
|
+
|
416
|
+
let(:splittable_new) { described_class.new }
|
417
|
+
let(:splittable_saved) { described_class.create!(email_local: 'splittable', email_domain: 'example.com') }
|
418
|
+
|
419
|
+
describe "*_change, *_was, *_changed?" do
|
420
|
+
context "new record" do
|
421
|
+
it "should be unchanged" do
|
422
|
+
splittable_new.email_local_change.should be_nil
|
423
|
+
splittable_new.email_local_was.should be_nil
|
424
|
+
splittable_new.email_local_changed?.should be_false
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should be unchanged first" do
|
428
|
+
splittable_new.email_local = 'first value'
|
429
|
+
splittable_new.email_local_change.should be_nil
|
430
|
+
splittable_new.email_local_was.should == 'first value'
|
431
|
+
splittable_new.email_local_changed?.should be_false
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should be changed from the second time" do
|
435
|
+
splittable_new.email_local = 'first value'
|
436
|
+
splittable_new.email_local = 'second value'
|
437
|
+
|
438
|
+
splittable_new.email_local_change.should == ['first value', 'second value']
|
439
|
+
splittable_new.email_local_was.should == 'first value'
|
440
|
+
splittable_new.email_local_changed?.should be_true
|
441
|
+
|
442
|
+
splittable_new.email_local = 'third value'
|
443
|
+
|
444
|
+
splittable_new.email_local_change.should == ['first value', 'third value']
|
445
|
+
splittable_new.email_local_was.should == 'first value'
|
446
|
+
splittable_new.email_local_changed?.should be_true
|
447
|
+
end
|
448
|
+
|
449
|
+
it "should not ignore if first value is nil" do
|
450
|
+
splittable_new.email_local = nil
|
451
|
+
splittable_new.email_local = 'second value'
|
452
|
+
|
453
|
+
splittable_new.email_local_change.should == [nil, 'second value']
|
454
|
+
splittable_new.email_local_was.should be_nil
|
455
|
+
splittable_new.email_local_changed?.should be_true
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
context "existent record" do
|
460
|
+
it "should be unchanged" do
|
461
|
+
splittable_saved.email_local.should == 'splittable'
|
462
|
+
splittable_saved.email_local_change.should be_nil
|
463
|
+
splittable_saved.email_local_was.should == 'splittable'
|
464
|
+
splittable_saved.email_local_changed?.should be_false
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should be changed" do
|
468
|
+
splittable_saved.email_local = 'splittable2'
|
469
|
+
splittable_saved.email_local_change.should == ['splittable', 'splittable2']
|
470
|
+
splittable_saved.email_local_was.should == 'splittable'
|
471
|
+
splittable_saved.email_local_changed?.should be_true
|
472
|
+
end
|
473
|
+
|
474
|
+
it "should ignore if it is not new record" do
|
475
|
+
record = described_class.where(email: splittable_saved.email).first
|
476
|
+
record.email_local_change.should be_nil
|
477
|
+
record.email_local_was.should == splittable_saved.email_local
|
478
|
+
record.email_local_changed?.should be_false
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
it "should be reset after save" do
|
483
|
+
splittable_saved.email_local = 'splittable2'
|
484
|
+
|
485
|
+
splittable_saved.email_local_changed?.should be_true
|
486
|
+
|
487
|
+
splittable_saved.save!
|
488
|
+
|
489
|
+
splittable_saved.email_local_changed?.should be_false
|
490
|
+
splittable_saved.splittable_attributes.previous_changes.should == {'email_local' => ['splittable', 'splittable2']}
|
491
|
+
|
492
|
+
splittable_saved.tap(&:save!).splittable_attributes.previous_changes.should be_empty
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
data/spec/schema.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_splittable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tatat
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-11-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -85,15 +85,20 @@ extra_rdoc_files: []
|
|
85
85
|
files:
|
86
86
|
- .gitignore
|
87
87
|
- .rspec
|
88
|
+
- .travis.yml
|
88
89
|
- Gemfile
|
89
90
|
- MIT-LICENSE
|
90
91
|
- README.md
|
91
92
|
- Rakefile
|
92
93
|
- acts_as_splittable.gemspec
|
94
|
+
- gemfiles/rails3.gemfile
|
95
|
+
- gemfiles/rails4.gemfile
|
93
96
|
- lib/acts_as_splittable.rb
|
97
|
+
- lib/acts_as_splittable/attributes.rb
|
94
98
|
- lib/acts_as_splittable/config.rb
|
95
99
|
- lib/acts_as_splittable/splittable.rb
|
96
100
|
- lib/acts_as_splittable/splitter.rb
|
101
|
+
- lib/acts_as_splittable/utility.rb
|
97
102
|
- lib/acts_as_splittable/version.rb
|
98
103
|
- lib/tasks/acts_as_splittable_tasks.rake
|
99
104
|
- spec/database.yml.sample
|