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 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