Commit 4001b435cc8e42f14a80853eae274b6472b95a21
1 parent
8042c0ea
Exists in
master
and in
22 other branches
rails3: update acts_as_versioned to a rails3 compatible version
Showing
28 changed files
with
513 additions
and
1240 deletions
Show diff stats
... | ... | @@ -0,0 +1 @@ |
1 | +rvm --create use 1.9.2@acts_as_versioned | ... | ... |
vendor/plugins/acts_as_versioned/CHANGELOG
... | ... | @@ -1,82 +0,0 @@ |
1 | -*GIT* (version numbers are overrated) | |
2 | - | |
3 | -* (16 Jun 2008) Backwards Compatibility is overrated (big updates for rails 2.1) | |
4 | - | |
5 | - * Use ActiveRecord 2.1's dirty attribute checking instead [Asa Calow] | |
6 | - * Remove last traces of #non_versioned_fields | |
7 | - * Remove AR::Base.find_version and AR::Base.find_versions, rely on AR association proxies and named_scope | |
8 | - * Remove #versions_count, rely on AR association counter caching. | |
9 | - * Remove #versioned_attributes, basically the same as AR::Base.versioned_columns | |
10 | - | |
11 | -* (5 Oct 2006) Allow customization of #versions association options [Dan Peterson] | |
12 | - | |
13 | -*0.5.1* | |
14 | - | |
15 | -* (8 Aug 2006) Versioned models now belong to the unversioned model. @article_version.article.class => Article [Aslak Hellesoy] | |
16 | - | |
17 | -*0.5* # do versions even matter for plugins? | |
18 | - | |
19 | -* (21 Apr 2006) Added without_locking and without_revision methods. | |
20 | - | |
21 | - Foo.without_revision do | |
22 | - @foo.update_attributes ... | |
23 | - end | |
24 | - | |
25 | -*0.4* | |
26 | - | |
27 | -* (28 March 2006) Rename non_versioned_fields to non_versioned_columns (old one is kept for compatibility). | |
28 | -* (28 March 2006) Made explicit documentation note that string column names are required for non_versioned_columns. | |
29 | - | |
30 | -*0.3.1* | |
31 | - | |
32 | -* (7 Jan 2006) explicitly set :foreign_key option for the versioned model's belongs_to assocation for STI [Caged] | |
33 | -* (7 Jan 2006) added tests to prove has_many :through joins work | |
34 | - | |
35 | -*0.3* | |
36 | - | |
37 | -* (2 Jan 2006) added ability to share a mixin with versioned class | |
38 | -* (2 Jan 2006) changed the dynamic version model to MyModel::Version | |
39 | - | |
40 | -*0.2.4* | |
41 | - | |
42 | -* (27 Nov 2005) added note about possible destructive behavior of if_changed? [Michael Schuerig] | |
43 | - | |
44 | -*0.2.3* | |
45 | - | |
46 | -* (12 Nov 2005) fixed bug with old behavior of #blank? [Michael Schuerig] | |
47 | -* (12 Nov 2005) updated tests to use ActiveRecord Schema | |
48 | - | |
49 | -*0.2.2* | |
50 | - | |
51 | -* (3 Nov 2005) added documentation note to #acts_as_versioned [Martin Jul] | |
52 | - | |
53 | -*0.2.1* | |
54 | - | |
55 | -* (6 Oct 2005) renamed dirty? to changed? to keep it uniform. it was aliased to keep it backwards compatible. | |
56 | - | |
57 | -*0.2* | |
58 | - | |
59 | -* (6 Oct 2005) added find_versions and find_version class methods. | |
60 | - | |
61 | -* (6 Oct 2005) removed transaction from create_versioned_table(). | |
62 | - this way you can specify your own transaction around a group of operations. | |
63 | - | |
64 | -* (30 Sep 2005) fixed bug where find_versions() would order by 'version' twice. (found by Joe Clark) | |
65 | - | |
66 | -* (26 Sep 2005) added :sequence_name option to acts_as_versioned to set the sequence name on the versioned model | |
67 | - | |
68 | -*0.1.3* (18 Sep 2005) | |
69 | - | |
70 | -* First RubyForge release | |
71 | - | |
72 | -*0.1.2* | |
73 | - | |
74 | -* check if module is already included when acts_as_versioned is called | |
75 | - | |
76 | -*0.1.1* | |
77 | - | |
78 | -* Adding tests and rdocs | |
79 | - | |
80 | -*0.1* | |
81 | - | |
82 | -* Initial transfer from Rails ticket: http://dev.rubyonrails.com/ticket/1974 | |
83 | 0 | \ No newline at end of file |
... | ... | @@ -0,0 +1,101 @@ |
1 | +GEM | |
2 | + remote: http://rubygems.org/ | |
3 | + specs: | |
4 | + actionmailer (3.1.4) | |
5 | + actionpack (= 3.1.4) | |
6 | + mail (~> 2.3.0) | |
7 | + actionpack (3.1.4) | |
8 | + activemodel (= 3.1.4) | |
9 | + activesupport (= 3.1.4) | |
10 | + builder (~> 3.0.0) | |
11 | + erubis (~> 2.7.0) | |
12 | + i18n (~> 0.6) | |
13 | + rack (~> 1.3.6) | |
14 | + rack-cache (~> 1.1) | |
15 | + rack-mount (~> 0.8.2) | |
16 | + rack-test (~> 0.6.1) | |
17 | + sprockets (~> 2.0.3) | |
18 | + activemodel (3.1.4) | |
19 | + activesupport (= 3.1.4) | |
20 | + builder (~> 3.0.0) | |
21 | + i18n (~> 0.6) | |
22 | + activerecord (3.1.4) | |
23 | + activemodel (= 3.1.4) | |
24 | + activesupport (= 3.1.4) | |
25 | + arel (~> 2.2.3) | |
26 | + tzinfo (~> 0.3.29) | |
27 | + activeresource (3.1.4) | |
28 | + activemodel (= 3.1.4) | |
29 | + activesupport (= 3.1.4) | |
30 | + activesupport (3.1.4) | |
31 | + multi_json (~> 1.0) | |
32 | + allison (2.0.3) | |
33 | + arel (2.2.3) | |
34 | + builder (3.0.0) | |
35 | + echoe (4.6.3) | |
36 | + allison (>= 2.0.3) | |
37 | + gemcutter (>= 0.7.0) | |
38 | + rake (>= 0.9.2) | |
39 | + rdoc (>= 3.6.1) | |
40 | + rubyforge (>= 2.0.4) | |
41 | + erubis (2.7.0) | |
42 | + gemcutter (0.7.1) | |
43 | + hike (1.2.1) | |
44 | + i18n (0.6.0) | |
45 | + json (1.6.6) | |
46 | + json_pure (1.7.7) | |
47 | + mail (2.3.3) | |
48 | + i18n (>= 0.4.0) | |
49 | + mime-types (~> 1.16) | |
50 | + treetop (~> 1.4.8) | |
51 | + mime-types (1.18) | |
52 | + multi_json (1.2.0) | |
53 | + polyglot (0.3.3) | |
54 | + rack (1.3.6) | |
55 | + rack-cache (1.2) | |
56 | + rack (>= 0.4) | |
57 | + rack-mount (0.8.3) | |
58 | + rack (>= 1.0.0) | |
59 | + rack-ssl (1.3.2) | |
60 | + rack | |
61 | + rack-test (0.6.1) | |
62 | + rack (>= 1.0) | |
63 | + rails (3.1.4) | |
64 | + actionmailer (= 3.1.4) | |
65 | + actionpack (= 3.1.4) | |
66 | + activerecord (= 3.1.4) | |
67 | + activeresource (= 3.1.4) | |
68 | + activesupport (= 3.1.4) | |
69 | + bundler (~> 1.0) | |
70 | + railties (= 3.1.4) | |
71 | + railties (3.1.4) | |
72 | + actionpack (= 3.1.4) | |
73 | + activesupport (= 3.1.4) | |
74 | + rack-ssl (~> 1.3.2) | |
75 | + rake (>= 0.8.7) | |
76 | + rdoc (~> 3.4) | |
77 | + thor (~> 0.14.6) | |
78 | + rake (0.9.2.2) | |
79 | + rdoc (3.12) | |
80 | + json (~> 1.4) | |
81 | + rubyforge (2.0.4) | |
82 | + json_pure (>= 1.1.7) | |
83 | + sprockets (2.0.3) | |
84 | + hike (~> 1.2) | |
85 | + rack (~> 1.0) | |
86 | + tilt (~> 1.1, != 1.3.0) | |
87 | + sqlite3 (1.3.5) | |
88 | + thor (0.14.6) | |
89 | + tilt (1.3.3) | |
90 | + treetop (1.4.10) | |
91 | + polyglot | |
92 | + polyglot (>= 0.3.1) | |
93 | + tzinfo (0.3.33) | |
94 | + | |
95 | +PLATFORMS | |
96 | + ruby | |
97 | + | |
98 | +DEPENDENCIES | |
99 | + echoe | |
100 | + rails (~> 3.1.0) | |
101 | + sqlite3 | ... | ... |
vendor/plugins/acts_as_versioned/README
... | ... | @@ -1,28 +0,0 @@ |
1 | -= acts_as_versioned | |
2 | - | |
3 | -This library adds simple versioning to an ActiveRecord module. ActiveRecord is required. | |
4 | - | |
5 | -== Resources | |
6 | - | |
7 | -Install | |
8 | - | |
9 | -* gem install acts_as_versioned | |
10 | - | |
11 | -Rubyforge project | |
12 | - | |
13 | -* http://rubyforge.org/projects/ar-versioned | |
14 | - | |
15 | -RDocs | |
16 | - | |
17 | -* http://ar-versioned.rubyforge.org | |
18 | - | |
19 | -Subversion | |
20 | - | |
21 | -* http://techno-weenie.net/svn/projects/acts_as_versioned | |
22 | - | |
23 | -Collaboa | |
24 | - | |
25 | -* http://collaboa.techno-weenie.net/repository/browse/acts_as_versioned | |
26 | - | |
27 | -Special thanks to Dreamer on ##rubyonrails for help in early testing. His ServerSideWiki (http://serversidewiki.com) | |
28 | -was the first project to use acts_as_versioned <em>in the wild</em>. | |
29 | 0 | \ No newline at end of file |
... | ... | @@ -0,0 +1,120 @@ |
1 | +## About ## | |
2 | +===== | |
3 | + | |
4 | +acts_as_versioned is a gem for Rails 3.1 & 3.2 to enable easy versioning of models. As a versioned model is updated revisions are kept in a seperate table, providing a record of what changed. | |
5 | + | |
6 | +## Getting Started ## | |
7 | +===== | |
8 | + | |
9 | +In your Gemfile simply include: | |
10 | + | |
11 | + gem 'acts_as_versioned', :git => 'https://github.com/jwhitehorn/acts_as_versioned.git' | |
12 | + | |
13 | +The next time you run `bundle install` you'll be all set to start using acts_as_versioned. | |
14 | + | |
15 | +## Usage ## | |
16 | +===== | |
17 | + | |
18 | +#### Versioning a Model #### | |
19 | +By default acts_as_versioned is unobtrusive. You will need to explicitly state which models to version. To do so, add the line `acts_as_versioned` to your model, like so: | |
20 | + | |
21 | + class MyModel < ActiveRecord::Base | |
22 | + acts_as_versioned | |
23 | + #... | |
24 | + end | |
25 | + | |
26 | +Next we need to create a migration to setup our versioning tables: | |
27 | + | |
28 | + bundle exec rails generate migration AddVersioningToMyModel | |
29 | + | |
30 | +Once that is completed, edit the generated migration. acts_as_versioned patches your model to add a `create_versioned_table` and `drop_versioned_table` method. A migration for `MyModel` (assuming MyModel already existed) might look like: | |
31 | + | |
32 | + class AddVersioningToMyModel < ActiveRecord::Migration | |
33 | + def self.up | |
34 | + MyModel.create_versioned_table | |
35 | + end | |
36 | + | |
37 | + def self.down | |
38 | + MyModel.drop_versioned_table | |
39 | + end | |
40 | + end | |
41 | + | |
42 | +Execute your migration: | |
43 | + | |
44 | + bundle exec rake db:migrate | |
45 | + | |
46 | +And you're finished! Without any addition work, `MyModel` is being versioned. | |
47 | + | |
48 | +#### Excluding attributes from versioning #### | |
49 | + | |
50 | +Sometime you want to exclude an attribute of a model from being versioned. That can be accomplished with the `:except` paramter to `acts_as_versioned`: | |
51 | + | |
52 | + class MyMode < ActiveRecord::Base | |
53 | + acts_as_versioned :except => :some_attr_i_dont_want_versioned | |
54 | + | |
55 | + end | |
56 | + | |
57 | + | |
58 | +#### Revisions #### | |
59 | + | |
60 | +Recording a history of changes to a model is only useful if you can do something with that data. With acts_as_versioned there are several ways you can interact with a model's revisions. | |
61 | + | |
62 | +##### Version Number ##### | |
63 | +To determine what the current version number for a model is: | |
64 | + | |
65 | + model.version | |
66 | + | |
67 | +The `version` attribute is available for both the actual model, and also any revisions of a model. Thusly, the following is valid: | |
68 | + | |
69 | + model.versions.last.version | |
70 | + | |
71 | +##### Revisions List ##### | |
72 | +As alluded to above, you can get an array of revisions of a model via the `versions` attribute: | |
73 | + | |
74 | + model.versions | |
75 | + | |
76 | +The returned objects are of a type `MyModel::Version` where `MyModel` is the model you are working with. These objects have identical fields to `MyModel`. So, if `MyModel` had a `name` attribute, you could also say: | |
77 | + | |
78 | + model.versions.last.name | |
79 | + | |
80 | +##### Reverting to a Revision ##### | |
81 | +To revert a model to an older revision, simply call `revert_to` with the version number you desire to rever to: | |
82 | + | |
83 | + model.revert_to(version_number) | |
84 | + | |
85 | +##### Saving Without Revisions ##### | |
86 | +Occasionally you might need to save a model without necessary creating revisions. To do so, use the `save_without_revision` method: | |
87 | + | |
88 | + model.save_without_revision | |
89 | + | |
90 | + | |
91 | +#### Migrations #### | |
92 | +Adding a field to your model does not automatically add it to the versioning table. So, when you add new fields, be sure to add them to both: | |
93 | + | |
94 | + class AddNewFieldToMyModel < ActiveRecord::Migration | |
95 | + def change | |
96 | + add_column :my_models, :new_field_, :string | |
97 | + add_column :my_model_versions, :new_field_, :string | |
98 | + end | |
99 | + end | |
100 | + | |
101 | +#### Version Class #### | |
102 | +As has been stated, the versioned data is stored seperately from the main class. This also implies that `model.versions` returns an area of object of a class _other_ than `MyModel` (where `model` is an instance of `MyModel`, keeping with our working example). The instances returned are actually of type `MyModel::Version`. With this, comes the fact that any methods, associations, etc. defined on `MyModel` are not present on `MyModel::Version`. | |
103 | + | |
104 | +While this sounds obvious, it can some times be unexpected. Especially when acts_as_versioned make it so easy to grab historical records from a live record. A common scenario where this can come up is associations. | |
105 | + | |
106 | +Say `MyModel` belongs to `TheMan`. Also, assume that you want to find out where (in the past) a particular instance of `MyModel` was updated in regards to it's association to `TheMan`. You could write that as: | |
107 | + | |
108 | + model.versions.keep_if { |m| m.the_man != current_man }.last | |
109 | + | |
110 | +However, this will not work. This is because `MyModel::Version` does _not_ belong to `TheMan`. You could compare ids here, or you could patch `MyModel::Version` to belong to `TheMan` like: | |
111 | + | |
112 | + class MyModel | |
113 | + acts_as_versioned | |
114 | + belongs_to :the_men | |
115 | + #some stuff | |
116 | + | |
117 | + class Version | |
118 | + belongs_to :the_men | |
119 | + end | |
120 | + end | |
0 | 121 | \ No newline at end of file | ... | ... |
vendor/plugins/acts_as_versioned/Rakefile
1 | -require 'rubygems' | |
2 | - | |
3 | -require 'rake/rdoctask' | |
4 | -require 'rake/packagetask' | |
5 | -require 'rake/gempackagetask' | |
6 | -require 'rake/testtask' | |
7 | -require 'rake/contrib/rubyforgepublisher' | |
8 | - | |
9 | -PKG_NAME = 'acts_as_versioned' | |
10 | -PKG_VERSION = '0.3.1' | |
11 | -PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" | |
12 | -PROD_HOST = "technoweenie@bidwell.textdrive.com" | |
13 | -RUBY_FORGE_PROJECT = 'ar-versioned' | |
14 | -RUBY_FORGE_USER = 'technoweenie' | |
15 | - | |
16 | -desc 'Default: run unit tests.' | |
17 | -task :default => :test | |
18 | - | |
19 | -desc 'Test the calculations plugin.' | |
20 | -Rake::TestTask.new(:test) do |t| | |
21 | - t.libs << 'lib' | |
22 | - t.pattern = 'test/**/*_test.rb' | |
23 | - t.verbose = true | |
24 | -end | |
25 | - | |
26 | -desc 'Generate documentation for the calculations plugin.' | |
27 | -Rake::RDocTask.new(:rdoc) do |rdoc| | |
28 | - rdoc.rdoc_dir = 'rdoc' | |
29 | - rdoc.title = "#{PKG_NAME} -- Simple versioning with active record models" | |
30 | - rdoc.options << '--line-numbers --inline-source' | |
31 | - rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS') | |
32 | - rdoc.rdoc_files.include('lib/**/*.rb') | |
33 | -end | |
34 | - | |
35 | -spec = Gem::Specification.new do |s| | |
36 | - s.name = PKG_NAME | |
37 | - s.version = PKG_VERSION | |
38 | - s.platform = Gem::Platform::RUBY | |
39 | - s.summary = "Simple versioning with active record models" | |
40 | - s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS) | |
41 | - s.files.delete "acts_as_versioned_plugin.sqlite.db" | |
42 | - s.files.delete "acts_as_versioned_plugin.sqlite3.db" | |
43 | - s.files.delete "test/debug.log" | |
44 | - s.require_path = 'lib' | |
45 | - s.autorequire = 'acts_as_versioned' | |
46 | - s.has_rdoc = true | |
47 | - s.test_files = Dir['test/**/*_test.rb'] | |
48 | - s.add_dependency 'activerecord', '>= 1.10.1' | |
49 | - s.add_dependency 'activesupport', '>= 1.1.1' | |
50 | - s.author = "Rick Olson" | |
51 | - s.email = "technoweenie@gmail.com" | |
52 | - s.homepage = "http://techno-weenie.net" | |
53 | -end | |
54 | - | |
55 | -Rake::GemPackageTask.new(spec) do |pkg| | |
56 | - pkg.need_tar = true | |
57 | -end | |
58 | - | |
59 | -desc "Publish the API documentation" | |
60 | -task :pdoc => [:rdoc] do | |
61 | - Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload | |
62 | -end | |
63 | - | |
64 | -desc 'Publish the gem and API docs' | |
65 | -task :publish => [:pdoc, :rubyforge_upload] | |
66 | - | |
67 | -desc "Publish the release files to RubyForge." | |
68 | -task :rubyforge_upload => :package do | |
69 | - files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" } | |
70 | - | |
71 | - if RUBY_FORGE_PROJECT then | |
72 | - require 'net/http' | |
73 | - require 'open-uri' | |
74 | - | |
75 | - project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/" | |
76 | - project_data = open(project_uri) { |data| data.read } | |
77 | - group_id = project_data[/[?&]group_id=(\d+)/, 1] | |
78 | - raise "Couldn't get group id" unless group_id | |
79 | - | |
80 | - # This echos password to shell which is a bit sucky | |
81 | - if ENV["RUBY_FORGE_PASSWORD"] | |
82 | - password = ENV["RUBY_FORGE_PASSWORD"] | |
83 | - else | |
84 | - print "#{RUBY_FORGE_USER}@rubyforge.org's password: " | |
85 | - password = STDIN.gets.chomp | |
86 | - end | |
87 | - | |
88 | - login_response = Net::HTTP.start("rubyforge.org", 80) do |http| | |
89 | - data = [ | |
90 | - "login=1", | |
91 | - "form_loginname=#{RUBY_FORGE_USER}", | |
92 | - "form_pw=#{password}" | |
93 | - ].join("&") | |
94 | - http.post("/account/login.php", data) | |
95 | - end | |
96 | - | |
97 | - cookie = login_response["set-cookie"] | |
98 | - raise "Login failed" unless cookie | |
99 | - headers = { "Cookie" => cookie } | |
100 | - | |
101 | - release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}" | |
102 | - release_data = open(release_uri, headers) { |data| data.read } | |
103 | - package_id = release_data[/[?&]package_id=(\d+)/, 1] | |
104 | - raise "Couldn't get package id" unless package_id | |
105 | - | |
106 | - first_file = true | |
107 | - release_id = "" | |
108 | - | |
109 | - files.each do |filename| | |
110 | - basename = File.basename(filename) | |
111 | - file_ext = File.extname(filename) | |
112 | - file_data = File.open(filename, "rb") { |file| file.read } | |
113 | - | |
114 | - puts "Releasing #{basename}..." | |
115 | - | |
116 | - release_response = Net::HTTP.start("rubyforge.org", 80) do |http| | |
117 | - release_date = Time.now.strftime("%Y-%m-%d %H:%M") | |
118 | - type_map = { | |
119 | - ".zip" => "3000", | |
120 | - ".tgz" => "3110", | |
121 | - ".gz" => "3110", | |
122 | - ".gem" => "1400" | |
123 | - }; type_map.default = "9999" | |
124 | - type = type_map[file_ext] | |
125 | - boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor" | |
126 | - | |
127 | - query_hash = if first_file then | |
128 | - { | |
129 | - "group_id" => group_id, | |
130 | - "package_id" => package_id, | |
131 | - "release_name" => PKG_FILE_NAME, | |
132 | - "release_date" => release_date, | |
133 | - "type_id" => type, | |
134 | - "processor_id" => "8000", # Any | |
135 | - "release_notes" => "", | |
136 | - "release_changes" => "", | |
137 | - "preformatted" => "1", | |
138 | - "submit" => "1" | |
139 | - } | |
140 | - else | |
141 | - { | |
142 | - "group_id" => group_id, | |
143 | - "release_id" => release_id, | |
144 | - "package_id" => package_id, | |
145 | - "step2" => "1", | |
146 | - "type_id" => type, | |
147 | - "processor_id" => "8000", # Any | |
148 | - "submit" => "Add This File" | |
149 | - } | |
150 | - end | |
151 | - | |
152 | - query = "?" + query_hash.map do |(name, value)| | |
153 | - [name, URI.encode(value)].join("=") | |
154 | - end.join("&") | |
155 | - | |
156 | - data = [ | |
157 | - "--" + boundary, | |
158 | - "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"", | |
159 | - "Content-Type: application/octet-stream", | |
160 | - "Content-Transfer-Encoding: binary", | |
161 | - "", file_data, "" | |
162 | - ].join("\x0D\x0A") | |
163 | - | |
164 | - release_headers = headers.merge( | |
165 | - "Content-Type" => "multipart/form-data; boundary=#{boundary}" | |
166 | - ) | |
167 | - | |
168 | - target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php" | |
169 | - http.post(target + query, data, release_headers) | |
170 | - end | |
171 | - | |
172 | - if first_file then | |
173 | - release_id = release_response.body[/release_id=(\d+)/, 1] | |
174 | - raise("Couldn't get release id") unless release_id | |
175 | - end | |
176 | - | |
177 | - first_file = false | |
178 | - end | |
179 | - end | |
180 | -end | |
181 | 1 | \ No newline at end of file |
2 | +require 'rubygems' | |
3 | +require 'rake' | |
4 | +require 'echoe' | |
5 | + | |
6 | +Echoe.new('acts_as_versioned', '3.2.1') do |p| | |
7 | + p.description = "Active Record model versioning" | |
8 | + p.url = "http://github.com/jwhitehorn/acts_as_versioned" | |
9 | + p.author = "Jason Whitehorn" | |
10 | + p.email = "jason.whitehorn@gmail.com" | |
11 | + p.dependencies = ['activerecord'] | |
12 | +end | |
13 | + | |
14 | +Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext } | |
182 | 15 | \ No newline at end of file | ... | ... |
vendor/plugins/acts_as_versioned/VERSION.yml
vendor/plugins/acts_as_versioned/acts_as_versioned.gemspec
1 | 1 | # -*- encoding: utf-8 -*- |
2 | 2 | |
3 | 3 | Gem::Specification.new do |s| |
4 | - s.name = %q{acts_as_versioned} | |
5 | - s.version = "0.5.2" | |
4 | + s.name = "acts_as_versioned" | |
5 | + s.version = "3.2.1" | |
6 | 6 | |
7 | - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= | |
8 | - s.authors = ["technoweenie"] | |
9 | - s.date = %q{2009-01-20} | |
10 | - s.description = %q{TODO} | |
11 | - s.email = %q{technoweenie@bidwell.textdrive.com} | |
12 | - s.files = ["VERSION.yml", "lib/acts_as_versioned.rb", "test/abstract_unit.rb", "test/database.yml", "test/fixtures", "test/fixtures/authors.yml", "test/fixtures/landmark.rb", "test/fixtures/landmark_versions.yml", "test/fixtures/landmarks.yml", "test/fixtures/locked_pages.yml", "test/fixtures/locked_pages_revisions.yml", "test/fixtures/migrations", "test/fixtures/migrations/1_add_versioned_tables.rb", "test/fixtures/page.rb", "test/fixtures/page_versions.yml", "test/fixtures/pages.yml", "test/fixtures/widget.rb", "test/migration_test.rb", "test/schema.rb", "test/versioned_test.rb"] | |
13 | - s.has_rdoc = true | |
14 | - s.homepage = %q{http://github.com/technoweenie/acts_as_versioned} | |
15 | - s.rdoc_options = ["--inline-source", "--charset=UTF-8"] | |
7 | + s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= | |
8 | + s.authors = ["Jason Whitehorn"] | |
9 | + s.date = "2013-05-03" | |
10 | + s.description = "Active Record model versioning" | |
11 | + s.email = "jason.whitehorn@gmail.com" | |
12 | + s.extra_rdoc_files = ["README.md", "lib/acts_as_versioned.rb"] | |
13 | + s.files = ["Gemfile", "Gemfile.lock", "MIT-LICENSE", "Manifest", "README.md", "RUNNING_UNIT_TESTS", "Rakefile", "acts_as_versioned.gemspec", "acts_as_versioned_plugin.sqlite3.db", "init.rb", "lib/acts_as_versioned.rb"] | |
14 | + s.homepage = "http://github.com/jwhitehorn/acts_as_versioned" | |
15 | + s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Acts_as_versioned", "--main", "README.md"] | |
16 | 16 | s.require_paths = ["lib"] |
17 | - s.rubygems_version = %q{1.3.1} | |
18 | - s.summary = %q{TODO} | |
17 | + s.rubyforge_project = "acts_as_versioned" | |
18 | + s.rubygems_version = "1.8.25" | |
19 | + s.summary = "Active Record model versioning" | |
19 | 20 | |
20 | 21 | if s.respond_to? :specification_version then |
21 | - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION | |
22 | - s.specification_version = 2 | |
22 | + s.specification_version = 3 | |
23 | 23 | |
24 | - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then | |
24 | + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then | |
25 | + s.add_runtime_dependency(%q<activerecord>, [">= 0"]) | |
25 | 26 | else |
27 | + s.add_dependency(%q<activerecord>, [">= 0"]) | |
26 | 28 | end |
27 | 29 | else |
30 | + s.add_dependency(%q<activerecord>, [">= 0"]) | |
28 | 31 | end |
29 | 32 | end | ... | ... |
vendor/plugins/acts_as_versioned/lib/acts_as_versioned.rb
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
19 | 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
20 | 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | +require 'active_support/concern' | |
21 | 22 | |
22 | 23 | module ActiveRecord #:nodoc: |
23 | 24 | module Acts #:nodoc: |
... | ... | @@ -66,203 +67,206 @@ module ActiveRecord #:nodoc: |
66 | 67 | # |
67 | 68 | # See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options |
68 | 69 | module Versioned |
70 | + VERSION = "0.6.0" | |
69 | 71 | CALLBACKS = [:set_new_version, :save_version, :save_version?] |
70 | - def self.included(base) # :nodoc: | |
71 | - base.extend ClassMethods | |
72 | - end | |
73 | 72 | |
74 | - module ClassMethods | |
75 | - # == Configuration options | |
76 | - # | |
77 | - # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example) | |
78 | - # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example) | |
79 | - # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example) | |
80 | - # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type) | |
81 | - # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version) | |
82 | - # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model. | |
83 | - # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited | |
84 | - # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved. | |
85 | - # For finer control, pass either a Proc or modify Model#version_condition_met? | |
86 | - # | |
87 | - # acts_as_versioned :if => Proc.new { |auction| !auction.expired? } | |
88 | - # | |
89 | - # or... | |
90 | - # | |
91 | - # class Auction | |
92 | - # def version_condition_met? # totally bypasses the <tt>:if</tt> option | |
93 | - # !expired? | |
94 | - # end | |
95 | - # end | |
96 | - # | |
97 | - # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes | |
98 | - # either a symbol or array of symbols. | |
99 | - # | |
100 | - # * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block | |
101 | - # to create an anonymous mixin: | |
102 | - # | |
103 | - # class Auction | |
104 | - # acts_as_versioned do | |
105 | - # def started? | |
106 | - # !started_at.nil? | |
107 | - # end | |
108 | - # end | |
109 | - # end | |
110 | - # | |
111 | - # or... | |
112 | - # | |
113 | - # module AuctionExtension | |
114 | - # def started? | |
115 | - # !started_at.nil? | |
116 | - # end | |
117 | - # end | |
118 | - # class Auction | |
119 | - # acts_as_versioned :extend => AuctionExtension | |
120 | - # end | |
121 | - # | |
122 | - # Example code: | |
123 | - # | |
124 | - # @auction = Auction.find(1) | |
125 | - # @auction.started? | |
126 | - # @auction.versions.first.started? | |
127 | - # | |
128 | - # == Database Schema | |
129 | - # | |
130 | - # The model that you're versioning needs to have a 'version' attribute. The model is versioned | |
131 | - # into a table called #{model}_versions where the model name is singlular. The _versions table should | |
132 | - # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field. | |
133 | - # | |
134 | - # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance, | |
135 | - # then that field is reflected in the versioned model as 'versioned_type' by default. | |
136 | - # | |
137 | - # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table | |
138 | - # method, perfect for a migration. It will also create the version column if the main model does not already have it. | |
139 | - # | |
140 | - # class AddVersions < ActiveRecord::Migration | |
141 | - # def self.up | |
142 | - # # create_versioned_table takes the same options hash | |
143 | - # # that create_table does | |
144 | - # Post.create_versioned_table | |
145 | - # end | |
146 | - # | |
147 | - # def self.down | |
148 | - # Post.drop_versioned_table | |
149 | - # end | |
150 | - # end | |
151 | - # | |
152 | - # == Changing What Fields Are Versioned | |
153 | - # | |
154 | - # By default, acts_as_versioned will version all but these fields: | |
155 | - # | |
156 | - # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column] | |
157 | - # | |
158 | - # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols. | |
159 | - # | |
160 | - # class Post < ActiveRecord::Base | |
161 | - # acts_as_versioned | |
162 | - # self.non_versioned_columns << 'comments_count' | |
163 | - # end | |
164 | - # | |
165 | - def acts_as_versioned(options = {}, &extension) | |
166 | - # don't allow multiple calls | |
167 | - return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods) | |
168 | - | |
169 | - send :include, ActiveRecord::Acts::Versioned::ActMethods | |
170 | - | |
171 | - cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, | |
172 | - :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns, | |
173 | - :version_association_options, :version_if_changed | |
174 | - | |
175 | - self.versioned_class_name = options[:class_name] || "Version" | |
176 | - self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key | |
177 | - self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}" | |
178 | - self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}" | |
179 | - self.version_column = options[:version_column] || 'version' | |
180 | - self.version_sequence_name = options[:sequence_name] | |
181 | - self.max_version_limit = options[:limit].to_i | |
182 | - self.version_condition = options[:if] || true | |
183 | - self.non_versioned_columns = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] + options[:non_versioned_columns].to_a.map(&:to_s) | |
184 | - self.version_association_options = { | |
185 | - :class_name => "#{self.to_s}::#{versioned_class_name}", | |
186 | - :foreign_key => versioned_foreign_key, | |
187 | - :dependent => :delete_all | |
188 | - }.merge(options[:association_options] || {}) | |
189 | - | |
190 | - if block_given? | |
191 | - extension_module_name = "#{versioned_class_name}Extension" | |
192 | - silence_warnings do | |
193 | - self.const_set(extension_module_name, Module.new(&extension)) | |
194 | - end | |
195 | - | |
196 | - options[:extend] = self.const_get(extension_module_name) | |
73 | + # == Configuration options | |
74 | + # | |
75 | + # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example) | |
76 | + # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example) | |
77 | + # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example) | |
78 | + # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type) | |
79 | + # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version) | |
80 | + # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model. | |
81 | + # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited | |
82 | + # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved. | |
83 | + # For finer control, pass either a Proc or modify Model#version_condition_met? | |
84 | + # | |
85 | + # acts_as_versioned :if => Proc.new { |auction| !auction.expired? } | |
86 | + # | |
87 | + # or... | |
88 | + # | |
89 | + # class Auction | |
90 | + # def version_condition_met? # totally bypasses the <tt>:if</tt> option | |
91 | + # !expired? | |
92 | + # end | |
93 | + # end | |
94 | + # | |
95 | + # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes | |
96 | + # either a symbol or array of symbols. | |
97 | + # | |
98 | + # * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block | |
99 | + # to create an anonymous mixin: | |
100 | + # | |
101 | + # class Auction | |
102 | + # acts_as_versioned do | |
103 | + # def started? | |
104 | + # !started_at.nil? | |
105 | + # end | |
106 | + # end | |
107 | + # end | |
108 | + # | |
109 | + # or... | |
110 | + # | |
111 | + # module AuctionExtension | |
112 | + # def started? | |
113 | + # !started_at.nil? | |
114 | + # end | |
115 | + # end | |
116 | + # class Auction | |
117 | + # acts_as_versioned :extend => AuctionExtension | |
118 | + # end | |
119 | + # | |
120 | + # Example code: | |
121 | + # | |
122 | + # @auction = Auction.find(1) | |
123 | + # @auction.started? | |
124 | + # @auction.versions.first.started? | |
125 | + # | |
126 | + # == Database Schema | |
127 | + # | |
128 | + # The model that you're versioning needs to have a 'version' attribute. The model is versioned | |
129 | + # into a table called #{model}_versions where the model name is singlular. The _versions table should | |
130 | + # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field. | |
131 | + # | |
132 | + # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance, | |
133 | + # then that field is reflected in the versioned model as 'versioned_type' by default. | |
134 | + # | |
135 | + # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table | |
136 | + # method, perfect for a migration. It will also create the version column if the main model does not already have it. | |
137 | + # | |
138 | + # class AddVersions < ActiveRecord::Migration | |
139 | + # def self.up | |
140 | + # # create_versioned_table takes the same options hash | |
141 | + # # that create_table does | |
142 | + # Post.create_versioned_table | |
143 | + # end | |
144 | + # | |
145 | + # def self.down | |
146 | + # Post.drop_versioned_table | |
147 | + # end | |
148 | + # end | |
149 | + # | |
150 | + # == Changing What Fields Are Versioned | |
151 | + # | |
152 | + # By default, acts_as_versioned will version all but these fields: | |
153 | + # | |
154 | + # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column] | |
155 | + # | |
156 | + # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols. | |
157 | + # | |
158 | + # class Post < ActiveRecord::Base | |
159 | + # acts_as_versioned | |
160 | + # self.non_versioned_columns << 'comments_count' | |
161 | + # end | |
162 | + # | |
163 | + def acts_as_versioned(options = {}, &extension) | |
164 | + # don't allow multiple calls | |
165 | + return if self.included_modules.include?(ActiveRecord::Acts::Versioned::Behaviors) | |
166 | + | |
167 | + cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, | |
168 | + :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns, | |
169 | + :version_association_options, :version_if_changed, :version_except_columns | |
170 | + | |
171 | + self.versioned_class_name = options[:class_name] || "Version" | |
172 | + self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key | |
173 | + self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}" | |
174 | + self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}" | |
175 | + self.version_column = options[:version_column] || 'version' | |
176 | + self.version_sequence_name = options[:sequence_name] | |
177 | + self.max_version_limit = options[:limit].to_i | |
178 | + self.version_condition = options[:if] || true | |
179 | + self.version_except_columns = [options[:except]].flatten.map(&:to_s) #these columns are kept in _versioned, but changing them does not excplitly cause a version change | |
180 | + self.non_versioned_columns = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] #these columns are excluded from _versions, and changing them does not cause a version change | |
181 | + self.version_association_options = { | |
182 | + :class_name => "#{self.to_s}::#{versioned_class_name}", | |
183 | + :foreign_key => versioned_foreign_key, | |
184 | + :dependent => :delete_all | |
185 | + }.merge(options[:association_options] || {}) | |
186 | + | |
187 | + if block_given? | |
188 | + extension_module_name = "#{versioned_class_name}Extension" | |
189 | + silence_warnings do | |
190 | + self.const_set(extension_module_name, Module.new(&extension)) | |
197 | 191 | end |
198 | 192 | |
199 | - class_eval <<-CLASS_METHODS | |
200 | - has_many :versions, version_association_options do | |
201 | - # finds earliest version of this record | |
202 | - def earliest | |
203 | - @earliest ||= find(:first, :order => '#{version_column}') | |
204 | - end | |
193 | + options[:extend] = self.const_get(extension_module_name) | |
194 | + end | |
205 | 195 | |
206 | - # find latest version of this record | |
207 | - def latest | |
208 | - @latest ||= find(:first, :order => '#{version_column} desc') | |
209 | - end | |
210 | - end | |
211 | - before_save :set_new_version | |
212 | - after_save :save_version | |
213 | - after_save :clear_old_versions | |
214 | - | |
215 | - unless options[:if_changed].nil? | |
216 | - self.track_altered_attributes = true | |
217 | - options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array) | |
218 | - self.version_if_changed = options[:if_changed].map(&:to_s) | |
219 | - end | |
196 | + unless options[:if_changed].nil? | |
197 | + self.track_altered_attributes = true | |
198 | + options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array) | |
199 | + self.version_if_changed = options[:if_changed].map(&:to_s) | |
200 | + end | |
220 | 201 | |
221 | - include options[:extend] if options[:extend].is_a?(Module) | |
222 | - CLASS_METHODS | |
202 | + include options[:extend] if options[:extend].is_a?(Module) | |
223 | 203 | |
224 | - # create the dynamic versioned model | |
225 | - const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do | |
226 | - def self.reloadable? ; false ; end | |
227 | - # find first version before the given version | |
228 | - def self.before(version) | |
229 | - find :first, :order => 'version desc', | |
230 | - :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version] | |
231 | - end | |
204 | + include ActiveRecord::Acts::Versioned::Behaviors | |
232 | 205 | |
233 | - # find first version after the given version. | |
234 | - def self.after(version) | |
235 | - find :first, :order => 'version', | |
236 | - :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version] | |
237 | - end | |
206 | + # | |
207 | + # Create the dynamic versioned model | |
208 | + # | |
209 | + const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do | |
210 | + def self.reloadable?; | |
211 | + false; | |
212 | + end | |
238 | 213 | |
239 | - def previous | |
240 | - self.class.before(self) | |
241 | - end | |
214 | + # find first version before the given version | |
215 | + def self.before(version) | |
216 | + where(["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]). | |
217 | + order('version DESC'). | |
218 | + first | |
219 | + end | |
242 | 220 | |
243 | - def next | |
244 | - self.class.after(self) | |
245 | - end | |
221 | + # find first version after the given version. | |
222 | + def self.after(version) | |
223 | + where(["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]). | |
224 | + order('version ASC'). | |
225 | + first | |
226 | + end | |
246 | 227 | |
247 | - def versions_count | |
248 | - page.version | |
249 | - end | |
228 | + # finds earliest version of this record | |
229 | + def self.earliest | |
230 | + order("#{original_class.version_column}").first | |
231 | + end | |
232 | + | |
233 | + # find latest version of this record | |
234 | + def self.latest | |
235 | + order("#{original_class.version_column} desc").first | |
236 | + end | |
237 | + | |
238 | + def previous | |
239 | + self.class.before(self) | |
240 | + end | |
241 | + | |
242 | + def next | |
243 | + self.class.after(self) | |
250 | 244 | end |
251 | 245 | |
252 | - versioned_class.cattr_accessor :original_class | |
253 | - versioned_class.original_class = self | |
254 | - versioned_class.set_table_name versioned_table_name | |
255 | - versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, | |
256 | - :class_name => "::#{self.to_s}", | |
257 | - :foreign_key => versioned_foreign_key | |
258 | - versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module) | |
259 | - versioned_class.set_sequence_name version_sequence_name if version_sequence_name | |
246 | + def versions_count | |
247 | + page.version | |
248 | + end | |
260 | 249 | end |
250 | + | |
251 | + versioned_class.cattr_accessor :original_class | |
252 | + versioned_class.original_class = self | |
253 | + versioned_class.table_name = versioned_table_name | |
254 | + versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, | |
255 | + :class_name => "::#{self.to_s}", | |
256 | + :foreign_key => versioned_foreign_key | |
257 | + versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module) | |
258 | + versioned_class.set_sequence_name version_sequence_name if version_sequence_name | |
261 | 259 | end |
262 | 260 | |
263 | - module ActMethods | |
264 | - def self.included(base) # :nodoc: | |
265 | - base.extend ClassMethods | |
261 | + module Behaviors | |
262 | + extend ActiveSupport::Concern | |
263 | + | |
264 | + included do | |
265 | + has_many :versions, self.version_association_options | |
266 | + | |
267 | + before_save :set_new_version | |
268 | + after_save :save_version | |
269 | + after_save :clear_old_versions | |
266 | 270 | end |
267 | 271 | |
268 | 272 | # Saves a version of the model in the versioned table. This is called in the after_save callback by default |
... | ... | @@ -292,7 +296,7 @@ module ActiveRecord #:nodoc: |
292 | 296 | if version.is_a?(self.class.versioned_class) |
293 | 297 | return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record? |
294 | 298 | else |
295 | - return false unless version = versions.send("find_by_#{self.class.version_column}", version) | |
299 | + return false unless version = versions.where(self.class.version_column => version).first | |
296 | 300 | end |
297 | 301 | self.clone_versioned_model(version, self) |
298 | 302 | send("#{self.class.version_column}=", version.send(self.class.version_column)) |
... | ... | @@ -320,23 +324,33 @@ module ActiveRecord #:nodoc: |
320 | 324 | end |
321 | 325 | end |
322 | 326 | end |
323 | - | |
327 | + | |
324 | 328 | def altered? |
325 | - track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed? | |
329 | + changed.map { |c| self.class.versioned_columns.map(&:name).include?(c) & !self.class.version_except_columns.include?(c) }.any? | |
326 | 330 | end |
327 | 331 | |
328 | 332 | # Clones a model. Used when saving a new version or reverting a model's version. |
329 | 333 | def clone_versioned_model(orig_model, new_model) |
330 | 334 | self.class.versioned_columns.each do |col| |
331 | - new_model.send("#{col.name}=", orig_model.send(col.name)) if orig_model.has_attribute?(col.name) | |
335 | + next unless orig_model.has_attribute?(col.name) | |
336 | + define_method(new_model, col.name.to_sym) | |
337 | + new_model.send("#{col.name.to_sym}=", orig_model.send(col.name)) | |
332 | 338 | end |
333 | 339 | |
334 | 340 | if orig_model.is_a?(self.class.versioned_class) |
335 | 341 | new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column] |
336 | 342 | elsif new_model.is_a?(self.class.versioned_class) |
337 | - new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column] | |
343 | + sym = self.class.versioned_inheritance_column.to_sym | |
344 | + define_method new_model, sym | |
345 | + new_model.send("#{sym}=", orig_model[orig_model.class.inheritance_column]) if orig_model[orig_model.class.inheritance_column] | |
338 | 346 | end |
339 | 347 | end |
348 | + | |
349 | + def define_method(object, method) | |
350 | + return if object.methods.include? method | |
351 | + metaclass = class << object; self; end | |
352 | + metaclass.send :attr_accessor, method | |
353 | + end | |
340 | 354 | |
341 | 355 | # Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>. |
342 | 356 | def save_version? |
... | ... | @@ -347,12 +361,12 @@ module ActiveRecord #:nodoc: |
347 | 361 | # custom version condition checking. |
348 | 362 | def version_condition_met? |
349 | 363 | case |
350 | - when version_condition.is_a?(Symbol) | |
351 | - send(version_condition) | |
352 | - when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1) | |
353 | - version_condition.call(self) | |
354 | - else | |
355 | - version_condition | |
364 | + when version_condition.is_a?(Symbol) | |
365 | + send(version_condition) | |
366 | + when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1) | |
367 | + version_condition.call(self) | |
368 | + else | |
369 | + version_condition | |
356 | 370 | end |
357 | 371 | end |
358 | 372 | |
... | ... | @@ -376,19 +390,23 @@ module ActiveRecord #:nodoc: |
376 | 390 | self.class.without_locking(&block) |
377 | 391 | end |
378 | 392 | |
379 | - def empty_callback() end #:nodoc: | |
393 | + def empty_callback() | |
394 | + end | |
395 | + | |
396 | + #:nodoc: | |
380 | 397 | |
381 | 398 | protected |
382 | - # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version. | |
383 | - def set_new_version | |
384 | - @saving_version = new_record? || save_version? | |
385 | - self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?) | |
386 | - end | |
399 | + # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version. | |
400 | + def set_new_version | |
401 | + @saving_version = new_record? || save_version? | |
402 | + self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?) | |
403 | + end | |
404 | + | |
405 | + # Gets the next available version for the current record, or 1 for a new record | |
406 | + def next_version | |
407 | + (new_record? ? 0 : versions.calculate(:maximum, version_column).to_i) + 1 | |
408 | + end | |
387 | 409 | |
388 | - # Gets the next available version for the current record, or 1 for a new record | |
389 | - def next_version | |
390 | - (new_record? ? 0 : versions.calculate(:max, version_column).to_i) + 1 | |
391 | - end | |
392 | 410 | |
393 | 411 | module ClassMethods |
394 | 412 | # Returns an array of columns that are versioned. See non_versioned_columns |
... | ... | @@ -410,28 +428,28 @@ module ActiveRecord #:nodoc: |
410 | 428 | end |
411 | 429 | |
412 | 430 | return if connection.table_exists?(versioned_table_name) |
413 | - | |
431 | + | |
414 | 432 | self.connection.create_table(versioned_table_name, create_table_options) do |t| |
415 | 433 | t.column versioned_foreign_key, :integer |
416 | 434 | t.column version_column, :integer |
417 | 435 | end |
418 | 436 | |
419 | - self.versioned_columns.each do |col| | |
420 | - self.connection.add_column versioned_table_name, col.name, col.type, | |
421 | - :limit => col.limit, | |
422 | - :default => col.default, | |
423 | - :scale => col.scale, | |
424 | - :precision => col.precision | |
437 | + self.versioned_columns.each do |col| | |
438 | + self.connection.add_column versioned_table_name, col.name, col.type, | |
439 | + :limit => col.limit, | |
440 | + :default => col.default, | |
441 | + :scale => col.scale, | |
442 | + :precision => col.precision | |
425 | 443 | end |
426 | 444 | |
427 | 445 | if type_col = self.columns_hash[inheritance_column] |
428 | - self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type, | |
429 | - :limit => type_col.limit, | |
430 | - :default => type_col.default, | |
431 | - :scale => type_col.scale, | |
432 | - :precision => type_col.precision | |
446 | + self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type, | |
447 | + :limit => type_col.limit, | |
448 | + :default => type_col.default, | |
449 | + :scale => type_col.scale, | |
450 | + :precision => type_col.precision | |
433 | 451 | end |
434 | - | |
452 | + | |
435 | 453 | self.connection.add_index versioned_table_name, versioned_foreign_key |
436 | 454 | end |
437 | 455 | |
... | ... | @@ -447,7 +465,7 @@ module ActiveRecord #:nodoc: |
447 | 465 | # end |
448 | 466 | # |
449 | 467 | def without_revision(&block) |
450 | - class_eval do | |
468 | + class_eval do | |
451 | 469 | CALLBACKS.each do |attr_name| |
452 | 470 | alias_method "orig_#{attr_name}".to_sym, attr_name |
453 | 471 | alias_method attr_name, :empty_callback |
... | ... | @@ -455,7 +473,7 @@ module ActiveRecord #:nodoc: |
455 | 473 | end |
456 | 474 | block.call |
457 | 475 | ensure |
458 | - class_eval do | |
476 | + class_eval do | |
459 | 477 | CALLBACKS.each do |attr_name| |
460 | 478 | alias_method attr_name, "orig_#{attr_name}".to_sym |
461 | 479 | end |
... | ... | @@ -483,4 +501,4 @@ module ActiveRecord #:nodoc: |
483 | 501 | end |
484 | 502 | end |
485 | 503 | |
486 | -ActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned | |
504 | +ActiveRecord::Base.extend ActiveRecord::Acts::Versioned | ... | ... |
vendor/plugins/acts_as_versioned/test/abstract_unit.rb
... | ... | @@ -1,48 +0,0 @@ |
1 | -$:.unshift(File.dirname(__FILE__) + '/../../../rails/activesupport/lib') | |
2 | -$:.unshift(File.dirname(__FILE__) + '/../../../rails/activerecord/lib') | |
3 | -$:.unshift(File.dirname(__FILE__) + '/../lib') | |
4 | -require 'test/unit' | |
5 | -begin | |
6 | - require 'active_support' | |
7 | - require 'active_record' | |
8 | - require 'active_record/fixtures' | |
9 | -rescue LoadError | |
10 | - require 'rubygems' | |
11 | - retry | |
12 | -end | |
13 | - | |
14 | -begin | |
15 | - require 'ruby-debug' | |
16 | - Debugger.start | |
17 | -rescue LoadError | |
18 | -end | |
19 | - | |
20 | -require 'acts_as_versioned' | |
21 | - | |
22 | -config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) | |
23 | -ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log") | |
24 | -ActiveRecord::Base.configurations = {'test' => config[ENV['DB'] || 'sqlite3']} | |
25 | -ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) | |
26 | - | |
27 | -load(File.dirname(__FILE__) + "/schema.rb") | |
28 | - | |
29 | -# set up custom sequence on widget_versions for DBs that support sequences | |
30 | -if ENV['DB'] == 'postgresql' | |
31 | - ActiveRecord::Base.connection.execute "DROP SEQUENCE widgets_seq;" rescue nil | |
32 | - ActiveRecord::Base.connection.remove_column :widget_versions, :id | |
33 | - ActiveRecord::Base.connection.execute "CREATE SEQUENCE widgets_seq START 101;" | |
34 | - ActiveRecord::Base.connection.execute "ALTER TABLE widget_versions ADD COLUMN id INTEGER PRIMARY KEY DEFAULT nextval('widgets_seq');" | |
35 | -end | |
36 | - | |
37 | -Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/" | |
38 | -$:.unshift(Test::Unit::TestCase.fixture_path) | |
39 | - | |
40 | -class Test::Unit::TestCase #:nodoc: | |
41 | - # Turn off transactional fixtures if you're working with MyISAM tables in MySQL | |
42 | - self.use_transactional_fixtures = true | |
43 | - | |
44 | - # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david) | |
45 | - self.use_instantiated_fixtures = false | |
46 | - | |
47 | - # Add more helper methods to be used by all tests here... | |
48 | -end | |
49 | 0 | \ No newline at end of file |
vendor/plugins/acts_as_versioned/test/database.yml
... | ... | @@ -1,18 +0,0 @@ |
1 | -sqlite: | |
2 | - :adapter: sqlite | |
3 | - :dbfile: acts_as_versioned_plugin.sqlite.db | |
4 | -sqlite3: | |
5 | - :adapter: sqlite3 | |
6 | - :dbfile: acts_as_versioned_plugin.sqlite3.db | |
7 | -postgresql: | |
8 | - :adapter: postgresql | |
9 | - :username: postgres | |
10 | - :password: postgres | |
11 | - :database: acts_as_versioned_plugin_test | |
12 | - :min_messages: ERROR | |
13 | -mysql: | |
14 | - :adapter: mysql | |
15 | - :host: localhost | |
16 | - :username: rails | |
17 | - :password: | |
18 | - :database: acts_as_versioned_plugin_test | |
19 | 0 | \ No newline at end of file |
vendor/plugins/acts_as_versioned/test/fixtures/authors.yml
vendor/plugins/acts_as_versioned/test/fixtures/landmark.rb
vendor/plugins/acts_as_versioned/test/fixtures/landmark_versions.yml
vendor/plugins/acts_as_versioned/test/fixtures/landmarks.yml
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages.yml
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages_revisions.yml
... | ... | @@ -1,27 +0,0 @@ |
1 | -welcome_1: | |
2 | - id: 1 | |
3 | - page_id: 1 | |
4 | - title: Welcome to the weblg | |
5 | - lock_version: 23 | |
6 | - version_type: LockedPage | |
7 | - | |
8 | -welcome_2: | |
9 | - id: 2 | |
10 | - page_id: 1 | |
11 | - title: Welcome to the weblog | |
12 | - lock_version: 24 | |
13 | - version_type: LockedPage | |
14 | - | |
15 | -thinking_1: | |
16 | - id: 3 | |
17 | - page_id: 2 | |
18 | - title: So I was thinking!!! | |
19 | - lock_version: 23 | |
20 | - version_type: SpecialLockedPage | |
21 | - | |
22 | -thinking_2: | |
23 | - id: 4 | |
24 | - page_id: 2 | |
25 | - title: So I was thinking | |
26 | - lock_version: 24 | |
27 | - version_type: SpecialLockedPage |
vendor/plugins/acts_as_versioned/test/fixtures/migrations/1_add_versioned_tables.rb
... | ... | @@ -1,15 +0,0 @@ |
1 | -class AddVersionedTables < ActiveRecord::Migration | |
2 | - def self.up | |
3 | - create_table("things") do |t| | |
4 | - t.column :title, :text | |
5 | - t.column :price, :decimal, :precision => 7, :scale => 2 | |
6 | - t.column :type, :string | |
7 | - end | |
8 | - Thing.create_versioned_table | |
9 | - end | |
10 | - | |
11 | - def self.down | |
12 | - Thing.drop_versioned_table | |
13 | - drop_table "things" rescue nil | |
14 | - end | |
15 | -end | |
16 | 0 | \ No newline at end of file |
vendor/plugins/acts_as_versioned/test/fixtures/page.rb
... | ... | @@ -1,43 +0,0 @@ |
1 | -class Page < ActiveRecord::Base | |
2 | - belongs_to :author | |
3 | - has_many :authors, :through => :versions, :order => 'name' | |
4 | - belongs_to :revisor, :class_name => 'Author' | |
5 | - has_many :revisors, :class_name => 'Author', :through => :versions, :order => 'name' | |
6 | - acts_as_versioned :if => :feeling_good? do | |
7 | - def self.included(base) | |
8 | - base.cattr_accessor :feeling_good | |
9 | - base.feeling_good = true | |
10 | - base.belongs_to :author | |
11 | - base.belongs_to :revisor, :class_name => 'Author' | |
12 | - end | |
13 | - | |
14 | - def feeling_good? | |
15 | - @@feeling_good == true | |
16 | - end | |
17 | - end | |
18 | -end | |
19 | - | |
20 | -module LockedPageExtension | |
21 | - def hello_world | |
22 | - 'hello_world' | |
23 | - end | |
24 | -end | |
25 | - | |
26 | -class LockedPage < ActiveRecord::Base | |
27 | - acts_as_versioned \ | |
28 | - :inheritance_column => :version_type, | |
29 | - :foreign_key => :page_id, | |
30 | - :table_name => :locked_pages_revisions, | |
31 | - :class_name => 'LockedPageRevision', | |
32 | - :version_column => :lock_version, | |
33 | - :limit => 2, | |
34 | - :if_changed => :title, | |
35 | - :extend => LockedPageExtension | |
36 | -end | |
37 | - | |
38 | -class SpecialLockedPage < LockedPage | |
39 | -end | |
40 | - | |
41 | -class Author < ActiveRecord::Base | |
42 | - has_many :pages | |
43 | -end | |
44 | 0 | \ No newline at end of file |
vendor/plugins/acts_as_versioned/test/fixtures/page_versions.yml
... | ... | @@ -1,16 +0,0 @@ |
1 | -welcome_2: | |
2 | - id: 1 | |
3 | - page_id: 1 | |
4 | - title: Welcome to the weblog | |
5 | - body: Such a lovely day | |
6 | - version: 24 | |
7 | - author_id: 1 | |
8 | - revisor_id: 1 | |
9 | -welcome_1: | |
10 | - id: 2 | |
11 | - page_id: 1 | |
12 | - title: Welcome to the weblg | |
13 | - body: Such a lovely day | |
14 | - version: 23 | |
15 | - author_id: 2 | |
16 | - revisor_id: 2 |
vendor/plugins/acts_as_versioned/test/fixtures/pages.yml
vendor/plugins/acts_as_versioned/test/fixtures/widget.rb
vendor/plugins/acts_as_versioned/test/migration_test.rb
... | ... | @@ -1,46 +0,0 @@ |
1 | -require File.join(File.dirname(__FILE__), 'abstract_unit') | |
2 | - | |
3 | -if ActiveRecord::Base.connection.supports_migrations? | |
4 | - class Thing < ActiveRecord::Base | |
5 | - attr_accessor :version | |
6 | - acts_as_versioned | |
7 | - end | |
8 | - | |
9 | - class MigrationTest < Test::Unit::TestCase | |
10 | - self.use_transactional_fixtures = false | |
11 | - def teardown | |
12 | - if ActiveRecord::Base.connection.respond_to?(:initialize_schema_information) | |
13 | - ActiveRecord::Base.connection.initialize_schema_information | |
14 | - ActiveRecord::Base.connection.update "UPDATE schema_info SET version = 0" | |
15 | - else | |
16 | - ActiveRecord::Base.connection.initialize_schema_migrations_table | |
17 | - ActiveRecord::Base.connection.assume_migrated_upto_version(0) | |
18 | - end | |
19 | - | |
20 | - Thing.connection.drop_table "things" rescue nil | |
21 | - Thing.connection.drop_table "thing_versions" rescue nil | |
22 | - Thing.reset_column_information | |
23 | - end | |
24 | - | |
25 | - def test_versioned_migration | |
26 | - assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' } | |
27 | - # take 'er up | |
28 | - ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/') | |
29 | - t = Thing.create :title => 'blah blah', :price => 123.45, :type => 'Thing' | |
30 | - assert_equal 1, t.versions.size | |
31 | - | |
32 | - # check that the price column has remembered its value correctly | |
33 | - assert_equal t.price, t.versions.first.price | |
34 | - assert_equal t.title, t.versions.first.title | |
35 | - assert_equal t[:type], t.versions.first[:type] | |
36 | - | |
37 | - # make sure that the precision of the price column has been preserved | |
38 | - assert_equal 7, Thing::Version.columns.find{|c| c.name == "price"}.precision | |
39 | - assert_equal 2, Thing::Version.columns.find{|c| c.name == "price"}.scale | |
40 | - | |
41 | - # now lets take 'er back down | |
42 | - ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/') | |
43 | - assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' } | |
44 | - end | |
45 | - end | |
46 | -end |
vendor/plugins/acts_as_versioned/test/schema.rb
... | ... | @@ -1,82 +0,0 @@ |
1 | -ActiveRecord::Schema.define(:version => 0) do | |
2 | - create_table :pages, :force => true do |t| | |
3 | - t.column :version, :integer | |
4 | - t.column :title, :string, :limit => 255 | |
5 | - t.column :body, :text | |
6 | - t.column :created_on, :datetime | |
7 | - t.column :updated_on, :datetime | |
8 | - t.column :author_id, :integer | |
9 | - t.column :revisor_id, :integer | |
10 | - end | |
11 | - | |
12 | - create_table :page_versions, :force => true do |t| | |
13 | - t.column :page_id, :integer | |
14 | - t.column :version, :integer | |
15 | - t.column :title, :string, :limit => 255 | |
16 | - t.column :body, :text | |
17 | - t.column :created_on, :datetime | |
18 | - t.column :updated_on, :datetime | |
19 | - t.column :author_id, :integer | |
20 | - t.column :revisor_id, :integer | |
21 | - end | |
22 | - | |
23 | - add_index :page_versions, [:page_id, :version], :unique => true | |
24 | - | |
25 | - create_table :authors, :force => true do |t| | |
26 | - t.column :page_id, :integer | |
27 | - t.column :name, :string | |
28 | - end | |
29 | - | |
30 | - create_table :locked_pages, :force => true do |t| | |
31 | - t.column :lock_version, :integer | |
32 | - t.column :title, :string, :limit => 255 | |
33 | - t.column :body, :text | |
34 | - t.column :type, :string, :limit => 255 | |
35 | - end | |
36 | - | |
37 | - create_table :locked_pages_revisions, :force => true do |t| | |
38 | - t.column :page_id, :integer | |
39 | - t.column :lock_version, :integer | |
40 | - t.column :title, :string, :limit => 255 | |
41 | - t.column :body, :text | |
42 | - t.column :version_type, :string, :limit => 255 | |
43 | - t.column :updated_at, :datetime | |
44 | - end | |
45 | - | |
46 | - add_index :locked_pages_revisions, [:page_id, :lock_version], :unique => true | |
47 | - | |
48 | - create_table :widgets, :force => true do |t| | |
49 | - t.column :name, :string, :limit => 50 | |
50 | - t.column :foo, :string | |
51 | - t.column :version, :integer | |
52 | - t.column :updated_at, :datetime | |
53 | - end | |
54 | - | |
55 | - create_table :widget_versions, :force => true do |t| | |
56 | - t.column :widget_id, :integer | |
57 | - t.column :name, :string, :limit => 50 | |
58 | - t.column :version, :integer | |
59 | - t.column :updated_at, :datetime | |
60 | - end | |
61 | - | |
62 | - add_index :widget_versions, [:widget_id, :version], :unique => true | |
63 | - | |
64 | - create_table :landmarks, :force => true do |t| | |
65 | - t.column :name, :string | |
66 | - t.column :latitude, :float | |
67 | - t.column :longitude, :float | |
68 | - t.column :doesnt_trigger_version,:string | |
69 | - t.column :version, :integer | |
70 | - end | |
71 | - | |
72 | - create_table :landmark_versions, :force => true do |t| | |
73 | - t.column :landmark_id, :integer | |
74 | - t.column :name, :string | |
75 | - t.column :latitude, :float | |
76 | - t.column :longitude, :float | |
77 | - t.column :doesnt_trigger_version,:string | |
78 | - t.column :version, :integer | |
79 | - end | |
80 | - | |
81 | - add_index :landmark_versions, [:landmark_id, :version], :unique => true | |
82 | -end |
vendor/plugins/acts_as_versioned/test/versioned_test.rb
... | ... | @@ -1,370 +0,0 @@ |
1 | -require File.join(File.dirname(__FILE__), 'abstract_unit') | |
2 | -require File.join(File.dirname(__FILE__), 'fixtures/page') | |
3 | -require File.join(File.dirname(__FILE__), 'fixtures/widget') | |
4 | - | |
5 | -class VersionedTest < Test::Unit::TestCase | |
6 | - fixtures :pages, :page_versions, :locked_pages, :locked_pages_revisions, :authors, :landmarks, :landmark_versions | |
7 | - set_fixture_class :page_versions => Page::Version | |
8 | - | |
9 | - def test_saves_versioned_copy | |
10 | - p = Page.create! :title => 'first title', :body => 'first body' | |
11 | - assert !p.new_record? | |
12 | - assert_equal 1, p.versions.size | |
13 | - assert_equal 1, p.version | |
14 | - assert_instance_of Page.versioned_class, p.versions.first | |
15 | - end | |
16 | - | |
17 | - def test_saves_without_revision | |
18 | - p = pages(:welcome) | |
19 | - old_versions = p.versions.count | |
20 | - | |
21 | - p.save_without_revision | |
22 | - | |
23 | - p.without_revision do | |
24 | - p.update_attributes :title => 'changed' | |
25 | - end | |
26 | - | |
27 | - assert_equal old_versions, p.versions.count | |
28 | - end | |
29 | - | |
30 | - def test_rollback_with_version_number | |
31 | - p = pages(:welcome) | |
32 | - assert_equal 24, p.version | |
33 | - assert_equal 'Welcome to the weblog', p.title | |
34 | - | |
35 | - assert p.revert_to!(23), "Couldn't revert to 23" | |
36 | - assert_equal 23, p.version | |
37 | - assert_equal 'Welcome to the weblg', p.title | |
38 | - end | |
39 | - | |
40 | - def test_versioned_class_name | |
41 | - assert_equal 'Version', Page.versioned_class_name | |
42 | - assert_equal 'LockedPageRevision', LockedPage.versioned_class_name | |
43 | - end | |
44 | - | |
45 | - def test_versioned_class | |
46 | - assert_equal Page::Version, Page.versioned_class | |
47 | - assert_equal LockedPage::LockedPageRevision, LockedPage.versioned_class | |
48 | - end | |
49 | - | |
50 | - def test_special_methods | |
51 | - assert_nothing_raised { pages(:welcome).feeling_good? } | |
52 | - assert_nothing_raised { pages(:welcome).versions.first.feeling_good? } | |
53 | - assert_nothing_raised { locked_pages(:welcome).hello_world } | |
54 | - assert_nothing_raised { locked_pages(:welcome).versions.first.hello_world } | |
55 | - end | |
56 | - | |
57 | - def test_rollback_with_version_class | |
58 | - p = pages(:welcome) | |
59 | - assert_equal 24, p.version | |
60 | - assert_equal 'Welcome to the weblog', p.title | |
61 | - | |
62 | - assert p.revert_to!(p.versions.find_by_version(23)), "Couldn't revert to 23" | |
63 | - assert_equal 23, p.version | |
64 | - assert_equal 'Welcome to the weblg', p.title | |
65 | - end | |
66 | - | |
67 | - def test_rollback_fails_with_invalid_revision | |
68 | - p = locked_pages(:welcome) | |
69 | - assert !p.revert_to!(locked_pages(:thinking)) | |
70 | - end | |
71 | - | |
72 | - def test_saves_versioned_copy_with_options | |
73 | - p = LockedPage.create! :title => 'first title' | |
74 | - assert !p.new_record? | |
75 | - assert_equal 1, p.versions.size | |
76 | - assert_instance_of LockedPage.versioned_class, p.versions.first | |
77 | - end | |
78 | - | |
79 | - def test_rollback_with_version_number_with_options | |
80 | - p = locked_pages(:welcome) | |
81 | - assert_equal 'Welcome to the weblog', p.title | |
82 | - assert_equal 'LockedPage', p.versions.first.version_type | |
83 | - | |
84 | - assert p.revert_to!(p.versions.first.lock_version), "Couldn't revert to 23" | |
85 | - assert_equal 'Welcome to the weblg', p.title | |
86 | - assert_equal 'LockedPage', p.versions.first.version_type | |
87 | - end | |
88 | - | |
89 | - def test_rollback_with_version_class_with_options | |
90 | - p = locked_pages(:welcome) | |
91 | - assert_equal 'Welcome to the weblog', p.title | |
92 | - assert_equal 'LockedPage', p.versions.first.version_type | |
93 | - | |
94 | - assert p.revert_to!(p.versions.first), "Couldn't revert to 1" | |
95 | - assert_equal 'Welcome to the weblg', p.title | |
96 | - assert_equal 'LockedPage', p.versions.first.version_type | |
97 | - end | |
98 | - | |
99 | - def test_saves_versioned_copy_with_sti | |
100 | - p = SpecialLockedPage.create! :title => 'first title' | |
101 | - assert !p.new_record? | |
102 | - assert_equal 1, p.versions.size | |
103 | - assert_instance_of LockedPage.versioned_class, p.versions.first | |
104 | - assert_equal 'SpecialLockedPage', p.versions.first.version_type | |
105 | - end | |
106 | - | |
107 | - def test_rollback_with_version_number_with_sti | |
108 | - p = locked_pages(:thinking) | |
109 | - assert_equal 'So I was thinking', p.title | |
110 | - | |
111 | - assert p.revert_to!(p.versions.first.lock_version), "Couldn't revert to 1" | |
112 | - assert_equal 'So I was thinking!!!', p.title | |
113 | - assert_equal 'SpecialLockedPage', p.versions.first.version_type | |
114 | - end | |
115 | - | |
116 | - def test_lock_version_works_with_versioning | |
117 | - p = locked_pages(:thinking) | |
118 | - p2 = LockedPage.find(p.id) | |
119 | - | |
120 | - p.title = 'fresh title' | |
121 | - p.save | |
122 | - assert_equal 2, p.versions.size # limit! | |
123 | - | |
124 | - assert_raises(ActiveRecord::StaleObjectError) do | |
125 | - p2.title = 'stale title' | |
126 | - p2.save | |
127 | - end | |
128 | - end | |
129 | - | |
130 | - def test_version_if_condition | |
131 | - p = Page.create! :title => "title" | |
132 | - assert_equal 1, p.version | |
133 | - | |
134 | - Page.feeling_good = false | |
135 | - p.save | |
136 | - assert_equal 1, p.version | |
137 | - Page.feeling_good = true | |
138 | - end | |
139 | - | |
140 | - def test_version_if_condition2 | |
141 | - # set new if condition | |
142 | - Page.class_eval do | |
143 | - def new_feeling_good() title[0..0] == 'a'; end | |
144 | - alias_method :old_feeling_good, :feeling_good? | |
145 | - alias_method :feeling_good?, :new_feeling_good | |
146 | - end | |
147 | - | |
148 | - p = Page.create! :title => "title" | |
149 | - assert_equal 1, p.version # version does not increment | |
150 | - assert_equal 1, p.versions.count | |
151 | - | |
152 | - p.update_attributes(:title => 'new title') | |
153 | - assert_equal 1, p.version # version does not increment | |
154 | - assert_equal 1, p.versions.count | |
155 | - | |
156 | - p.update_attributes(:title => 'a title') | |
157 | - assert_equal 2, p.version | |
158 | - assert_equal 2, p.versions.count | |
159 | - | |
160 | - # reset original if condition | |
161 | - Page.class_eval { alias_method :feeling_good?, :old_feeling_good } | |
162 | - end | |
163 | - | |
164 | - def test_version_if_condition_with_block | |
165 | - # set new if condition | |
166 | - old_condition = Page.version_condition | |
167 | - Page.version_condition = Proc.new { |page| page.title[0..0] == 'b' } | |
168 | - | |
169 | - p = Page.create! :title => "title" | |
170 | - assert_equal 1, p.version # version does not increment | |
171 | - assert_equal 1, p.versions.count | |
172 | - | |
173 | - p.update_attributes(:title => 'a title') | |
174 | - assert_equal 1, p.version # version does not increment | |
175 | - assert_equal 1, p.versions.count | |
176 | - | |
177 | - p.update_attributes(:title => 'b title') | |
178 | - assert_equal 2, p.version | |
179 | - assert_equal 2, p.versions.count | |
180 | - | |
181 | - # reset original if condition | |
182 | - Page.version_condition = old_condition | |
183 | - end | |
184 | - | |
185 | - def test_version_no_limit | |
186 | - p = Page.create! :title => "title", :body => 'first body' | |
187 | - p.save | |
188 | - p.save | |
189 | - 5.times do |i| | |
190 | - p.title = "title#{i}" | |
191 | - p.save | |
192 | - assert_equal "title#{i}", p.title | |
193 | - assert_equal (i+2), p.version | |
194 | - end | |
195 | - end | |
196 | - | |
197 | - def test_version_max_limit | |
198 | - p = LockedPage.create! :title => "title" | |
199 | - p.update_attributes(:title => "title1") | |
200 | - p.update_attributes(:title => "title2") | |
201 | - 5.times do |i| | |
202 | - p.title = "title#{i}" | |
203 | - p.save | |
204 | - assert_equal "title#{i}", p.title | |
205 | - assert_equal (i+4), p.lock_version | |
206 | - assert p.versions(true).size <= 2, "locked version can only store 2 versions" | |
207 | - end | |
208 | - end | |
209 | - | |
210 | - def test_track_altered_attributes_default_value | |
211 | - assert !Page.track_altered_attributes | |
212 | - assert LockedPage.track_altered_attributes | |
213 | - assert SpecialLockedPage.track_altered_attributes | |
214 | - end | |
215 | - | |
216 | - def test_track_altered_attributes | |
217 | - p = LockedPage.create! :title => "title" | |
218 | - assert_equal 1, p.lock_version | |
219 | - assert_equal 1, p.versions(true).size | |
220 | - | |
221 | - p.body = 'whoa' | |
222 | - assert !p.save_version? | |
223 | - p.save | |
224 | - assert_equal 2, p.lock_version # still increments version because of optimistic locking | |
225 | - assert_equal 1, p.versions(true).size | |
226 | - | |
227 | - p.title = 'updated title' | |
228 | - assert p.save_version? | |
229 | - p.save | |
230 | - assert_equal 3, p.lock_version | |
231 | - assert_equal 1, p.versions(true).size # version 1 deleted | |
232 | - | |
233 | - p.title = 'updated title!' | |
234 | - assert p.save_version? | |
235 | - p.save | |
236 | - assert_equal 4, p.lock_version | |
237 | - assert_equal 2, p.versions(true).size # version 1 deleted | |
238 | - end | |
239 | - | |
240 | - def test_find_versions | |
241 | - assert_equal 1, locked_pages(:welcome).versions.find(:all, :conditions => ['title LIKE ?', '%weblog%']).size | |
242 | - end | |
243 | - | |
244 | - def test_find_version | |
245 | - assert_equal page_versions(:welcome_1), pages(:welcome).versions.find_by_version(23) | |
246 | - end | |
247 | - | |
248 | - def test_with_sequence | |
249 | - assert_equal 'widgets_seq', Widget.versioned_class.sequence_name | |
250 | - 3.times { Widget.create! :name => 'new widget' } | |
251 | - assert_equal 3, Widget.count | |
252 | - assert_equal 3, Widget.versioned_class.count | |
253 | - end | |
254 | - | |
255 | - def test_has_many_through | |
256 | - assert_equal [authors(:caged), authors(:mly)], pages(:welcome).authors | |
257 | - end | |
258 | - | |
259 | - def test_has_many_through_with_custom_association | |
260 | - assert_equal [authors(:caged), authors(:mly)], pages(:welcome).revisors | |
261 | - end | |
262 | - | |
263 | - def test_referential_integrity | |
264 | - pages(:welcome).destroy | |
265 | - assert_equal 0, Page.count | |
266 | - assert_equal 0, Page::Version.count | |
267 | - end | |
268 | - | |
269 | - def test_association_options | |
270 | - association = Page.reflect_on_association(:versions) | |
271 | - options = association.options | |
272 | - assert_equal :delete_all, options[:dependent] | |
273 | - | |
274 | - association = Widget.reflect_on_association(:versions) | |
275 | - options = association.options | |
276 | - assert_equal :nullify, options[:dependent] | |
277 | - assert_equal 'version desc', options[:order] | |
278 | - assert_equal 'widget_id', options[:foreign_key] | |
279 | - | |
280 | - widget = Widget.create! :name => 'new widget' | |
281 | - assert_equal 1, Widget.count | |
282 | - assert_equal 1, Widget.versioned_class.count | |
283 | - widget.destroy | |
284 | - assert_equal 0, Widget.count | |
285 | - assert_equal 1, Widget.versioned_class.count | |
286 | - end | |
287 | - | |
288 | - def test_versioned_records_should_belong_to_parent | |
289 | - page = pages(:welcome) | |
290 | - page_version = page.versions.last | |
291 | - assert_equal page, page_version.page | |
292 | - end | |
293 | - | |
294 | - def test_unaltered_attributes | |
295 | - landmarks(:washington).attributes = landmarks(:washington).attributes.except("id") | |
296 | - assert !landmarks(:washington).changed? | |
297 | - end | |
298 | - | |
299 | - def test_unchanged_string_attributes | |
300 | - landmarks(:washington).attributes = landmarks(:washington).attributes.except("id").inject({}) { |params, (key, value)| params.update(key => value.to_s) } | |
301 | - assert !landmarks(:washington).changed? | |
302 | - end | |
303 | - | |
304 | - def test_should_find_earliest_version | |
305 | - assert_equal page_versions(:welcome_1), pages(:welcome).versions.earliest | |
306 | - end | |
307 | - | |
308 | - def test_should_find_latest_version | |
309 | - assert_equal page_versions(:welcome_2), pages(:welcome).versions.latest | |
310 | - end | |
311 | - | |
312 | - def test_should_find_previous_version | |
313 | - assert_equal page_versions(:welcome_1), page_versions(:welcome_2).previous | |
314 | - assert_equal page_versions(:welcome_1), pages(:welcome).versions.before(page_versions(:welcome_2)) | |
315 | - end | |
316 | - | |
317 | - def test_should_find_next_version | |
318 | - assert_equal page_versions(:welcome_2), page_versions(:welcome_1).next | |
319 | - assert_equal page_versions(:welcome_2), pages(:welcome).versions.after(page_versions(:welcome_1)) | |
320 | - end | |
321 | - | |
322 | - def test_should_find_version_count | |
323 | - assert_equal 2, pages(:welcome).versions.size | |
324 | - end | |
325 | - | |
326 | - def test_if_changed_creates_version_if_a_listed_column_is_changed | |
327 | - landmarks(:washington).name = "Washington" | |
328 | - assert landmarks(:washington).changed? | |
329 | - assert landmarks(:washington).altered? | |
330 | - end | |
331 | - | |
332 | - def test_if_changed_creates_version_if_all_listed_columns_are_changed | |
333 | - landmarks(:washington).name = "Washington" | |
334 | - landmarks(:washington).latitude = 1.0 | |
335 | - landmarks(:washington).longitude = 1.0 | |
336 | - assert landmarks(:washington).changed? | |
337 | - assert landmarks(:washington).altered? | |
338 | - end | |
339 | - | |
340 | - def test_if_changed_does_not_create_new_version_if_unlisted_column_is_changed | |
341 | - landmarks(:washington).doesnt_trigger_version = "This should not trigger version" | |
342 | - assert landmarks(:washington).changed? | |
343 | - assert !landmarks(:washington).altered? | |
344 | - end | |
345 | - | |
346 | - def test_without_locking_temporarily_disables_optimistic_locking | |
347 | - enabled1 = false | |
348 | - block_called = false | |
349 | - | |
350 | - ActiveRecord::Base.lock_optimistically = true | |
351 | - LockedPage.without_locking do | |
352 | - enabled1 = ActiveRecord::Base.lock_optimistically | |
353 | - block_called = true | |
354 | - end | |
355 | - enabled2 = ActiveRecord::Base.lock_optimistically | |
356 | - | |
357 | - assert block_called | |
358 | - assert !enabled1 | |
359 | - assert enabled2 | |
360 | - end | |
361 | - | |
362 | - def test_without_locking_reverts_optimistic_locking_settings_if_block_raises_exception | |
363 | - assert_raises(RuntimeError) do | |
364 | - LockedPage.without_locking do | |
365 | - raise RuntimeError, "oh noes" | |
366 | - end | |
367 | - end | |
368 | - assert ActiveRecord::Base.lock_optimistically | |
369 | - end | |
370 | -end | |
371 | 0 | \ No newline at end of file |