acts_as_comparable 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ # Copyright (c) 2007 Mark Van Holstyn, Zach Dennis
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ acts_as_comparable
2
+ -------------------
3
+
4
+ acts_as_comparable allows you to compare ActiveRecord models.
5
+ To test or build documentation see the rake tasks available.
6
+ rake -T
7
+
8
+
9
+ TESTING
10
+ -------
11
+ Testing acts_as_comparable relies on:
12
+ * ActiveRecord (any version, tested up to 1.15.3)
13
+ * Mocha 0.4.0 or higher (tested with 0.4.0)
14
+ * Ruby 1.8.4, 1.8.5 or 1.8.6
15
+
16
+ SVN
17
+ ----
18
+ Public anonymous read svn:
19
+ http://rails.lotswholetime.com/svn/acts_as_comparable/trunk
20
+
21
+ AUTHORS
22
+ -------
23
+ * Mark Van Holstyn - http://www.lotswholetime.com
24
+ * Zach Dennis - http://www.continuousthinking.com
25
+
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc "Default Task"
6
+ task :default => :test
7
+
8
+ desc "Test acts_as_comparable"
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc "Generate documentation for acts_as_comparable"
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = "rdoc"
18
+ rdoc.title = "acts_as_comparable"
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join( File.dirname( __FILE__ ), 'lib', 'acts_as_comparable' )
@@ -0,0 +1,170 @@
1
+ # == acts_as_comparable
2
+ # acts_as_comparable provides comparison to ActiveRecord models.
3
+ # It allows you to capture what fields are different, as well as
4
+ # easy access to those diferrent values.
5
+ #
6
+ # class MyModel < ActiveRecord::Base
7
+ # acts_as_comparable
8
+ # end
9
+ #
10
+ #
11
+ # == EXAMPLES
12
+ #
13
+ # # default options which treats all fields as comparable
14
+ # class Person < ActiveRecord::Base
15
+ # acts_as_comparable
16
+ # end
17
+ #
18
+ # # specifying which fields that should act as comparable
19
+ # class Person < ActiveRecord::Base
20
+ # acts_as_comparable :only => [ :first_name, :last_name, :age ]
21
+ # end
22
+ #
23
+ #
24
+ # # specifying which fields shouldn't act as comparable
25
+ # class Person < ActiveRecord::Base
26
+ # acts_as_comparable :except => [ :eyecolor ]
27
+ # end
28
+ #
29
+ #
30
+ # # specifying fields which compare to another field on the same model
31
+ # class Account < ActiveRecord::Base
32
+ # acts_as_comparable :attrs_map => { :accountno => :parent_accountno }
33
+ # end
34
+ #
35
+ # See unit tests for more usage examples.
36
+ #
37
+ # == AUTHORS
38
+ # * Mark Van Holstyn http://www.lotswholetime.com
39
+ # * Zach Dennis http://www.continuousthinking.com
40
+ #
41
+ module ActiveRecord::Acts::Comparable
42
+ def self.included( base )
43
+ base.extend ClassMethods
44
+ end
45
+
46
+ module ClassMethods
47
+
48
+ # Sets up the current model to be comparable against other models or instances.
49
+ #
50
+ # == Options
51
+ # * :only - an array of fields as symbols which should be held onto for comparison
52
+ # * :except - an array of fields as symbols which should not be held onto for comparison
53
+ # * :attrs_map - a hash of field to field mappings as symbols which define how fields should be compared
54
+ def acts_as_comparable( options = {} )
55
+ #pullin in necessary methods
56
+ cattr_accessor :comparable_attributes, :comparable_options
57
+ include ActiveRecord::Acts::Comparable::InstanceMethods
58
+
59
+ #setup options
60
+ self.comparable_options = options
61
+ self.comparable_attributes = comparable_options_to_attributes_hash options
62
+ end
63
+
64
+ private
65
+
66
+ def comparable_options_to_attributes_hash( options )
67
+ #setup attributes
68
+ attrs = {}
69
+
70
+ if options[:only]
71
+ options[:only].to_a.each { |attr| attrs[attr.to_sym] = attr.to_sym }
72
+ else
73
+ self.column_names.each { |attr| attrs[attr.to_sym] = attr.to_sym }
74
+ end
75
+
76
+ if options[:attrs_map]
77
+ options[:attrs_map].each_pair do |attr,action|
78
+ attrs[attr.to_sym] = action
79
+ end
80
+ end
81
+
82
+ if options[:except]
83
+ options[:except].to_a.each { |attr| attrs.delete attr.to_sym }
84
+ end
85
+
86
+ attrs
87
+ end
88
+ end
89
+
90
+ module InstanceMethods
91
+
92
+ # Returns true if the passed in +other+ model is different then this
93
+ # model given the fields that are marked as comparable. Otherwise
94
+ # returns false.
95
+ #
96
+ # == Arguments
97
+ # * other - another ActiveRecord model to compare this model against
98
+ # * options - a hash of options. All options for ActiveRecord::Acts::Comparable::ClassMethods.acts_as_comparable apply.
99
+ #
100
+ def different?( other = nil, options = nil )
101
+ other ||= self.new_record? ? self.class.new : self.class.find( self.id )
102
+ comparable_attributes_for( options ).each_pair do |attr, action|
103
+ if action.is_a? Proc
104
+ return true unless action.call( self ) == action.call( other )
105
+ else
106
+ return true unless self.send( attr ) == other.send( attr )
107
+ end
108
+ end
109
+ false
110
+ end
111
+
112
+ # Returns true if the passed in +other+ model is comparable to this
113
+ # model given the fields that are marked as comparable. Otherwise
114
+ # returns false.
115
+ #
116
+ # == Arguments
117
+ # * other - another ActiveRecord model to compare this model against
118
+ # * options - a hash of options. All options for ActiveRecord::Acts::Comparable::ClassMethods.acts_as_comparable apply.
119
+ #
120
+ def same?( other = nil, options = nil )
121
+ not different?( other, options )
122
+ end
123
+
124
+ # Returns a hash of field to value mappings which signify the differences between
125
+ # this model object and the passed in +other+ model object given an optional
126
+ # set of options. Returns an empty hash if there are no comparable differences.
127
+ #
128
+ # The value of the field to value mappings is an array of the values that differ.
129
+ #
130
+ # == Arguments
131
+ # * other - another ActiveRecord model to compare this model against
132
+ # * options - a hash of options. All options for ActiveRecord::Acts::Comparable::ClassMethods.acts_as_comparable apply.
133
+ #
134
+ # == Example
135
+ #
136
+ # class Pet < ActiveRecord::Base ; end
137
+ #
138
+ # pet1 = Pet.new :id=>1, :name=>"dog", :value=>"Tiny"
139
+ # pet2 = Pet.new :id=>5, :name=>"cat", :value=>"Norm"
140
+ #
141
+ # differences = pet1.differences(pet2)
142
+ # # => {:value=>["Tiny", "Norm"], :name=>["dog", "cat"], :id=>[1, 5]}
143
+ #
144
+ def differences( other = nil, options = nil )
145
+ other ||= self.new_record? ? self.class.new : self.class.find( self.id )
146
+ diffs = {}
147
+ comparable_attributes_for( options ).each_pair do |attr, action|
148
+ unless self.send( attr ) == other.send( attr )
149
+ if action.is_a? Proc
150
+ diffs[attr] = [ action.call( self ), action.call( other ) ]
151
+ else
152
+ diffs[attr] = [ self.send( attr ), other.send( attr ) ]
153
+ end
154
+ end
155
+ end
156
+ diffs.symbolize_keys
157
+ end
158
+
159
+ # Returns an hash of comparable attribute mappings given the passed in options.
160
+ #
161
+ # == Arguments
162
+ # * options - a hash of options. All options for ActiveRecord::Acts::Comparable::ClassMethods.acts_as_comparable apply.
163
+ def comparable_attributes_for( options )
164
+ attrs = options ? self.class.send( :comparable_options_to_attributes_hash, options ) : self.class.comparable_attributes
165
+ end
166
+ end
167
+
168
+ end
169
+
170
+ ActiveRecord::Base.send( :include, ActiveRecord::Acts::Comparable )
@@ -0,0 +1,136 @@
1
+ require File.join( File.dirname( __FILE__ ), "test_helper")
2
+
3
+ class ActsAsComparableTest < Test::Unit::TestCase
4
+
5
+ def test_models_are_different_when_both_records_are_new
6
+ model1 = MockModel.new :name=>"dog"
7
+ model2 = MockModel.new :name=>"cat"
8
+
9
+ assert model1.different?(model2), "models should have been different!"
10
+ assert !model1.same?(model2), "models shouldn't have been the same!"
11
+ end
12
+
13
+ def test_models_are_different_when_one_records_is_new
14
+ model1 = MockModel.new :name=>"dog"
15
+ model1.instance_variable_set "@new_record", false
16
+
17
+ model2 = MockModel.new :name=>"cat"
18
+
19
+ assert model1.different?(model2), "models should have been different!"
20
+ assert !model1.same?(model2), "models shouldn't have been the same!"
21
+ end
22
+
23
+ def test_models_are_different_when_neither_record_is_new
24
+ model1 = MockModel.new :name=>"dog"
25
+ model1.instance_variable_set "@new_record", false
26
+
27
+ model2 = MockModel.new :name=>"cat"
28
+ model2.instance_variable_set "@new_record", false
29
+
30
+ assert model1.different?(model2), "models should have been different!"
31
+ assert !model1.same?(model2), "models shouldn't have been the same!"
32
+ end
33
+
34
+ def test_models_are_not_different_when_both_records_are_new
35
+ model1 = MockModel.new :name=>"cat"
36
+ model2 = MockModel.new :name=>"cat"
37
+
38
+ assert model1.same?(model2), "models should have been the same!"
39
+ assert !model1.different?(model2), "models shouldn't have been different!"
40
+ end
41
+
42
+ def test_models_are_not_different_when_one_record_is_new
43
+ model1 = MockModel.new :name=>"cat"
44
+ model1.instance_variable_set "@new_record", false
45
+
46
+ model2 = MockModel.new :name=>"cat"
47
+
48
+ assert model1.same?(model2), "models should have been the same!"
49
+ assert !model1.different?(model2), "models shouldn't have been different!"
50
+ end
51
+
52
+ def test_models_are_not_different_when_neither_record_is_new
53
+ model1 = MockModel.new :name=>"cat"
54
+ model1.instance_variable_set "@new_record", false
55
+
56
+ model2 = MockModel.new :name=>"cat"
57
+ model2.instance_variable_set "@new_record", false
58
+
59
+ assert model1.same?(model2), "models should have been the same!"
60
+ assert !model1.different?(model2), "models shouldn't have been different!"
61
+ end
62
+
63
+ def test_differences_with_new_records
64
+ model1 = MockModel.new :id=>nil, :name=>"dog", :value=>"Tiny"
65
+ model2 = MockModel.new :id=>nil, :name=>"cat", :value=>"Norm"
66
+ model1.instance_variable_set "@new_record", false
67
+
68
+ differences = model1.differences(model2)
69
+
70
+ assert differences[:name] == [ "dog", "cat" ], "Name was expected to be different!"
71
+ assert differences[:value] == [ "Tiny", "Norm" ], "Value was expected to be different!"
72
+ assert 2 == differences.size, "Wrong number of differences!"
73
+ end
74
+
75
+ def test_differences_with_one_new_record
76
+ model1 = MockModel.new :id=>nil, :name=>"dog", :value=>"Tiny"
77
+ model2 = MockModel.new :id=>5, :name=>"cat", :value=>"Norm"
78
+ model2.instance_variable_set "@new_record", false
79
+
80
+ differences = model1.differences(model2)
81
+
82
+ assert differences[:id] == [ nil, 5 ], "ID was expected to be different!"
83
+ assert differences[:name] == [ "dog", "cat" ], "Name was expected to be different!"
84
+ assert differences[:value] == [ "Tiny", "Norm" ], "Value was expected to be different!"
85
+ assert 3 == differences.size, "Wrong number of differences!"
86
+ end
87
+
88
+ def test_differences_with_existing_records
89
+ model1 = MockModel.new :id=>1, :name=>"dog", :value=>"Tiny"
90
+ model1.instance_variable_set "@new_record", false
91
+
92
+ model2 = MockModel.new :id=>5, :name=>"cat", :value=>"Norm"
93
+ model1.instance_variable_set "@new_record", false
94
+
95
+ differences = model1.differences(model2)
96
+
97
+ assert differences[:id] == [ 1, 5 ], "ID was expected to be different!"
98
+ assert differences[:name] == [ "dog", "cat" ], "Name was expected to be different!"
99
+ assert differences[:value] == [ "Tiny", "Norm" ], "Value was expected to be different!"
100
+ assert 3 == differences.size, "Wrong number of differences!"
101
+ end
102
+
103
+ def test_comparable_attributes_for_without_options
104
+ comparable_attributes = MockModel.new.comparable_attributes_for( {} )
105
+ expected_comparable_attributes = { :id=>:id, :name=>:name, :value=>:value }
106
+
107
+ assert expected_comparable_attributes == comparable_attributes, "Expected attributes were not comparable!"
108
+ end
109
+
110
+ def test_comparable_attributes_for_with_only_option
111
+ comparable_attributes = MockModel.new.comparable_attributes_for(
112
+ :only=>[:value]
113
+ )
114
+ expected_comparable_attributes = { :value=>:value }
115
+
116
+ assert expected_comparable_attributes == comparable_attributes, "Expected attributes were not comparable!"
117
+ end
118
+
119
+ def test_comparable_attributes_for_with_except_option
120
+ comparable_attributes = MockModel.new.comparable_attributes_for(
121
+ :except=>[:value]
122
+ )
123
+ expected_comparable_attributes = { :id=>:id, :name=>:name }
124
+
125
+ assert expected_comparable_attributes == comparable_attributes, "Expected attributes were not comparable!"
126
+ end
127
+
128
+ def test_comparable_attributes_for_with_attrs_map_option
129
+ comparable_attributes = MockModel.new.comparable_attributes_for(
130
+ :attrs_map=> {:name=>:value, :value=>:id}
131
+ )
132
+ expected_comparable_attributes = { :id=>:id, :name=>:value, :value=>:id }
133
+ assert expected_comparable_attributes == comparable_attributes, "Expected attributes were not comparable!"
134
+ end
135
+
136
+ end
@@ -0,0 +1,33 @@
1
+ unless Object.const_defined?( :ActiveRecord )
2
+ begin
3
+ require 'active_record'
4
+ rescue LoadError => ex
5
+ require 'rubygems'
6
+ require 'active_record'
7
+ end
8
+ end
9
+
10
+ require File.join( File.dirname( __FILE__ ), '..', 'init' )
11
+ require 'test/unit'
12
+ require 'mocha'
13
+
14
+ ###### SETUP a partially mocked out Model for testing #######
15
+
16
+ class MockModel < ActiveRecord::Base
17
+ class << self
18
+ def column_names
19
+ %w[ id name value ]
20
+ end
21
+ end
22
+
23
+ acts_as_comparable
24
+
25
+ def initialize( attributes_hsh={} )
26
+ @attributes = { :id=>nil, :name=>"", :value=>"" }.merge( attributes_hsh )
27
+ @new_record = true
28
+
29
+ @attributes.keys.each do |column_name|
30
+ self.class.send( :define_method, column_name ) { @attributes[column_name] }
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: acts_as_comparable
5
+ version: !ruby/object:Gem::Version
6
+ version: "1.0"
7
+ date: 2007-04-20 00:00:00 -04:00
8
+ summary: Adds ActiveRecord model comparison functionality.
9
+ require_paths:
10
+ - lib
11
+ email: mvette13@gmail.com zach.dennis@gmail.com
12
+ homepage: http://www.continuousthinking.com/tags/acts_as_comparable
13
+ rubyforge_project: arext
14
+ description: Adds ActiveRecord model comparison functionality.
15
+ autorequire: acts_as_comparable.rb
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Mark Van Holstyn
31
+ - Zach Dennis
32
+ files:
33
+ - init.rb
34
+ - Rakefile
35
+ - LICENSE
36
+ - README
37
+ - lib/acts_as_comparable.rb
38
+ - test/acts_as_comparable_test.rb
39
+ - test/test_helper.rb
40
+ test_files: []
41
+
42
+ rdoc_options:
43
+ - --main
44
+ - README
45
+ extra_rdoc_files:
46
+ - README
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ requirements: []
52
+
53
+ dependencies:
54
+ - !ruby/object:Gem::Dependency
55
+ name: activerecord
56
+ version_requirement:
57
+ version_requirements: !ruby/object:Gem::Version::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.14.1
62
+ version: