acts_as_diffable 0.0.1

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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 James Mason & The SUSE Studio Team @ NOVELL/SUSE
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,56 @@
1
+ ActsAsDiffable provides a dead-simple way to compare two instances of a class,
2
+ including any or all associations, or more complex relationships.
3
+
4
+ The return is a hash*, suitable for digestion by case-based textualizers,
5
+ JSON processors, etc., in the form { 'attribute' => [from, to] }
6
+ * In the instance of no changes, a nil is returned
7
+
8
+ == Usage
9
+
10
+ class Foo < ActiveRecord::Base
11
+ acts_as_diffable
12
+ end
13
+ > Foo.first.diff(Foo.last) => { 'bar' => ['foo', nil] }
14
+
15
+ === Associations
16
+
17
+ For plural associations, a :diff_key option needs to be added to the association,
18
+ defining how to relate disparate instances within each parent's collections.
19
+ This can be a single field or a collection of fields in an array, and will be
20
+ expressed as the key side of a hash with the value being the hash of attribute
21
+ differences.
22
+
23
+ For singular association, there is no need to specify a way to organize and
24
+ compare, so we only need to express which associations to include in the diff,
25
+ by adding a :diff option to the association that evaluates to true.
26
+
27
+ class Foo < ActiveRecord::Base
28
+ acts_as_diffable
29
+
30
+ has_one :bar, :diff => true
31
+ has_many :fish, :diff_keypattern => :name
32
+ has_and_belongs_to_many :users, :diff_keypattern => [:firstname, :lastname]
33
+ end
34
+ > Foo.first.diff(Foo.last)
35
+ => { 'bar' => { 'attr1' => ['a', 'b'],
36
+ 'attr2' => [14, nil] },
37
+ 'fish => { 'nemo' => {'fish_attr1' => [nil, 'zip'] },
38
+ 'goldie' => {'fish_attr1' => ['zap', 'zop'] } },
39
+ 'users' => { ['Jane', 'Doe'] => { 'firstname' => 'Jane',
40
+ 'lastname' => 'Doe' },
41
+ ['John', 'Doe'] => { '_deleted' => true } } }
42
+
43
+ === More complex relationships
44
+
45
+ In addition to the marked associations, any method on the class that returns an
46
+ ActiveRecord-ish object can be included in the diff by adding a
47
+ manual_diff_definiton. For comparing collections of ActiveRecord objects,
48
+ use the form:
49
+
50
+ manual_diff_definition :name, :eval => 'instance_eval_code',
51
+ :diff_key => [:key, :pattern]
52
+
53
+ For simpler singular comparisons, omit the diff_key option.
54
+
55
+
56
+ Have a lot of fun!
@@ -0,0 +1,16 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "acts_as_diffable"
5
+ gem.summary = "Compare two instances of an ActiveRecord::Base class."
6
+ gem.description = "ActsAsDiffable provides a dead-simple way to compare two instances of a class, including any or all associations, or more complex relationships."
7
+ gem.email = "jmason@suse.com"
8
+ gem.homepage = "https://github.com/bear454/ActsAsDiffable"
9
+ gem.authors = ["James Mason 'bear454'"]
10
+ gem.add_dependency "activerecord", ">=2.3.14"
11
+ end
12
+ Jeweler::RubygemsDotOrgTasks.new
13
+ rescue LoadError
14
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
15
+ end
16
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "acts_as_diffable"
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["James Mason 'bear454'"]
12
+ s.date = "2012-04-19"
13
+ s.description = "ActsAsDiffable provides a dead-simple way to compare two instances of a class, including any or all associations, or more complex relationships."
14
+ s.email = "jmason@suse.com"
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "acts_as_diffable.gemspec",
24
+ "init.rb",
25
+ "lib/acts_as_diffable.rb",
26
+ "test/app_root/app/controllers/application_controller.rb",
27
+ "test/app_root/app/models/group.rb",
28
+ "test/app_root/app/models/tag.rb",
29
+ "test/app_root/app/models/user.rb",
30
+ "test/app_root/config/boot.rb",
31
+ "test/app_root/config/database.yml",
32
+ "test/app_root/config/environment.rb",
33
+ "test/app_root/config/environments/in_memory.rb",
34
+ "test/app_root/config/environments/mysql.rb",
35
+ "test/app_root/config/environments/postgresql.rb",
36
+ "test/app_root/config/environments/sqlite.rb",
37
+ "test/app_root/config/environments/sqlite3.rb",
38
+ "test/app_root/config/routes.rb",
39
+ "test/app_root/db/migrate/20101117081517_create_users.rb",
40
+ "test/app_root/db/migrate/20101117081553_create_groups.rb",
41
+ "test/app_root/db/migrate/20101117081632_create_tags.rb",
42
+ "test/app_root/lib/console_with_fixtures.rb",
43
+ "test/app_root/log/.gitignore",
44
+ "test/app_root/script/console",
45
+ "test/diff_test.rb",
46
+ "test/fixtures/groups.yml",
47
+ "test/fixtures/tags.yml",
48
+ "test/fixtures/users.yml",
49
+ "test/test_helper.rb"
50
+ ]
51
+ s.homepage = "https://github.com/bear454/ActsAsDiffable"
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = "1.8.22"
54
+ s.summary = "Compare two instances of an ActiveRecord::Base class."
55
+
56
+ if s.respond_to? :specification_version then
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
60
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.3.14"])
61
+ else
62
+ s.add_dependency(%q<activerecord>, [">= 2.3.14"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<activerecord>, [">= 2.3.14"])
66
+ end
67
+ end
68
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_diffable'
@@ -0,0 +1,213 @@
1
+ require 'active_record'
2
+
3
+ module ActiveRecord #:nodoc:
4
+ module Acts #:nodoc:
5
+ module Diffable #:nodoc:
6
+
7
+ SINGULAR_MACROS = [:has_one, :belongs_to]
8
+ PLURAL_MACROS = [:has_many, :has_and_belongs_to_many]
9
+
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+
13
+ end
14
+
15
+ module ClassMethods
16
+ def acts_as_diffable
17
+ class_variable_set :@@manual_diff_definitions, {}
18
+ extend ActiveRecord::Acts::Diffable::SingletonMethods
19
+ include ActiveRecord::Acts::Diffable::InstanceMethods
20
+ end
21
+
22
+ end
23
+
24
+ module SingletonMethods
25
+ def manual_diff_definition(name, options = {})
26
+ definitions = class_variable_get :@@manual_diff_definitions
27
+ definitions[name.to_s] = options if options[:eval] # otherwise just ignore it
28
+ class_variable_set :@@manual_diff_definitions, definitions
29
+ end
30
+
31
+ def manual_diff_definitions
32
+ class_variable_get :@@manual_diff_definitions
33
+ end
34
+ end
35
+
36
+ # Adds instance methods.
37
+ module InstanceMethods
38
+ # Return a hash of the different attributes between two hashes, such as
39
+ # attributes of an ActiveRecord class.
40
+ def diff(other)
41
+ # is other an instance or just an id?
42
+ case other.class
43
+ when self.class
44
+ other
45
+ else
46
+ other = self.class.find(other)
47
+ end
48
+ # diff the top-level attributes
49
+ differences = attributes_diff(self, other) || {}
50
+ # has_one and belongs_to associations
51
+ ActiveRecord::Acts::Diffable::SINGULAR_MACROS.each do |macro|
52
+ self.class.reflect_on_all_associations(macro).each do |a|
53
+ differences[a.name.to_s] = singular_association_diff(self, other, a.name) if a.options[:diff]
54
+ differences.delete(a.options[:foreign_key] || "#{a.name}_id")
55
+ end
56
+ end
57
+ # has_many and habtm associations
58
+ ActiveRecord::Acts::Diffable::PLURAL_MACROS.each do |macro|
59
+ self.class.reflect_on_all_associations(macro).each do |a|
60
+ differences[a.name.to_s] = plural_association_diff(self, other, a.name, a.options[:diff_key]) if a.options[:diff_key]
61
+ end
62
+ end
63
+ # manually defined diffs
64
+ self.class.manual_diff_definitions.each{|d_name, d_props|
65
+ if d_props[:diff_key]
66
+ differences[d_name] = plural_association_diff(self, other, d_props[:eval], d_props[:diff_key])
67
+ else
68
+ differences[d_name] = singular_association_diff(self, other, d_props[:eval])
69
+ end
70
+ }
71
+ remove_unchanged_entries differences
72
+ end
73
+
74
+ def timed_log(start_time, msg)
75
+ puts "%04.2fs %s" % [(Time.now - start_time), msg]
76
+ end
77
+
78
+ # Helper for processing a single associated object,
79
+ # such as has_one (or belongs_to) associations.
80
+ def singular_association_diff(left_parent, right_parent, association)
81
+ association = association.to_s #instance_eval doesn't like symbols. What'ev.
82
+ attributes_diff(
83
+ left_parent.instance_eval(association),
84
+ right_parent.instance_eval(association),
85
+ association_ids(left_parent) )
86
+ end
87
+
88
+ # Helper for processing a collection of associated objects,
89
+ # such as has_many (or habtm) association.
90
+ def plural_association_diff(left_parent, right_parent, association, key_pattern)
91
+ key_pattern = Array(key_pattern)
92
+ association = association.to_s #instance_eval doesn't like symbols. What'ev.
93
+ left_association_set = Array(left_parent.instance_eval(association))
94
+ right_associaton_set = Array(right_parent.instance_eval(association))
95
+ # construct a set of values (key_set) from the attributes defined in key_pattern
96
+ key_sets = (
97
+ left_association_set.collect{|i| key_pattern.collect{|k| i.send(k) } } +
98
+ right_associaton_set.collect{|i| key_pattern.collect{|k| i.send(k) } } ).uniq
99
+ # for each key_set, compare instances in each collection
100
+ diff_set = {}
101
+ key_sets.each do |key_set|
102
+ conditions = {}
103
+ key_pattern.each_with_index{|k, i| conditions[k] = key_set[i] }
104
+ left_instance = left_association_set.find{|i|
105
+ conditions.collect{|cf,cv| i.send(cf) == cv}.all?
106
+ }
107
+ right_instance = right_associaton_set.find{|i|
108
+ conditions.collect{|cf,cv| i.send(cf) == cv}.all?
109
+ }
110
+ diff_set[keyify(key_set)] = attributes_diff(left_instance, right_instance, association_ids(left_parent) )
111
+ end
112
+
113
+ # clean up unchanged pairs
114
+ remove_unchanged_entries diff_set
115
+ end
116
+
117
+ # reduce the key out of an array to a single string if only one element
118
+ def keyify(keyset)
119
+ case keyset.size
120
+ when 1
121
+ keyset[0]
122
+ else
123
+ keyset
124
+ end
125
+ end
126
+
127
+ # Helper for collecting ids to ignore.
128
+ def association_ids(*instances)
129
+ ['id'] + instances.collect{|i| i.class.to_s.underscore + '_id'}
130
+ end
131
+
132
+ # Helper for handing objects with an attributes hash (a la ARec).
133
+ def attributes_diff(left, right, ignore = [:id])
134
+ left_attributes = case
135
+ when left.is_a?(Hash) then
136
+ left
137
+ when left.respond_to?(:attributes) then
138
+ left.attributes
139
+ else
140
+ left.instance_values
141
+ end
142
+ right_attributes = case
143
+ when right.is_a?(Hash) then
144
+ right
145
+ when right.respond_to?(:attributes) then
146
+ right.attributes
147
+ else
148
+ right.instance_values
149
+ end
150
+
151
+ if left_attributes == right_attributes
152
+ return nil
153
+ else
154
+ generate_diff_hash(left_attributes, right_attributes, *ignore)
155
+ end
156
+ end
157
+
158
+ # Helper for thinning the herd
159
+ def remove_unchanged_entries(diff_hash)
160
+ return nil if !diff_hash
161
+ diff_hash.delete_if{|k,v| v.nil? }
162
+ if diff_hash.empty?
163
+ return nil
164
+ else
165
+ return diff_hash
166
+ end
167
+ end
168
+
169
+ # Accepts a left & right hash, and an array of keys to ignore,
170
+ # returns a hash of the differences.
171
+ #
172
+ # This here is the meat & potatoes!
173
+ def generate_diff_hash(left, right, *ignore)
174
+ case [left.blank?, right.blank?]
175
+ when [false, true] # the represented object was deleted
176
+ { '_delete' => true } # inspired by nested_attributes
177
+ when [true, false] # the represented object was added
178
+ (ignore + %w(created_at updated_at)).each{|k| right.delete(k.to_s) }
179
+ return right # just return the attributes to add
180
+ when [false, false] # the represented object changed
181
+ # generate the attribute diffs from each side and
182
+ # merge them together as attribute => [left_value, right_value]
183
+ if left == right
184
+ return nil
185
+ else
186
+ diff_hash = left.diff(right).merge(right.diff(left)){|k, lv, rv| [lv, rv] }
187
+ # remove any ignored attributes
188
+ ignore.each {|k| diff_hash.delete(k.to_s) }
189
+ # compress created_at/updated_at duplication
190
+ diff_hash.delete('updated_at') if diff_hash['created_at'] == diff_hash['updated_at']
191
+ remove_unchanged_entries diff_hash
192
+ end
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
200
+ end
201
+
202
+ # reopen ActiveRecord and include all the above to make
203
+ # them available to all our models if they want it
204
+
205
+ ActiveRecord::Base.class_eval do
206
+ include ActiveRecord::Acts::Diffable
207
+ end
208
+
209
+ # monkeypatch the diff keys onto the association proxies
210
+ ActiveRecord::Associations::Builder::BelongsTo.class_eval("self.valid_options += [:diff]")
211
+ ActiveRecord::Associations::Builder::HasOne.class_eval("self.valid_options += [:diff]")
212
+ ActiveRecord::Associations::Builder::HasMany.class_eval("self.valid_options += [:diff_key]")
213
+ ActiveRecord::Associations::Builder::HasAndBelongsToMany.class_eval("self.valid_options += [:diff_key]")
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ # for testing single associations
2
+ class Group < ActiveRecord::Base
3
+ has_many :users
4
+ end
@@ -0,0 +1,4 @@
1
+ # for testing plural associations
2
+ class Tag < ActiveRecord::Base
3
+ belongs_to :user
4
+ end
@@ -0,0 +1,11 @@
1
+ # top-level class
2
+ class User < ActiveRecord::Base
3
+ acts_as_diffable
4
+
5
+ belongs_to :group, :diff => true
6
+ has_many :tags, :diff_key => :name
7
+
8
+ manual_diff_definition :group_name, :eval => 'group.name'
9
+ manual_diff_definition :m_tags, :eval => 'tags', :diff_key => 'name'
10
+
11
+ end
@@ -0,0 +1,115 @@
1
+ # Allow customization of the rails framework path
2
+ RAILS_FRAMEWORK_ROOT = (ENV['RAILS_FRAMEWORK_ROOT'] || "#{File.dirname(__FILE__)}/../../../../../../vendor/rails") unless defined?(RAILS_FRAMEWORK_ROOT)
3
+
4
+ # Don't change this file!
5
+ # Configure your app in config/environment.rb and config/environments/*.rb
6
+
7
+ Rails.root.to_s = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
8
+
9
+ module Rails
10
+ class << self
11
+ def boot!
12
+ unless booted?
13
+ preinitialize
14
+ pick_boot.run
15
+ end
16
+ end
17
+
18
+ def booted?
19
+ defined? Rails::Initializer
20
+ end
21
+
22
+ def pick_boot
23
+ (vendor_rails? ? VendorBoot : GemBoot).new
24
+ end
25
+
26
+ def vendor_rails?
27
+ File.exist?(RAILS_FRAMEWORK_ROOT)
28
+ end
29
+
30
+ def preinitialize
31
+ load(preinitializer_path) if File.exist?(preinitializer_path)
32
+ end
33
+
34
+ def preinitializer_path
35
+ "#{Rails.root.to_s}/config/preinitializer.rb"
36
+ end
37
+ end
38
+
39
+ class Boot
40
+ def run
41
+ load_initializer
42
+ Rails::Initializer.run(:set_load_path)
43
+ end
44
+ end
45
+
46
+ class VendorBoot < Boot
47
+ def load_initializer
48
+ require "#{RAILS_FRAMEWORK_ROOT}/railties/lib/initializer"
49
+ Rails::Initializer.run(:install_gem_spec_stubs)
50
+ Rails::GemDependency.add_frozen_gem_path
51
+ end
52
+ end
53
+
54
+ class GemBoot < Boot
55
+ def load_initializer
56
+ self.class.load_rubygems
57
+ load_rails_gem
58
+ require 'initializer'
59
+ end
60
+
61
+ def load_rails_gem
62
+ if version = self.class.gem_version
63
+ gem 'rails', version
64
+ else
65
+ gem 'rails'
66
+ end
67
+ rescue Gem::LoadError => load_error
68
+ $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
69
+ exit 1
70
+ end
71
+
72
+ class << self
73
+ def rubygems_version
74
+ Gem::RubyGemsVersion rescue nil
75
+ end
76
+
77
+ def gem_version
78
+ if defined? RAILS_GEM_VERSION
79
+ RAILS_GEM_VERSION
80
+ elsif ENV.include?('RAILS_GEM_VERSION')
81
+ ENV['RAILS_GEM_VERSION']
82
+ else
83
+ parse_gem_version(read_environment_rb)
84
+ end
85
+ end
86
+
87
+ def load_rubygems
88
+ require 'rubygems'
89
+ min_version = '1.3.1'
90
+ unless rubygems_version >= min_version
91
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
92
+ exit 1
93
+ end
94
+
95
+ rescue LoadError
96
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
97
+ exit 1
98
+ end
99
+
100
+ def parse_gem_version(text)
101
+ $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
102
+ end
103
+
104
+ private
105
+ def read_environment_rb
106
+ environment_rb = "#{Rails.root.to_s}/config/environment.rb"
107
+ environment_rb = "#{HELPER_Rails.root.to_s}/config/environment.rb" unless File.exists?(environment_rb)
108
+ File.read(environment_rb)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ # All that for this:
115
+ Rails.boot!
@@ -0,0 +1,31 @@
1
+ in_memory:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+ verbosity: quiet
5
+ pool: 5
6
+ timeout: 5000
7
+ sqlite:
8
+ adapter: sqlite
9
+ dbfile: plugin_test.sqlite.db
10
+ pool: 5
11
+ timeout: 5000
12
+ sqlite3:
13
+ adapter: sqlite3
14
+ dbfile: plugin_test.sqlite3.db
15
+ pool: 5
16
+ timeout: 5000
17
+ postgresql:
18
+ adapter: postgresql
19
+ username: postgres
20
+ password: postgres
21
+ database: plugin_test
22
+ pool: 5
23
+ timeout: 5000
24
+ mysql:
25
+ adapter: mysql
26
+ host: localhost
27
+ username: root
28
+ password:
29
+ database: plugin_test
30
+ pool: 5
31
+ timeout: 5000
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), 'boot')
2
+
3
+ Rails::Initializer.run do |config|
4
+ config.cache_classes = false
5
+ config.whiny_nils = true
6
+ config.action_controller.session = {:key => 'rails_session', :secret => 'd229e4d22437432705ab3985d4d246'}
7
+ config.plugin_locators.unshift(
8
+ Class.new(Rails::Plugin::Locator) do
9
+ def plugins
10
+ [Rails::Plugin.new(File.expand_path('.'))]
11
+ end
12
+ end
13
+ ) unless defined?(PluginTestHelper::PluginLocator)
14
+ end
@@ -0,0 +1,4 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.connect ':controller/:action/:id'
3
+ map.connect ':controller/:action/:id.:format'
4
+ end
@@ -0,0 +1,19 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.integer :rank
6
+ t.float :factor
7
+ t.date :activated_on
8
+ t.text :biography
9
+ t.boolean :admin
10
+ t.integer :group_id
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table :users
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ class CreateGroups < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :groups do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :groups
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class CreateTags < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ t.integer :user_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :tags
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ # Loads fixtures into the database when running the test app via the console
2
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(Rails.root, '../fixtures/*.{yml,csv}'))).each do |fixture_file|
3
+ Fixtures.create_fixtures(File.join(Rails.root, '../fixtures'), File.basename(fixture_file, '.*'))
4
+ end
File without changes
@@ -0,0 +1,7 @@
1
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
2
+ libs = " -r irb/completion"
3
+ libs << " -r test/test_helper"
4
+ libs << " -r console_app"
5
+ libs << " -r console_with_helpers"
6
+ libs << " -r console_with_fixtures"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,40 @@
1
+ require 'test/test_helper'
2
+
3
+ class DiffTest < ActiveSupport::TestCase
4
+ def test_instance_is_loadable
5
+ assert users(:john)
6
+ end
7
+
8
+ def test_instances_with_identical_attributes_should_return_a_nill_for_diff
9
+ assert_equal nil, users(:john).diff(users(:john))
10
+ end
11
+
12
+ def test_only_different_attributes_should_be_returned
13
+ diff_expectation = {'name' => ['John Doe', 'Jane Doe'],
14
+ 'rank' => [1, 2],
15
+ 'factor' => [2.718281828459045, 3.141592653589793],
16
+ 'activated_on' => [ Date.new(2010,11,17), Date.new(2005,01,01) ],
17
+ 'admin' => [false, true] }
18
+ diff_result = users(:john).diff(users(:jane))
19
+
20
+ assert_equal diff_expectation.inspect, diff_result.inspect
21
+ end
22
+
23
+ def test_singular_associations
24
+ diff_expectation = {'group' => { 'name' => ['FirstGroup', 'SecondGroup'] } }
25
+ diff_result = users(:jane).diff(users(:jane_in_a_different_group))
26
+
27
+ assert_equal diff_expectation.inspect, diff_result.inspect
28
+ end
29
+
30
+ def test_plural_associations
31
+ diff_expectation = {'tags' => { 'foo' => {'name' => 'foo' },
32
+ 'bar' => {'_delete' => true } },
33
+ 'm_tags' => { 'foo' => {'name' => 'foo' },
34
+ 'bar' => {'_delete' => true } } }
35
+ diff_result = users(:john).diff(users(:john_with_different_tags))
36
+
37
+ assert_equal diff_expectation.inspect, diff_result.inspect
38
+ end
39
+
40
+ end
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+
3
+ one:
4
+ name: FirstGroup
5
+
6
+ two:
7
+ name: SecondGroup
@@ -0,0 +1,33 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+
3
+ foo:
4
+ name: foo
5
+ user: john_with_different_tags
6
+
7
+ de:
8
+ name: de
9
+ user: john
10
+
11
+ de2:
12
+ name: de
13
+ user: john_with_different_tags
14
+
15
+ de3:
16
+ name: de
17
+ user: jane
18
+
19
+ de4:
20
+ name: de
21
+ user: jane_in_a_different_group
22
+
23
+ bar:
24
+ name: bar
25
+ user: john
26
+
27
+ bar2:
28
+ name: bar
29
+ user: jane
30
+
31
+ bar3:
32
+ name: bar
33
+ user: jane_in_a_different_group
@@ -0,0 +1,54 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+
3
+ john:
4
+ name: John Doe
5
+ rank: 1
6
+ factor: 2.718281828459045
7
+ activated_on: 2010-11-17
8
+ biography: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a
9
+ justo sed mauris rutrum rutrum nec vitae ante. Mauris tincidunt, mi sed
10
+ commodo fermentum, velit magna pulvinar enim, nec fringilla elit risus in
11
+ dolor. Aenean a lacus nec sem iaculis lacinia eget sed leo. Quisque ipsum
12
+ velit, sodales non dapibus."
13
+ admin: false
14
+ group: one
15
+
16
+ john_with_different_tags:
17
+ name: John Doe
18
+ rank: 1
19
+ factor: 2.718281828459045
20
+ activated_on: 2010-11-17
21
+ biography: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a
22
+ justo sed mauris rutrum rutrum nec vitae ante. Mauris tincidunt, mi sed
23
+ commodo fermentum, velit magna pulvinar enim, nec fringilla elit risus in
24
+ dolor. Aenean a lacus nec sem iaculis lacinia eget sed leo. Quisque ipsum
25
+ velit, sodales non dapibus."
26
+ admin: false
27
+ group: one
28
+
29
+ jane:
30
+ name: Jane Doe
31
+ rank: 2
32
+ factor: 3.141592653589793
33
+ activated_on: 2005-01-01
34
+ biography: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a
35
+ justo sed mauris rutrum rutrum nec vitae ante. Mauris tincidunt, mi sed
36
+ commodo fermentum, velit magna pulvinar enim, nec fringilla elit risus in
37
+ dolor. Aenean a lacus nec sem iaculis lacinia eget sed leo. Quisque ipsum
38
+ velit, sodales non dapibus."
39
+ admin: true
40
+ group: one
41
+
42
+ jane_in_a_different_group:
43
+ name: Jane Doe
44
+ rank: 2
45
+ factor: 3.141592653589793
46
+ activated_on: 2005-01-01
47
+ biography: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a
48
+ justo sed mauris rutrum rutrum nec vitae ante. Mauris tincidunt, mi sed
49
+ commodo fermentum, velit magna pulvinar enim, nec fringilla elit risus in
50
+ dolor. Aenean a lacus nec sem iaculis lacinia eget sed leo. Quisque ipsum
51
+ velit, sodales non dapibus."
52
+ admin: true
53
+ group: two
54
+
@@ -0,0 +1,21 @@
1
+ # Set the default environment to sqlite3's in_memory database
2
+ ENV['Rails.env.to_s'] ||= 'in_memory'
3
+
4
+ # Load the Rails environment and testing framework
5
+ require "#{File.dirname(__FILE__)}/app_root/config/environment"
6
+ require 'test_help'
7
+
8
+ # Undo changes to Rails.env.to_s
9
+ silence_warnings {Rails.env.to_s = ENV['RAILS_ENV']}
10
+
11
+ # Run the migrations
12
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
13
+
14
+ # Set default fixture loading properties
15
+ ActiveSupport::TestCase.class_eval do
16
+ self.use_transactional_fixtures = true
17
+ self.use_instantiated_fixtures = false
18
+ self.fixture_path = "#{File.dirname(__FILE__)}/fixtures"
19
+
20
+ fixtures :all
21
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_diffable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - James Mason 'bear454'
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-19 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 31
29
+ segments:
30
+ - 2
31
+ - 3
32
+ - 14
33
+ version: 2.3.14
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: ActsAsDiffable provides a dead-simple way to compare two instances of a class, including any or all associations, or more complex relationships.
37
+ email: jmason@suse.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README
44
+ files:
45
+ - MIT-LICENSE
46
+ - README
47
+ - Rakefile
48
+ - VERSION
49
+ - acts_as_diffable.gemspec
50
+ - init.rb
51
+ - lib/acts_as_diffable.rb
52
+ - test/app_root/app/controllers/application_controller.rb
53
+ - test/app_root/app/models/group.rb
54
+ - test/app_root/app/models/tag.rb
55
+ - test/app_root/app/models/user.rb
56
+ - test/app_root/config/boot.rb
57
+ - test/app_root/config/database.yml
58
+ - test/app_root/config/environment.rb
59
+ - test/app_root/config/environments/in_memory.rb
60
+ - test/app_root/config/environments/mysql.rb
61
+ - test/app_root/config/environments/postgresql.rb
62
+ - test/app_root/config/environments/sqlite.rb
63
+ - test/app_root/config/environments/sqlite3.rb
64
+ - test/app_root/config/routes.rb
65
+ - test/app_root/db/migrate/20101117081517_create_users.rb
66
+ - test/app_root/db/migrate/20101117081553_create_groups.rb
67
+ - test/app_root/db/migrate/20101117081632_create_tags.rb
68
+ - test/app_root/lib/console_with_fixtures.rb
69
+ - test/app_root/log/.gitignore
70
+ - test/app_root/script/console
71
+ - test/diff_test.rb
72
+ - test/fixtures/groups.yml
73
+ - test/fixtures/tags.yml
74
+ - test/fixtures/users.yml
75
+ - test/test_helper.rb
76
+ homepage: https://github.com/bear454/ActsAsDiffable
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.22
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Compare two instances of an ActiveRecord::Base class.
109
+ test_files: []
110
+