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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 637013ac8fc1b1ed1e0bb79b28fd09caa2ec9172
4
- data.tar.gz: 708003b189efb2ef05eba73a960a35cd5190c5ef
3
+ metadata.gz: 98433cfcbbae56a6573fad6039f2ae0b9cceebe7
4
+ data.tar.gz: c25a0d2a1dbc32ce93aba8a29edff5d621fa3a7d
5
5
  SHA512:
6
- metadata.gz: 8d4999d86e3455f1ee8f1a711ba2165035ca7cf2bb6b8123a8a1b84b523235bc3561e9862a6ee870ccd28daedf3a277a0e22c128199e16016f74e5a1389635ba
7
- data.tar.gz: 580d2eda99d8b58e629062f99deb5a36fed68a83575ce9aa3e79d57e8bfe7d25287ce01723cfa3d1568d22206c4e69c4cfeaef41c20f5d631e99c184a85d3adf
6
+ metadata.gz: 101239965067ab75d3334fd76c4b3ec078c3d1d718ae1565556236382293b2653be72f9c6fe8e54a80439ca34a301731c090b441c15e0d2c7321c2ae2db6d9d6
7
+ data.tar.gz: 5d7de21a6d5f6252209b25ec4c39d77ad29da455b28582d050ab3c5900ed7ab86b28cd12fd8a2ace50747e8897986a2a47e1427c5bf2135c21ec1ee136a5598f
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ spec/database.yml
6
6
  Gemfile.lock
7
7
  *.sqlite3
8
8
  .DS_Store
9
+ vendor/bundle
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
 
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rails", "~> 3.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rails", "~> 4.0"
4
+
5
+ gemspec path: "../"
@@ -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
- def acts_as_splittable(options = {})
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
- self.splittable_options = options
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
- self.splittable_options = old.merge(other_options)
63
+ @splittable_options = old.merge(other_options)
47
64
  Proc.new.(splittable_options)
48
65
  ensure
49
- self.splittable_options = old
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!(name: column.to_sym)
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 splittable_options[:predicates]
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.splittable_options = splittable_options.dup
76
-
77
- if child.splittable_options[:split_on_change]
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
- define_method attribute do
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
- define_method :"#{attribute}=" do |value|
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
- define_method :"#{attribute}_changed?" do
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
- splittable_module.module_eval do
125
- define_method("#{name}=") do |value|
126
- if defined?(super)
127
- super(value)
128
- elsif respond_to?(:write_attribute, true)
129
- write_attribute name, value
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
- self.class.with_splittable_options join_on_change: false do
133
- split_column_values! name
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
- value = __send__(column) or next
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
- else
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
@@ -1,3 +1,3 @@
1
1
  module ActsAsSplittable
2
- VERSION = "0.0.7"
2
+ VERSION = "0.1.0"
3
3
  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
- class Splittable < ActiveRecord::Base
5
-
6
- acts_as_splittable predicates: true
7
-
8
- splittable :email, split: ['@', 2], attributes: [:email_local, :email_domain], on_join: EMAIL_JOIN_PROCESS
9
- splittable :postal_code, pattern: /\A(?<postal_code1>[0-9]{3})(?<postal_code2>[0-9]{4})\Z/
10
- splittable :phone_number, attributes: [:phone_number1, :phone_number2, :phone_number3], on_split: :split_phone_number, on_join: :join_phone_number
11
-
12
- protected
13
-
14
- def split_phone_number(value)
15
- return if value.nil?
16
- [value[0, 3], value[3, 4], value[7, 4]]
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
- def join_phone_number(values)
20
- values.join
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 '#*_changed?' do
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.email_local_changed?.should_not be_true
98
- @splittable.email_domain_changed?.should_not be_true
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.email_local_changed?.should be_true
101
+ @splittable.email_local_unsynced?.should be_true
102
102
 
103
103
  @splittable.email_domain = 'example.com'
104
- @splittable.email_domain_changed?.should be_true
104
+ @splittable.email_domain_unsynced?.should be_true
105
105
 
106
106
  @splittable.join_column_values!
107
107
 
108
- @splittable.email_local_changed?.should_not be_true
109
- @splittable.email_domain_changed?.should_not be_true
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.email_local_changed?.should be_true
112
+ @splittable.email_local_unsynced?.should be_true
113
113
 
114
114
  @splittable.email_domain = nil
115
- @splittable.email_domain_changed?.should be_true
115
+ @splittable.email_domain_unsynced?.should be_true
116
116
 
117
117
  @splittable.split_column_values!
118
118
 
119
- @splittable.email_local_changed?.should_not be_true
120
- @splittable.email_domain_changed?.should_not be_true
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: -> (value) {
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: -> (values) {
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
@@ -3,6 +3,7 @@ ActiveRecord::Schema.define version: 0 do
3
3
  create_table "splittables", force: true do |t|
4
4
  t.string "name"
5
5
  t.string "email"
6
+ t.string "email_sub"
6
7
  t.string "postal_code"
7
8
  t.string "phone_number"
8
9
  t.string "birthday_era"
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.7
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-06-26 00:00:00.000000000 Z
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