better_attrs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af8eff6df461e23c181ad4fdb54890deff900c7a
4
+ data.tar.gz: 48470107f367a89ead3367311bd54e55f8517cf2
5
+ SHA512:
6
+ metadata.gz: cb5c88f788985d41c6263be30b3990357f7da2a5235bfafa78afda881ab6bd3d3a4e6184f1ad86e156740fb02913422205d6e23dc44e6043effe764d8315901e
7
+ data.tar.gz: df297bbf889bcc83fde1247e3e8f555aec95a4f0d053c281e540eb9acc7b55c40ab8c68351338c4ef9d0a3334e28b1b1ec1d85aec320cb9cb8758f172b19baac
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .ruby-gemset
6
+ .ruby-version
7
+ coverage
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ tags
15
+ tags.*
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+
20
+ # YARD artifacts
21
+ .yardoc
22
+ _yardoc
23
+ doc/
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+ - 2.1.1
8
+ - 2.1.2
9
+ - 2.1.3
10
+ - 2.1.4
11
+ - 2.1.5
12
+ - 2.2.0
13
+ - jruby-19mode
14
+ - rbx-2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Jonathan W. Zaleski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # BetterAttrs
2
+
3
+ [![Build Status](https://secure.travis-ci.org/jzaleski/better_attrs.png?branch=master)](http://travis-ci.org/jzaleski/better_attrs)
4
+
5
+ Enhances `attr_accessor` and `attr_writer` to allow the specification of a callback to be invoked when an attribute value is changed.
6
+
7
+ Ideal for cascading updates or deployment scenarios where it is necessary to keep legacy values in sync until a time that they can be safely removed.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'better_attrs'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install better_attrs
22
+
23
+ ## Usage
24
+
25
+ The callback key is the attribute name, suffixed by either `_changed` or `_updated` (both behave identically).
26
+
27
+ In the examples below the callback (specified as `:foo_changed`) will be invoked when the value of `foo` changes.
28
+
29
+ A callback can be:
30
+
31
+ * a `lambda`
32
+ * a `proc`
33
+ * a `String` or `Symbol` (referring to a method)
34
+
35
+ In all cases the callback *must* be callable and *must* accept two arguments (the `old_value` and the `new_value` of the related attribute).
36
+
37
+ Adding a callback to an `attr_accessor` in new or existing class:
38
+
39
+ ```ruby
40
+ class AnyClass
41
+ attr_accessor :foo, :foo_changed => proc { |old_value, new_value| }
42
+ end
43
+ ```
44
+
45
+ Adding a callback to an `attr_writer` in a new or existing class:
46
+
47
+ ```ruby
48
+ class AnyClass
49
+ attr_writer :foo, :foo_changed => proc { |old_value, new_value| }
50
+ end
51
+ ```
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it (http://github.com/jzaleski/better_attrs/fork)
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec) do |task|
6
+ task.rspec_opts = %w[--format progress]
7
+ end
8
+
9
+ task :test => :spec
10
+
11
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'better_attrs/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'better_attrs'
8
+ gem.version = BetterAttrs::VERSION
9
+ gem.authors = ['Jonathan W. Zaleski']
10
+ gem.email = ['JonathanZaleski@gmail.com']
11
+ gem.summary = 'Enhances `attr_accessor` and `attr_writer` to allow the specification of a callback to be invoked when an attribute value is changed.'
12
+ gem.description = "#{gem.summary} Ideal for cascading updates or deployment scenarios where it is necessary to keep legacy values in sync until a time that they can be safely removed."
13
+ gem.homepage = 'https://github.com/jzaleski/better_attrs'
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |file| File.basename(file) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ['lib']
20
+
21
+ gem.add_development_dependency 'bundler', '~> 1.0'
22
+ gem.add_development_dependency 'pry', '~> 0.10'
23
+ gem.add_development_dependency 'rake', '~> 10.4'
24
+ gem.add_development_dependency 'rspec', '~> 3.1'
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'better_attrs/class'
2
+ require 'better_attrs/version'
@@ -0,0 +1,70 @@
1
+ class Class
2
+ private
3
+
4
+ alias_method :original_attr_accessor, :attr_accessor
5
+
6
+ def attr_accessor(*args)
7
+ attr_reader *extract_attrs_from_args(args)
8
+ attr_writer *args
9
+ end
10
+
11
+ alias_method :original_attr_writer, :attr_writer
12
+
13
+ def attr_writer(*args)
14
+ attrs = extract_attrs_from_args(args)
15
+ opts = extract_opts_from_args(args)
16
+ attrs.each { |attr| define_attr_writer(attr, opts) }
17
+ end
18
+
19
+ def callback_for_attr(attr, opts)
20
+ opts[:"#{attr}_changed"] || opts[:"#{attr}_updated"]
21
+ end
22
+
23
+ def define_attr_writer(attr, opts)
24
+ class_eval do
25
+ # Memoized state
26
+ instance_variable = instance_variable_for_attr(attr)
27
+ callback = callback_for_attr(attr, opts)
28
+ writer_method = writer_method_for_attr(attr)
29
+
30
+ define_method(writer_method) do |new_value|
31
+ # Capture the `old_value`
32
+ old_value = instance_variable_get(instance_variable)
33
+
34
+ # Short-circuit if the values are the same
35
+ return new_value if old_value == new_value
36
+
37
+ # Update the instance-variable
38
+ instance_variable_set(instance_variable, new_value)
39
+
40
+ # Update the attributes `Hash` if it is present
41
+ send(:[]=, attr, new_value) if defined?(::Rails) && respond_to?(:attributes)
42
+
43
+ # Short-circuit if no callback was specified
44
+ return new_value if callback.nil?
45
+
46
+ # Callable object (e.g. `Proc`, `Lambda`)
47
+ instance_exec(old_value, new_value, &callback) if callback.respond_to?(:call)
48
+
49
+ # Method name (must be `String` or `Symbol`)
50
+ send(callback, old_value, new_value) if [String, Symbol].include?(callback.class)
51
+ end
52
+ end
53
+ end
54
+
55
+ def extract_attrs_from_args(args)
56
+ args.last.is_a?(Hash) ? args[0..-2] : args
57
+ end
58
+
59
+ def extract_opts_from_args(args)
60
+ args.last.is_a?(Hash) ? args.last : {}
61
+ end
62
+
63
+ def instance_variable_for_attr(attr)
64
+ :"@#{attr}"
65
+ end
66
+
67
+ def writer_method_for_attr(attr)
68
+ :"#{attr}="
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module BetterAttrs
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ class AttrAccessor; end
4
+
5
+ describe AttrAccessor do
6
+ let(:klass) { subject.class }
7
+
8
+ it 'will accept a `lambda` as a valid callback' do
9
+ expect do
10
+ klass.class_eval do
11
+ attr_accessor :foo, :foo_changed => lambda { |old_value, new_value| }
12
+ end
13
+ end.to_not raise_error
14
+
15
+ expect(subject).to respond_to(:foo=)
16
+ expect(subject).to respond_to(:foo)
17
+ end
18
+
19
+ it 'will accept a `proc` as a valid callback' do
20
+ expect do
21
+ klass.class_eval do
22
+ attr_accessor :foo, :foo_changed => proc { |old_value, new_value| }
23
+ end
24
+ end.to_not raise_error
25
+
26
+ expect(subject).to respond_to(:foo=)
27
+ expect(subject).to respond_to(:foo)
28
+ end
29
+
30
+ it 'will accept a `String` as a valid callback' do
31
+ expect do
32
+ klass.class_eval do
33
+ attr_accessor :foo, :foo_changed => 'foo_changed'
34
+ end
35
+ end.to_not raise_error
36
+
37
+ expect(subject).to respond_to(:foo=)
38
+ expect(subject).to respond_to(:foo)
39
+ end
40
+
41
+ it 'will accept a `Symbol` as a valid callback' do
42
+ expect do
43
+ klass.class_eval do
44
+ attr_accessor :foo, :foo_changed => :foo_changed
45
+ end
46
+ end.to_not raise_error
47
+
48
+ expect(subject).to respond_to(:foo=)
49
+ expect(subject).to respond_to(:foo)
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ class AttrWriter; end
4
+
5
+ describe AttrWriter do
6
+ let(:klass) { subject.class }
7
+
8
+ it 'will accept a `lambda` as a valid callback' do
9
+ expect do
10
+ klass.class_eval do
11
+ attr_writer :foo, :foo_changed => lambda { |old_value, new_value| }
12
+ end
13
+ end.to_not raise_error
14
+
15
+ expect(subject).to respond_to(:foo=)
16
+ expect(subject).not_to respond_to(:foo)
17
+ end
18
+
19
+ it 'will accept a `proc` as a valid callback' do
20
+ expect do
21
+ klass.class_eval do
22
+ attr_writer :foo, :foo_changed => proc { |old_value, new_value| }
23
+ end
24
+ end.to_not raise_error
25
+
26
+ expect(subject).to respond_to(:foo=)
27
+ expect(subject).not_to respond_to(:foo)
28
+ end
29
+
30
+ it 'will accept a `String` as a valid callback' do
31
+ expect do
32
+ klass.class_eval do
33
+ attr_writer :foo, :foo_changed => 'foo_changed'
34
+ end
35
+ end.to_not raise_error
36
+
37
+ expect(subject).to respond_to(:foo=)
38
+ expect(subject).not_to respond_to(:foo)
39
+ end
40
+
41
+ it 'will accept a `Symbol` as a valid callback' do
42
+ expect do
43
+ klass.class_eval do
44
+ attr_writer :foo, :foo_changed => :foo_changed
45
+ end
46
+ end.to_not raise_error
47
+
48
+ expect(subject).to respond_to(:foo=)
49
+ expect(subject).not_to respond_to(:foo)
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ class SimpleAttrAccessor
4
+ attr_accessor :foo, :bar, :foo_changed => :foo_changed
5
+
6
+ def foo_changed(old_value, new_value); end
7
+ end
8
+
9
+ describe SimpleAttrAccessor do
10
+ it 'will call `foo_changed` when `foo` is updated' do
11
+ expect(subject).to receive(:foo_changed).with(nil, 2)
12
+
13
+ subject.foo = 2
14
+ end
15
+
16
+ it 'will not call `foo_changed` when `bar` is updated' do
17
+ expect(subject).not_to receive(:foo_changed)
18
+
19
+ subject.bar = 2
20
+ end
21
+
22
+ it 'will not call `foo_changed` if `old_value` and `new_value` are the same' do
23
+ subject.foo = 2
24
+
25
+ expect(subject).not_to receive(:foo_changed)
26
+
27
+ subject.foo = 2
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ class SimpleAttrWriter
4
+ attr_accessor :foo, :bar, :foo_changed => :foo_changed
5
+
6
+ def foo_changed(old_value, new_value); end
7
+ end
8
+
9
+ describe SimpleAttrWriter do
10
+ it 'will call `foo_changed` when `foo` is updated' do
11
+ expect(subject).to receive(:foo_changed).with(nil, 2)
12
+
13
+ subject.foo = 2
14
+ end
15
+
16
+ it 'will not call `foo_changed` when `bar` is updated' do
17
+ expect(subject).not_to receive(:foo_changed)
18
+
19
+ subject.bar = 2
20
+ end
21
+
22
+ it 'will not call `foo_changed` if `old_value` and `new_value` are the same' do
23
+ subject.foo = 2
24
+
25
+ expect(subject).not_to receive(:foo_changed)
26
+
27
+ subject.foo = 2
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+
3
+ require File.expand_path('../../lib/better_attrs.rb', __FILE__)
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: better_attrs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan W. Zaleski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.1'
69
+ description: Enhances `attr_accessor` and `attr_writer` to allow the specification
70
+ of a callback to be invoked when an attribute value is changed. Ideal for cascading
71
+ updates or deployment scenarios where it is necessary to keep legacy values in sync
72
+ until a time that they can be safely removed.
73
+ email:
74
+ - JonathanZaleski@gmail.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - better_attrs.gemspec
86
+ - lib/better_attrs.rb
87
+ - lib/better_attrs/class.rb
88
+ - lib/better_attrs/version.rb
89
+ - spec/attr_accessor_spec.rb
90
+ - spec/attr_writer_spec.rb
91
+ - spec/simple_attr_accessor_spec.rb
92
+ - spec/simple_attr_writer_spec.rb
93
+ - spec/spec_helper.rb
94
+ homepage: https://github.com/jzaleski/better_attrs
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.4.3
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Enhances `attr_accessor` and `attr_writer` to allow the specification of
118
+ a callback to be invoked when an attribute value is changed.
119
+ test_files:
120
+ - spec/attr_accessor_spec.rb
121
+ - spec/attr_writer_spec.rb
122
+ - spec/simple_attr_accessor_spec.rb
123
+ - spec/simple_attr_writer_spec.rb
124
+ - spec/spec_helper.rb