acts_as_splittable 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/tatat/acts_as_splittable.png)](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
|