mirror of
https://github.com/ENSL/ensl.org.git
synced 2025-05-31 00:41:20 +00:00
Purged git history and removed sensitive information.
This commit is contained in:
commit
6bcc8dc76b
862 changed files with 25312 additions and 0 deletions
47
vendor/plugins/acts-as-readable/README
vendored
Normal file
47
vendor/plugins/acts-as-readable/README
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
ActsAsReadable
|
||||
==============
|
||||
|
||||
ActsAsReadable allows you to create a generic relationship of items which can
|
||||
be marked as 'read' by users. This is useful for forums or any other kind of
|
||||
situation where you might need to know whether or not a user has seen a particular
|
||||
model.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To install the plugin just install from the SVN:
|
||||
|
||||
script/plugin install http://svn.intridea.com/svn/public/acts_as_readable
|
||||
|
||||
You will need the readings table to use this plugin. A generator has been included,
|
||||
simply type
|
||||
|
||||
script/generate acts_as_readable_migration
|
||||
|
||||
to get the standard migration created for you.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
acts_as_readable
|
||||
end
|
||||
|
||||
bob = User.find_by_name("bob")
|
||||
|
||||
bob.readings # => []
|
||||
|
||||
Post.find_unread_by(bob) # => [<Post 1>,<Post 2>,<Post 3>...]
|
||||
Post.find_read_by(bob) # => []
|
||||
|
||||
Post.find(1).read_by?(bob) # => false
|
||||
Post.find(1).read_by!(bob) # => <Reading 1>
|
||||
Post.find(1).read_by?(bob) # => true
|
||||
Post.find(1).users_who_read # => [<User bob>]
|
||||
|
||||
Post.find_unread_by(bob) # => [<Post 2>,<Post 3>...]
|
||||
Post.find_read_by(bob) # => [<Post 1>]
|
||||
|
||||
bob.readings # => [<Reading 1>]
|
||||
|
||||
Copyright (c) 2008 Michael Bleigh and Intridea, Inc. released under the MIT license
|
|
@ -0,0 +1,11 @@
|
|||
class ActsAsReadableMigrationGenerator < Rails::Generator::Base
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
|
||||
def file_name
|
||||
"acts_as_readable_migration"
|
||||
end
|
||||
end
|
14
vendor/plugins/acts-as-readable/generators/acts_as_readable_migration/templates/migration.rb
vendored
Normal file
14
vendor/plugins/acts-as-readable/generators/acts_as_readable_migration/templates/migration.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
class ActsAsReadableMigration < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :readings do |t|
|
||||
t.string :readable_type
|
||||
t.integer :readable_id
|
||||
t.integer :user_id
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :readings
|
||||
end
|
||||
end
|
6
vendor/plugins/acts-as-readable/init.rb
vendored
Normal file
6
vendor/plugins/acts-as-readable/init.rb
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'acts_as_readable')
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'reading')
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'user_with_readings')
|
||||
|
||||
ActiveRecord::Base.send :include, ActiveRecord::Acts::Readable
|
43
vendor/plugins/acts-as-readable/lib/acts_as_readable.rb
vendored
Normal file
43
vendor/plugins/acts-as-readable/lib/acts_as_readable.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
module ActiveRecord
|
||||
module Acts
|
||||
module Readable
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def acts_as_readable
|
||||
has_many :readings, :as => :readable
|
||||
has_many :users_who_read, :through => :readings, :source => :user
|
||||
|
||||
include ActiveRecord::Acts::Readable::InstanceMethods
|
||||
extend ActiveRecord::Acts::Readable::SingletonMethods
|
||||
end
|
||||
end
|
||||
|
||||
module SingletonMethods
|
||||
def find_unread_by(user)
|
||||
find(:all) - find_read_by(user)
|
||||
end
|
||||
|
||||
def find_read_by(user)
|
||||
find(:all, :conditions => ["readings.readable_id = #{table_name}.id AND readings.user_id=?", user.id], :include => :readings)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def read_by!(user)
|
||||
readings << Reading.new(:user_id => user.id)
|
||||
end
|
||||
|
||||
def unread_by!(user)
|
||||
readings.find(:first, :conditions => ["user_id = ?",user.id])
|
||||
end
|
||||
|
||||
def read_by?(user)
|
||||
!!users_who_read.find(:first, :conditions => ["user_id = ?",user.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
vendor/plugins/acts-as-readable/lib/reading.rb
vendored
Normal file
7
vendor/plugins/acts-as-readable/lib/reading.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
class Reading < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :readable, :polymorphic => true
|
||||
|
||||
validates_presence_of :user_id, :readable_id, :readable_type
|
||||
validates_uniqueness_of :user_id, :scope => [:readable_id, :readable_type]
|
||||
end
|
3
vendor/plugins/acts-as-readable/lib/user_with_readings.rb
vendored
Normal file
3
vendor/plugins/acts-as-readable/lib/user_with_readings.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
User.class_eval do
|
||||
has_many :readings
|
||||
end
|
20
vendor/plugins/acts_as_rateable/MIT-LICENSE
vendored
Normal file
20
vendor/plugins/acts_as_rateable/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2007 [name of plugin creator]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
40
vendor/plugins/acts_as_rateable/README
vendored
Normal file
40
vendor/plugins/acts_as_rateable/README
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
Acts As Rateble
|
||||
=============
|
||||
|
||||
Acts_as_rateable is a plugin released under the MIT license.
|
||||
It makes activerecord models rateable through a polymorphic association and optionally logs which user rated which model.
|
||||
In this case, one user can rate an object once. Used on cotcot.hu for article rating, sponsored quizzes, etc.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Install the plugin into your vendor/plugins directory, insert 'acts_as_rateable' into your model, then restart your application.
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
|
||||
acts_as_rateable
|
||||
|
||||
end
|
||||
|
||||
Now your model is extended by the plugin, you can rate it ( 1-# )or calculate the average rating.
|
||||
|
||||
@post.rate_it( 4, current_user.id )
|
||||
|
||||
@post.average_rating #=> 4.0
|
||||
|
||||
@post.average_rating_round #=> 4
|
||||
|
||||
@post.average_rating_percent #=> 80
|
||||
|
||||
@post.rated_by?( current_user ) #=> rating || false
|
||||
|
||||
Post.find_average_of( 4 ) #=> array of posts
|
||||
|
||||
See acts_as_rateable.rb for further details!
|
||||
|
||||
# Notes
|
||||
|
||||
Jinzhu - generator is compatible with rails 3.
|
||||
|
||||
Copyright (c) 2007-2010 Ferenc Fekete, http://feketeferenc.hu , released under the MIT license
|
22
vendor/plugins/acts_as_rateable/Rakefile
vendored
Normal file
22
vendor/plugins/acts_as_rateable/Rakefile
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the acts_as_ratable plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the acts_as_ratable plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'ActsAsRatable'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
class ActsAsRateableMigrationGenerator < Rails::Generator::Base
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
|
||||
def file_name
|
||||
"acts_as_rateable_migration"
|
||||
end
|
||||
end
|
23
vendor/plugins/acts_as_rateable/generators/acts_as_rateable_migration/templates/migration.rb
vendored
Normal file
23
vendor/plugins/acts_as_rateable/generators/acts_as_rateable_migration/templates/migration.rb
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
class ActsAsRateableMigration < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :rates do |t|
|
||||
t.column :score, :integer
|
||||
end
|
||||
|
||||
create_table :ratings do |t|
|
||||
t.column :user_id, :integer
|
||||
t.column :rate_id, :integer
|
||||
t.column :rateable_id, :integer
|
||||
t.column :rateable_type, :string, :limit => 32
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :ratings, :rate_id
|
||||
add_index :ratings, [:rateable_id, :rateable_type]
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :ratings
|
||||
drop_table :rates
|
||||
end
|
||||
end
|
2
vendor/plugins/acts_as_rateable/init.rb
vendored
Normal file
2
vendor/plugins/acts_as_rateable/init.rb
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
require File.dirname(__FILE__) + '/lib/acts_as_rateable'
|
||||
ActiveRecord::Base.send(:include, ActiveRecord::Acts::Rateable)
|
70
vendor/plugins/acts_as_rateable/lib/acts_as_rateable.rb
vendored
Normal file
70
vendor/plugins/acts_as_rateable/lib/acts_as_rateable.rb
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
module ActiveRecord
|
||||
module Acts
|
||||
module Rateable
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module AssignRateWithUserId
|
||||
def <<( rate )
|
||||
r = Rating.new
|
||||
r.rate = rate
|
||||
r.rateable = proxy_owner
|
||||
r.user_id = rate.user_id
|
||||
r.save
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def acts_as_rateable(options = {})
|
||||
has_many :ratings, :as => :rateable, :dependent => :destroy, :include => :rate
|
||||
has_many :rates, :through => :ratings, :extend => AssignRateWithUserId
|
||||
|
||||
include ActiveRecord::Acts::Rateable::InstanceMethods
|
||||
extend ActiveRecord::Acts::Rateable::SingletonMethods
|
||||
end
|
||||
end
|
||||
|
||||
module SingletonMethods
|
||||
# Find all objects rated by score.
|
||||
def find_average_of( score )
|
||||
find(:all, :include => [:rates] ).collect {|i| i if i.average_rating.to_i == score }.compact
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Rates the object by a given score. A user object can be passed to the method.
|
||||
def rate_it( score, user_id )
|
||||
return unless score
|
||||
rate = Rate.find_or_create_by_score( score.to_i )
|
||||
rate.user_id = user_id
|
||||
rates << rate
|
||||
end
|
||||
|
||||
# Calculates the average rating. Calculation based on the already given scores.
|
||||
def average_rating
|
||||
return 0 if rates.empty?
|
||||
( rates.inject(0){|total, rate| total += rate.score }.to_f / rates.size )
|
||||
end
|
||||
|
||||
# Rounds the average rating value.
|
||||
def average_rating_round
|
||||
average_rating.round
|
||||
end
|
||||
|
||||
# Returns the average rating in percent. The maximal score must be provided or the default value (5) will be used.
|
||||
# TODO make maximum_rating automatically calculated.
|
||||
def average_rating_percent( maximum_rating = 5 )
|
||||
f = 100 / maximum_rating.to_f
|
||||
average_rating * f
|
||||
end
|
||||
|
||||
# Checks wheter a user rated the object or not.
|
||||
def rated_by?( user )
|
||||
ratings.detect {|r| r.user_id == user.id }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
require 'rails/generators/migration'
|
||||
|
||||
class ActsAsRateableMigrationGenerator < Rails::Generators::Base
|
||||
include Rails::Generators::Migration
|
||||
|
||||
def self.source_root
|
||||
@_acts_as_commentable_source_root ||= File.expand_path("../templates", __FILE__)
|
||||
end
|
||||
|
||||
def self.next_migration_number(path)
|
||||
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
||||
end
|
||||
|
||||
def copy_migration_file
|
||||
migration_template 'migration.rb', 'db/migrate/acts_as_rateable_migration'
|
||||
end
|
||||
end
|
23
vendor/plugins/acts_as_rateable/lib/generators/acts_as_rateable_migration/templates/migration.rb
vendored
Normal file
23
vendor/plugins/acts_as_rateable/lib/generators/acts_as_rateable_migration/templates/migration.rb
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
class ActsAsRateableMigration < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :rates do |t|
|
||||
t.column :score, :integer
|
||||
end
|
||||
|
||||
create_table :ratings do |t|
|
||||
t.column :user_id, :integer
|
||||
t.column :rate_id, :integer
|
||||
t.column :rateable_id, :integer
|
||||
t.column :rateable_type, :string, :limit => 32
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :ratings, :rate_id
|
||||
add_index :ratings, [:rateable_id, :rateable_type]
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :ratings
|
||||
drop_table :rates
|
||||
end
|
||||
end
|
10
vendor/plugins/acts_as_rateable/lib/rate.rb
vendored
Normal file
10
vendor/plugins/acts_as_rateable/lib/rate.rb
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Rate < ActiveRecord::Base
|
||||
has_many :ratings
|
||||
|
||||
validates_presence_of :score
|
||||
validates_uniqueness_of :score
|
||||
validates_numericality_of :score, :greater_than_or_equal_to => 1, :less_than_or_equal_to => 10
|
||||
|
||||
attr_accessor :user_id
|
||||
|
||||
end
|
6
vendor/plugins/acts_as_rateable/lib/rating.rb
vendored
Normal file
6
vendor/plugins/acts_as_rateable/lib/rating.rb
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
class Rating < ActiveRecord::Base
|
||||
belongs_to :rate
|
||||
belongs_to :rateable, :polymorphic => true
|
||||
|
||||
validates_uniqueness_of :user_id, :scope => [:rateable_id, :rateable_type]
|
||||
end
|
4
vendor/plugins/acts_as_rateable/tasks/acts_as_rateable_tasks.rake
vendored
Normal file
4
vendor/plugins/acts_as_rateable/tasks/acts_as_rateable_tasks.rake
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# desc "Explaining what the task does"
|
||||
# task :acts_as_ratable do
|
||||
# # Task goes here
|
||||
# end
|
8
vendor/plugins/acts_as_rateable/test/acts_as_rateable_test.rb
vendored
Normal file
8
vendor/plugins/acts_as_rateable/test/acts_as_rateable_test.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'test/unit'
|
||||
|
||||
class ActsAsRateableTest < Test::Unit::TestCase
|
||||
# Replace this with your real tests.
|
||||
def test_this_plugin
|
||||
flunk
|
||||
end
|
||||
end
|
5
vendor/plugins/acts_as_versioned/.document
vendored
Normal file
5
vendor/plugins/acts_as_versioned/.document
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
README.rdoc
|
||||
lib/**/*.rb
|
||||
bin/*
|
||||
features/**/*.feature
|
||||
LICENSE
|
84
vendor/plugins/acts_as_versioned/CHANGELOG
vendored
Normal file
84
vendor/plugins/acts_as_versioned/CHANGELOG
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
*GIT* (version numbers are overrated)
|
||||
|
||||
* 0.6 (19 Jul 2010) Rails 3 refactoring by gvarela!
|
||||
|
||||
* (16 Jun 2008) Backwards Compatibility is overrated (big updates for rails 2.1)
|
||||
|
||||
* Use ActiveRecord 2.1's dirty attribute checking instead [Asa Calow]
|
||||
* Remove last traces of #non_versioned_fields
|
||||
* Remove AR::Base.find_version and AR::Base.find_versions, rely on AR association proxies and named_scope
|
||||
* Remove #versions_count, rely on AR association counter caching.
|
||||
* Remove #versioned_attributes, basically the same as AR::Base.versioned_columns
|
||||
|
||||
* (5 Oct 2006) Allow customization of #versions association options [Dan Peterson]
|
||||
|
||||
*0.5.1*
|
||||
|
||||
* (8 Aug 2006) Versioned models now belong to the unversioned model. @article_version.article.class => Article [Aslak Hellesoy]
|
||||
|
||||
*0.5* # do versions even matter for plugins?
|
||||
|
||||
* (21 Apr 2006) Added without_locking and without_revision methods.
|
||||
|
||||
Foo.without_revision do
|
||||
@foo.update_attributes ...
|
||||
end
|
||||
|
||||
*0.4*
|
||||
|
||||
* (28 March 2006) Rename non_versioned_fields to non_versioned_columns (old one is kept for compatibility).
|
||||
* (28 March 2006) Made explicit documentation note that string column names are required for non_versioned_columns.
|
||||
|
||||
*0.3.1*
|
||||
|
||||
* (7 Jan 2006) explicitly set :foreign_key option for the versioned model's belongs_to assocation for STI [Caged]
|
||||
* (7 Jan 2006) added tests to prove has_many :through joins work
|
||||
|
||||
*0.3*
|
||||
|
||||
* (2 Jan 2006) added ability to share a mixin with versioned class
|
||||
* (2 Jan 2006) changed the dynamic version model to MyModel::Version
|
||||
|
||||
*0.2.4*
|
||||
|
||||
* (27 Nov 2005) added note about possible destructive behavior of if_changed? [Michael Schuerig]
|
||||
|
||||
*0.2.3*
|
||||
|
||||
* (12 Nov 2005) fixed bug with old behavior of #blank? [Michael Schuerig]
|
||||
* (12 Nov 2005) updated tests to use ActiveRecord Schema
|
||||
|
||||
*0.2.2*
|
||||
|
||||
* (3 Nov 2005) added documentation note to #acts_as_versioned [Martin Jul]
|
||||
|
||||
*0.2.1*
|
||||
|
||||
* (6 Oct 2005) renamed dirty? to changed? to keep it uniform. it was aliased to keep it backwards compatible.
|
||||
|
||||
*0.2*
|
||||
|
||||
* (6 Oct 2005) added find_versions and find_version class methods.
|
||||
|
||||
* (6 Oct 2005) removed transaction from create_versioned_table().
|
||||
this way you can specify your own transaction around a group of operations.
|
||||
|
||||
* (30 Sep 2005) fixed bug where find_versions() would order by 'version' twice. (found by Joe Clark)
|
||||
|
||||
* (26 Sep 2005) added :sequence_name option to acts_as_versioned to set the sequence name on the versioned model
|
||||
|
||||
*0.1.3* (18 Sep 2005)
|
||||
|
||||
* First RubyForge release
|
||||
|
||||
*0.1.2*
|
||||
|
||||
* check if module is already included when acts_as_versioned is called
|
||||
|
||||
*0.1.1*
|
||||
|
||||
* Adding tests and rdocs
|
||||
|
||||
*0.1*
|
||||
|
||||
* Initial transfer from Rails ticket: http://dev.rubyonrails.com/ticket/1974
|
7
vendor/plugins/acts_as_versioned/Gemfile
vendored
Normal file
7
vendor/plugins/acts_as_versioned/Gemfile
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
source 'http://rubygems.org'
|
||||
|
||||
group :development do
|
||||
gem 'rails', '3.1.0'
|
||||
gem 'sqlite3-ruby', '1.3.1'
|
||||
gem 'mysql', '2.8.1'
|
||||
end
|
20
vendor/plugins/acts_as_versioned/MIT-LICENSE
vendored
Normal file
20
vendor/plugins/acts_as_versioned/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2005 Rick Olson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
vendor/plugins/acts_as_versioned/README
vendored
Normal file
24
vendor/plugins/acts_as_versioned/README
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
= acts_as_versioned
|
||||
|
||||
This library adds simple versioning to an ActiveRecord module. ActiveRecord is required.
|
||||
|
||||
== Resources
|
||||
|
||||
Install
|
||||
|
||||
* gem install acts_as_versioned
|
||||
|
||||
<3 GitHub
|
||||
|
||||
* http://github.com/technoweenie/acts_as_versioned
|
||||
|
||||
Gemcutter FTW
|
||||
|
||||
* http://gemcutter.org/gems/acts_as_versioned
|
||||
|
||||
Subversion
|
||||
|
||||
* http://svn.github.com/technoweenie/acts_as_versioned.git
|
||||
|
||||
Special thanks to Dreamer on ##rubyonrails for help in early testing. His ServerSideWiki (http://serversidewiki.com)
|
||||
was the first project to use acts_as_versioned <em>in the wild</em>.
|
41
vendor/plugins/acts_as_versioned/RUNNING_UNIT_TESTS
vendored
Normal file
41
vendor/plugins/acts_as_versioned/RUNNING_UNIT_TESTS
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
== Creating the test database
|
||||
|
||||
The default name for the test databases is "activerecord_versioned". If you
|
||||
want to use another database name then be sure to update the connection
|
||||
adapter setups you want to test with in test/connections/<your database>/connection.rb.
|
||||
When you have the database online, you can import the fixture tables with
|
||||
the test/fixtures/db_definitions/*.sql files.
|
||||
|
||||
Make sure that you create database objects with the same user that you specified in i
|
||||
connection.rb otherwise (on Postgres, at least) tests for default values will fail.
|
||||
|
||||
== Running with Rake
|
||||
|
||||
The easiest way to run the unit tests is through Rake. The default task runs
|
||||
the entire test suite for all the adapters. You can also run the suite on just
|
||||
one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
|
||||
or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
|
||||
|
||||
Rake can be found at http://rake.rubyforge.org
|
||||
|
||||
== Running by hand
|
||||
|
||||
Unit tests are located in test directory. If you only want to run a single test suite,
|
||||
or don't want to bother with Rake, you can do so with something like:
|
||||
|
||||
cd test; ruby -I "connections/native_mysql" base_test.rb
|
||||
|
||||
That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
|
||||
and test suite name as needed.
|
||||
|
||||
== Faster tests
|
||||
|
||||
If you are using a database that supports transactions, you can set the
|
||||
"AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
|
||||
This gives a very large speed boost. With rake:
|
||||
|
||||
rake AR_TX_FIXTURES=yes
|
||||
|
||||
Or, by hand:
|
||||
|
||||
AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
|
146
vendor/plugins/acts_as_versioned/Rakefile
vendored
Normal file
146
vendor/plugins/acts_as_versioned/Rakefile
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'date'
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Helper functions
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
def name
|
||||
@name ||= Dir['*.gemspec'].first.split('.').first
|
||||
end
|
||||
|
||||
def version
|
||||
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
||||
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
||||
end
|
||||
|
||||
def date
|
||||
Date.today.to_s
|
||||
end
|
||||
|
||||
def rubyforge_project
|
||||
name
|
||||
end
|
||||
|
||||
def gemspec_file
|
||||
"#{name}.gemspec"
|
||||
end
|
||||
|
||||
def gem_file
|
||||
"#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
def replace_header(head, header_name)
|
||||
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
||||
end
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Standard tasks
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
task :default => :test
|
||||
|
||||
require 'rake/testtask'
|
||||
Rake::TestTask.new(:test) do |test|
|
||||
test.libs << 'lib' << 'test'
|
||||
test.pattern = 'test/**/*_test.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
|
||||
desc "Generate RCov test coverage and open in your browser"
|
||||
task :coverage do
|
||||
require 'rcov'
|
||||
sh "rm -fr coverage"
|
||||
sh "rcov test/test_*.rb"
|
||||
sh "open coverage/index.html"
|
||||
end
|
||||
|
||||
require 'rake/rdoctask'
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = "#{name} #{version}"
|
||||
rdoc.rdoc_files.include('README*')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
||||
desc "Open an irb session preloaded with this library"
|
||||
task :console do
|
||||
sh "irb -rubygems -r ./lib/#{name}.rb"
|
||||
end
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Custom tasks (add your own tasks here)
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
|
||||
|
||||
#############################################################################
|
||||
#
|
||||
# Packaging tasks
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
task :release => :build do
|
||||
unless `git branch` =~ /^\* master$/
|
||||
puts "You must be on the master branch to release!"
|
||||
exit!
|
||||
end
|
||||
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
||||
sh "git tag v#{version}"
|
||||
sh "git push origin master"
|
||||
sh "git push v#{version}"
|
||||
sh "gem push pkg/#{name}-#{version}.gem"
|
||||
end
|
||||
|
||||
task :build => :gemspec do
|
||||
sh "mkdir -p pkg"
|
||||
sh "gem build #{gemspec_file}"
|
||||
sh "mv #{gem_file} pkg"
|
||||
end
|
||||
|
||||
task :gemspec => :validate do
|
||||
# read spec file and split out manifest section
|
||||
spec = File.read(gemspec_file)
|
||||
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
||||
|
||||
# replace name version and date
|
||||
replace_header(head, :name)
|
||||
replace_header(head, :version)
|
||||
replace_header(head, :date)
|
||||
#comment this out if your rubyforge_project has a different name
|
||||
replace_header(head, :rubyforge_project)
|
||||
|
||||
# determine file list from git ls-files
|
||||
files = `git ls-files`.
|
||||
split("\n").
|
||||
sort.
|
||||
reject { |file| file =~ /^\./ }.
|
||||
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
||||
map { |file| " #{file}" }.
|
||||
join("\n")
|
||||
|
||||
# piece file back together and write
|
||||
manifest = " s.files = %w[\n#{files}\n ]\n"
|
||||
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
||||
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
||||
puts "Updated #{gemspec_file}"
|
||||
end
|
||||
|
||||
task :validate do
|
||||
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
||||
unless libfiles.empty?
|
||||
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
||||
exit!
|
||||
end
|
||||
unless Dir['VERSION*'].empty?
|
||||
puts "A `VERSION` file at root level violates Gem best practices."
|
||||
exit!
|
||||
end
|
||||
end
|
85
vendor/plugins/acts_as_versioned/acts_as_versioned.gemspec
vendored
Normal file
85
vendor/plugins/acts_as_versioned/acts_as_versioned.gemspec
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
## This is the rakegem gemspec template. Make sure you read and understand
|
||||
## all of the comments. Some sections require modification, and others can
|
||||
## be deleted if you don't need them. Once you understand the contents of
|
||||
## this file, feel free to delete any comments that begin with two hash marks.
|
||||
## You can find comprehensive Gem::Specification documentation, at
|
||||
## http://docs.rubygems.org/read/chapter/20
|
||||
Gem::Specification.new do |s|
|
||||
s.specification_version = 2 if s.respond_to? :specification_version=
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.rubygems_version = '1.3.5'
|
||||
|
||||
## Leave these as is they will be modified for you by the rake gemspec task.
|
||||
## If your rubyforge_project name is different, then edit it and comment out
|
||||
## the sub! line in the Rakefile
|
||||
s.name = 'acts_as_versioned'
|
||||
s.version = '0.6.0'
|
||||
s.date = '2010-07-19'
|
||||
s.rubyforge_project = 'acts_as_versioned'
|
||||
|
||||
## Make sure your summary is short. The description may be as long
|
||||
## as you like.
|
||||
s.summary = "Add simple versioning to ActiveRecord models."
|
||||
s.description = "Add simple versioning to ActiveRecord models."
|
||||
|
||||
## List the primary authors. If there are a bunch of authors, it's probably
|
||||
## better to set the email to an email list or something. If you don't have
|
||||
## a custom homepage, consider using your GitHub URL or the like.
|
||||
s.authors = ["Rick Olson"]
|
||||
s.email = 'technoweenie@gmail.com'
|
||||
s.homepage = 'http://github.com/technoweenie/acts_as_versioned'
|
||||
|
||||
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
||||
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
||||
s.require_paths = %w[lib]
|
||||
|
||||
## Specify any RDoc options here. You'll want to add your README and
|
||||
## LICENSE files to the extra_rdoc_files list.
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.extra_rdoc_files = %w[README MIT-LICENSE CHANGELOG]
|
||||
|
||||
## List your runtime dependencies here. Runtime dependencies are those
|
||||
## that are needed for an end user to actually USE your code.
|
||||
s.add_dependency('activerecord', [">= 3.1.0"])
|
||||
|
||||
## List your development dependencies here. Development dependencies are
|
||||
## those that are only needed during development
|
||||
s.add_development_dependency('sqlite3-ruby', ["~> 1.3.1"])
|
||||
|
||||
## Leave this section as-is. It will be automatically generated from the
|
||||
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
||||
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
||||
# = MANIFEST =
|
||||
s.files = %w[
|
||||
CHANGELOG
|
||||
Gemfile
|
||||
MIT-LICENSE
|
||||
README
|
||||
RUNNING_UNIT_TESTS
|
||||
Rakefile
|
||||
acts_as_versioned.gemspec
|
||||
init.rb
|
||||
lib/acts_as_versioned.rb
|
||||
test/abstract_unit.rb
|
||||
test/database.yml
|
||||
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/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
|
||||
]
|
||||
# = MANIFEST =
|
||||
|
||||
## Test files will be grabbed from the file list. Make sure the path glob
|
||||
## matches what you actually use.
|
||||
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
||||
end
|
1
vendor/plugins/acts_as_versioned/init.rb
vendored
Normal file
1
vendor/plugins/acts_as_versioned/init.rb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
require File.join(File.dirname(__FILE__), 'lib', 'acts_as_versioned')
|
494
vendor/plugins/acts_as_versioned/lib/acts_as_versioned.rb
vendored
Normal file
494
vendor/plugins/acts_as_versioned/lib/acts_as_versioned.rb
vendored
Normal file
|
@ -0,0 +1,494 @@
|
|||
# Copyright (c) 2005 Rick Olson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
require 'active_support/concern'
|
||||
|
||||
module ActiveRecord #:nodoc:
|
||||
module Acts #:nodoc:
|
||||
# Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
|
||||
# versioned table ready and that your model has a version field. This works with optimistic locking if the lock_version
|
||||
# column is present as well.
|
||||
#
|
||||
# The class for the versioned model is derived the first time it is seen. Therefore, if you change your database schema you have to restart
|
||||
# your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
|
||||
#
|
||||
# class Page < ActiveRecord::Base
|
||||
# # assumes pages_versions table
|
||||
# acts_as_versioned
|
||||
# end
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# page = Page.create(:title => 'hello world!')
|
||||
# page.version # => 1
|
||||
#
|
||||
# page.title = 'hello world'
|
||||
# page.save
|
||||
# page.version # => 2
|
||||
# page.versions.size # => 2
|
||||
#
|
||||
# page.revert_to(1) # using version number
|
||||
# page.title # => 'hello world!'
|
||||
#
|
||||
# page.revert_to(page.versions.last) # using versioned instance
|
||||
# page.title # => 'hello world'
|
||||
#
|
||||
# page.versions.earliest # efficient query to find the first version
|
||||
# page.versions.latest # efficient query to find the most recently created version
|
||||
#
|
||||
#
|
||||
# Simple Queries to page between versions
|
||||
#
|
||||
# page.versions.before(version)
|
||||
# page.versions.after(version)
|
||||
#
|
||||
# Access the previous/next versions from the versioned model itself
|
||||
#
|
||||
# version = page.versions.latest
|
||||
# version.previous # go back one version
|
||||
# version.next # go forward one version
|
||||
#
|
||||
# See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
|
||||
module Versioned
|
||||
VERSION = "0.6.0"
|
||||
CALLBACKS = [:set_new_version, :save_version, :save_version?]
|
||||
|
||||
# == Configuration options
|
||||
#
|
||||
# * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
|
||||
# * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
|
||||
# * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
|
||||
# * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
|
||||
# * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
|
||||
# * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
|
||||
# * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
|
||||
# * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
|
||||
# For finer control, pass either a Proc or modify Model#version_condition_met?
|
||||
#
|
||||
# acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
|
||||
#
|
||||
# or...
|
||||
#
|
||||
# class Auction
|
||||
# def version_condition_met? # totally bypasses the <tt>:if</tt> option
|
||||
# !expired?
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
|
||||
# either a symbol or array of symbols.
|
||||
#
|
||||
# * <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
|
||||
# to create an anonymous mixin:
|
||||
#
|
||||
# class Auction
|
||||
# acts_as_versioned do
|
||||
# def started?
|
||||
# !started_at.nil?
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# or...
|
||||
#
|
||||
# module AuctionExtension
|
||||
# def started?
|
||||
# !started_at.nil?
|
||||
# end
|
||||
# end
|
||||
# class Auction
|
||||
# acts_as_versioned :extend => AuctionExtension
|
||||
# end
|
||||
#
|
||||
# Example code:
|
||||
#
|
||||
# @auction = Auction.find(1)
|
||||
# @auction.started?
|
||||
# @auction.versions.first.started?
|
||||
#
|
||||
# == Database Schema
|
||||
#
|
||||
# The model that you're versioning needs to have a 'version' attribute. The model is versioned
|
||||
# into a table called #{model}_versions where the model name is singlular. The _versions table should
|
||||
# contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
|
||||
#
|
||||
# A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
|
||||
# then that field is reflected in the versioned model as 'versioned_type' by default.
|
||||
#
|
||||
# Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
|
||||
# method, perfect for a migration. It will also create the version column if the main model does not already have it.
|
||||
#
|
||||
# class AddVersions < ActiveRecord::Migration
|
||||
# def self.up
|
||||
# # create_versioned_table takes the same options hash
|
||||
# # that create_table does
|
||||
# Post.create_versioned_table
|
||||
# end
|
||||
#
|
||||
# def self.down
|
||||
# Post.drop_versioned_table
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Changing What Fields Are Versioned
|
||||
#
|
||||
# By default, acts_as_versioned will version all but these fields:
|
||||
#
|
||||
# [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
|
||||
#
|
||||
# You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
|
||||
#
|
||||
# class Post < ActiveRecord::Base
|
||||
# acts_as_versioned
|
||||
# self.non_versioned_columns << 'comments_count'
|
||||
# end
|
||||
#
|
||||
def acts_as_versioned(options = {}, &extension)
|
||||
# don't allow multiple calls
|
||||
return if self.included_modules.include?(ActiveRecord::Acts::Versioned::Behaviors)
|
||||
|
||||
cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
|
||||
:version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
|
||||
:version_association_options, :version_if_changed
|
||||
|
||||
self.versioned_class_name = options[:class_name] || "Version"
|
||||
self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
|
||||
self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
|
||||
self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
|
||||
self.version_column = options[:version_column] || 'version'
|
||||
self.version_sequence_name = options[:sequence_name]
|
||||
self.max_version_limit = options[:limit].to_i
|
||||
self.version_condition = options[:if] || true
|
||||
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)
|
||||
self.version_association_options = {
|
||||
:class_name => "#{self.to_s}::#{versioned_class_name}",
|
||||
:foreign_key => versioned_foreign_key,
|
||||
:dependent => :delete_all
|
||||
}.merge(options[:association_options] || {})
|
||||
|
||||
if block_given?
|
||||
extension_module_name = "#{versioned_class_name}Extension"
|
||||
silence_warnings do
|
||||
self.const_set(extension_module_name, Module.new(&extension))
|
||||
end
|
||||
|
||||
options[:extend] = self.const_get(extension_module_name)
|
||||
end
|
||||
|
||||
unless options[:if_changed].nil?
|
||||
self.track_altered_attributes = true
|
||||
options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
|
||||
self.version_if_changed = options[:if_changed].map(&:to_s)
|
||||
end
|
||||
|
||||
include options[:extend] if options[:extend].is_a?(Module)
|
||||
|
||||
include ActiveRecord::Acts::Versioned::Behaviors
|
||||
|
||||
#
|
||||
# Create the dynamic versioned model
|
||||
#
|
||||
const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
|
||||
def self.reloadable?;
|
||||
false;
|
||||
end
|
||||
|
||||
# find first version before the given version
|
||||
def self.before(version)
|
||||
where(["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]).
|
||||
order('version DESC').
|
||||
first
|
||||
end
|
||||
|
||||
# find first version after the given version.
|
||||
def self.after(version)
|
||||
where(["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]).
|
||||
order('version ASC').
|
||||
first
|
||||
end
|
||||
|
||||
# finds earliest version of this record
|
||||
def self.earliest
|
||||
order("#{original_class.version_column}").first
|
||||
end
|
||||
|
||||
# find latest version of this record
|
||||
def self.latest
|
||||
order("#{original_class.version_column} desc").first
|
||||
end
|
||||
|
||||
def previous
|
||||
self.class.before(self)
|
||||
end
|
||||
|
||||
def next
|
||||
self.class.after(self)
|
||||
end
|
||||
|
||||
def versions_count
|
||||
page.version
|
||||
end
|
||||
end
|
||||
|
||||
versioned_class.cattr_accessor :original_class
|
||||
versioned_class.original_class = self
|
||||
versioned_class.set_table_name versioned_table_name
|
||||
versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
|
||||
:class_name => "::#{self.to_s}",
|
||||
:foreign_key => versioned_foreign_key
|
||||
versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
|
||||
versioned_class.set_sequence_name version_sequence_name if version_sequence_name
|
||||
end
|
||||
|
||||
module Behaviors
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :versions, self.version_association_options
|
||||
|
||||
before_save :set_new_version
|
||||
after_save :save_version
|
||||
after_save :clear_old_versions
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Saves a version of the model in the versioned table. This is called in the after_save callback by default
|
||||
def save_version
|
||||
if @saving_version
|
||||
@saving_version = nil
|
||||
rev = self.class.versioned_class.new
|
||||
clone_versioned_model(self, rev)
|
||||
rev.send("#{self.class.version_column}=", send(self.class.version_column))
|
||||
rev.send("#{self.class.versioned_foreign_key}=", id)
|
||||
rev.save
|
||||
end
|
||||
end
|
||||
|
||||
# Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
|
||||
# Override this method to set your own criteria for clearing old versions.
|
||||
def clear_old_versions
|
||||
return if self.class.max_version_limit == 0
|
||||
excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
|
||||
if excess_baggage > 0
|
||||
self.class.versioned_class.delete_all ["#{self.class.version_column} <= ? and #{self.class.versioned_foreign_key} = ?", excess_baggage, id]
|
||||
end
|
||||
end
|
||||
|
||||
# Reverts a model to a given version. Takes either a version number or an instance of the versioned model
|
||||
def revert_to(version)
|
||||
if version.is_a?(self.class.versioned_class)
|
||||
return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record?
|
||||
else
|
||||
return false unless version = versions.where(self.class.version_column => version).first
|
||||
end
|
||||
self.clone_versioned_model(version, self)
|
||||
send("#{self.class.version_column}=", version.send(self.class.version_column))
|
||||
true
|
||||
end
|
||||
|
||||
# Reverts a model to a given version and saves the model.
|
||||
# Takes either a version number or an instance of the versioned model
|
||||
def revert_to!(version)
|
||||
revert_to(version) ? save_without_revision : false
|
||||
end
|
||||
|
||||
# Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
|
||||
def save_without_revision
|
||||
save_without_revision!
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def save_without_revision!
|
||||
without_locking do
|
||||
without_revision do
|
||||
save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def altered?
|
||||
track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed?
|
||||
end
|
||||
|
||||
# Clones a model. Used when saving a new version or reverting a model's version.
|
||||
def clone_versioned_model(orig_model, new_model)
|
||||
self.class.versioned_columns.each do |col|
|
||||
new_model[col.name] = orig_model.send(col.name) if orig_model.has_attribute?(col.name)
|
||||
end
|
||||
|
||||
if orig_model.is_a?(self.class.versioned_class)
|
||||
new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
|
||||
elsif new_model.is_a?(self.class.versioned_class)
|
||||
new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
|
||||
def save_version?
|
||||
version_condition_met? && altered?
|
||||
end
|
||||
|
||||
# Checks condition set in the :if option to check whether a revision should be created or not. Override this for
|
||||
# custom version condition checking.
|
||||
def version_condition_met?
|
||||
case
|
||||
when version_condition.is_a?(Symbol)
|
||||
send(version_condition)
|
||||
when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
|
||||
version_condition.call(self)
|
||||
else
|
||||
version_condition
|
||||
end
|
||||
end
|
||||
|
||||
# Executes the block with the versioning callbacks disabled.
|
||||
#
|
||||
# @foo.without_revision do
|
||||
# @foo.save
|
||||
# end
|
||||
#
|
||||
def without_revision(&block)
|
||||
self.class.without_revision(&block)
|
||||
end
|
||||
|
||||
# Turns off optimistic locking for the duration of the block
|
||||
#
|
||||
# @foo.without_locking do
|
||||
# @foo.save
|
||||
# end
|
||||
#
|
||||
def without_locking(&block)
|
||||
self.class.without_locking(&block)
|
||||
end
|
||||
|
||||
def empty_callback()
|
||||
end
|
||||
|
||||
#:nodoc:
|
||||
|
||||
protected
|
||||
# sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
|
||||
def set_new_version
|
||||
@saving_version = new_record? || save_version?
|
||||
self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?)
|
||||
end
|
||||
|
||||
# Gets the next available version for the current record, or 1 for a new record
|
||||
def next_version
|
||||
(new_record? ? 0 : versions.calculate(:maximum, version_column).to_i) + 1
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Returns an array of columns that are versioned. See non_versioned_columns
|
||||
def versioned_columns
|
||||
@versioned_columns ||= columns.select { |c| !non_versioned_columns.include?(c.name) }
|
||||
end
|
||||
|
||||
# Returns an instance of the dynamic versioned model
|
||||
def versioned_class
|
||||
const_get versioned_class_name
|
||||
end
|
||||
|
||||
# Rake migration task to create the versioned table using options passed to acts_as_versioned
|
||||
def create_versioned_table(create_table_options = {})
|
||||
# create version column in main table if it does not exist
|
||||
if !self.content_columns.find { |c| [version_column.to_s, 'lock_version'].include? c.name }
|
||||
self.connection.add_column table_name, version_column, :integer
|
||||
self.reset_column_information
|
||||
end
|
||||
|
||||
return if connection.table_exists?(versioned_table_name)
|
||||
|
||||
self.connection.create_table(versioned_table_name, create_table_options) do |t|
|
||||
t.column versioned_foreign_key, :integer
|
||||
t.column version_column, :integer
|
||||
end
|
||||
|
||||
self.versioned_columns.each do |col|
|
||||
self.connection.add_column versioned_table_name, col.name, col.type,
|
||||
:limit => col.limit,
|
||||
:default => col.default,
|
||||
:scale => col.scale,
|
||||
:precision => col.precision
|
||||
end
|
||||
|
||||
if type_col = self.columns_hash[inheritance_column]
|
||||
self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
|
||||
:limit => type_col.limit,
|
||||
:default => type_col.default,
|
||||
:scale => type_col.scale,
|
||||
:precision => type_col.precision
|
||||
end
|
||||
|
||||
self.connection.add_index versioned_table_name, versioned_foreign_key
|
||||
end
|
||||
|
||||
# Rake migration task to drop the versioned table
|
||||
def drop_versioned_table
|
||||
self.connection.drop_table versioned_table_name
|
||||
end
|
||||
|
||||
# Executes the block with the versioning callbacks disabled.
|
||||
#
|
||||
# Foo.without_revision do
|
||||
# @foo.save
|
||||
# end
|
||||
#
|
||||
def without_revision(&block)
|
||||
class_eval do
|
||||
CALLBACKS.each do |attr_name|
|
||||
alias_method "orig_#{attr_name}".to_sym, attr_name
|
||||
alias_method attr_name, :empty_callback
|
||||
end
|
||||
end
|
||||
block.call
|
||||
ensure
|
||||
class_eval do
|
||||
CALLBACKS.each do |attr_name|
|
||||
alias_method attr_name, "orig_#{attr_name}".to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Turns off optimistic locking for the duration of the block
|
||||
#
|
||||
# Foo.without_locking do
|
||||
# @foo.save
|
||||
# end
|
||||
#
|
||||
def without_locking(&block)
|
||||
current = ActiveRecord::Base.lock_optimistically
|
||||
ActiveRecord::Base.lock_optimistically = false if current
|
||||
begin
|
||||
block.call
|
||||
ensure
|
||||
ActiveRecord::Base.lock_optimistically = true if current
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.extend ActiveRecord::Acts::Versioned
|
49
vendor/plugins/acts_as_versioned/test/abstract_unit.rb
vendored
Normal file
49
vendor/plugins/acts_as_versioned/test/abstract_unit.rb
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
require "rubygems"
|
||||
require "bundler"
|
||||
Bundler.setup(:default, :development)
|
||||
|
||||
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
require 'test/unit'
|
||||
require 'active_support'
|
||||
require 'active_record'
|
||||
require 'active_record/fixtures'
|
||||
require 'active_record/test_case'
|
||||
|
||||
begin
|
||||
require 'ruby-debug'
|
||||
Debugger.start
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
require 'acts_as_versioned'
|
||||
|
||||
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
||||
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
||||
ActiveRecord::Base.configurations = {'test' => config[ENV['DB'] || 'sqlite3']}
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
||||
|
||||
load(File.dirname(__FILE__) + "/schema.rb")
|
||||
|
||||
# set up custom sequence on widget_versions for DBs that support sequences
|
||||
if ENV['DB'] == 'postgresql'
|
||||
ActiveRecord::Base.connection.execute "DROP SEQUENCE widgets_seq;" rescue nil
|
||||
ActiveRecord::Base.connection.remove_column :widget_versions, :id
|
||||
ActiveRecord::Base.connection.execute "CREATE SEQUENCE widgets_seq START 101;"
|
||||
ActiveRecord::Base.connection.execute "ALTER TABLE widget_versions ADD COLUMN id INTEGER PRIMARY KEY DEFAULT nextval('widgets_seq');"
|
||||
end
|
||||
|
||||
class ActiveSupport::TestCase #:nodoc:
|
||||
include ActiveRecord::TestFixtures
|
||||
|
||||
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
||||
|
||||
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
|
||||
self.use_instantiated_fixtures = false
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
end
|
||||
|
||||
$:.unshift(ActiveSupport::TestCase.fixture_path)
|
18
vendor/plugins/acts_as_versioned/test/database.yml
vendored
Normal file
18
vendor/plugins/acts_as_versioned/test/database.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
sqlite:
|
||||
adapter: sqlite
|
||||
dbfile: acts_as_versioned_plugin.sqlite.db
|
||||
sqlite3:
|
||||
adapter: sqlite3
|
||||
database: acts_as_versioned_plugin.sqlite3.db
|
||||
postgresql:
|
||||
adapter: postgresql
|
||||
username: postgres
|
||||
password: postgres
|
||||
database: acts_as_versioned_plugin_test
|
||||
min_messages: ERROR
|
||||
mysql:
|
||||
adapter: mysql
|
||||
host: localhost
|
||||
username: rails
|
||||
password:
|
||||
database: acts_as_versioned_plugin_test
|
6
vendor/plugins/acts_as_versioned/test/fixtures/authors.yml
vendored
Normal file
6
vendor/plugins/acts_as_versioned/test/fixtures/authors.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
caged:
|
||||
id: 1
|
||||
name: caged
|
||||
mly:
|
||||
id: 2
|
||||
name: mly
|
3
vendor/plugins/acts_as_versioned/test/fixtures/landmark.rb
vendored
Normal file
3
vendor/plugins/acts_as_versioned/test/fixtures/landmark.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
class Landmark < ActiveRecord::Base
|
||||
acts_as_versioned :if_changed => [ :name, :longitude, :latitude ]
|
||||
end
|
7
vendor/plugins/acts_as_versioned/test/fixtures/landmark_versions.yml
vendored
Normal file
7
vendor/plugins/acts_as_versioned/test/fixtures/landmark_versions.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
washington:
|
||||
id: 1
|
||||
landmark_id: 1
|
||||
version: 1
|
||||
name: Washington, D.C.
|
||||
latitude: 38.895
|
||||
longitude: -77.036667
|
7
vendor/plugins/acts_as_versioned/test/fixtures/landmarks.yml
vendored
Normal file
7
vendor/plugins/acts_as_versioned/test/fixtures/landmarks.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
washington:
|
||||
id: 1
|
||||
name: Washington, D.C.
|
||||
latitude: 38.895
|
||||
longitude: -77.036667
|
||||
doesnt_trigger_version: This is not important
|
||||
version: 1
|
10
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages.yml
vendored
Normal file
10
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
welcome:
|
||||
id: 1
|
||||
title: Welcome to the weblog
|
||||
lock_version: 24
|
||||
type: LockedPage
|
||||
thinking:
|
||||
id: 2
|
||||
title: So I was thinking
|
||||
lock_version: 24
|
||||
type: SpecialLockedPage
|
27
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages_revisions.yml
vendored
Normal file
27
vendor/plugins/acts_as_versioned/test/fixtures/locked_pages_revisions.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
welcome_1:
|
||||
id: 1
|
||||
page_id: 1
|
||||
title: Welcome to the weblg
|
||||
lock_version: 23
|
||||
version_type: LockedPage
|
||||
|
||||
welcome_2:
|
||||
id: 2
|
||||
page_id: 1
|
||||
title: Welcome to the weblog
|
||||
lock_version: 24
|
||||
version_type: LockedPage
|
||||
|
||||
thinking_1:
|
||||
id: 3
|
||||
page_id: 2
|
||||
title: So I was thinking!!!
|
||||
lock_version: 23
|
||||
version_type: SpecialLockedPage
|
||||
|
||||
thinking_2:
|
||||
id: 4
|
||||
page_id: 2
|
||||
title: So I was thinking
|
||||
lock_version: 24
|
||||
version_type: SpecialLockedPage
|
15
vendor/plugins/acts_as_versioned/test/fixtures/migrations/1_add_versioned_tables.rb
vendored
Normal file
15
vendor/plugins/acts_as_versioned/test/fixtures/migrations/1_add_versioned_tables.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
class AddVersionedTables < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table("things") do |t|
|
||||
t.column :title, :text
|
||||
t.column :price, :decimal, :precision => 7, :scale => 2
|
||||
t.column :type, :string
|
||||
end
|
||||
Thing.create_versioned_table
|
||||
end
|
||||
|
||||
def self.down
|
||||
Thing.drop_versioned_table
|
||||
drop_table "things" rescue nil
|
||||
end
|
||||
end
|
43
vendor/plugins/acts_as_versioned/test/fixtures/page.rb
vendored
Normal file
43
vendor/plugins/acts_as_versioned/test/fixtures/page.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
class Page < ActiveRecord::Base
|
||||
belongs_to :author
|
||||
has_many :authors, :through => :versions, :order => 'name'
|
||||
belongs_to :revisor, :class_name => 'Author'
|
||||
has_many :revisors, :class_name => 'Author', :through => :versions, :order => 'name'
|
||||
acts_as_versioned :if => :feeling_good? do
|
||||
def self.included(base)
|
||||
base.cattr_accessor :feeling_good
|
||||
base.feeling_good = true
|
||||
base.belongs_to :author
|
||||
base.belongs_to :revisor, :class_name => 'Author'
|
||||
end
|
||||
|
||||
def feeling_good?
|
||||
@@feeling_good == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module LockedPageExtension
|
||||
def hello_world
|
||||
'hello_world'
|
||||
end
|
||||
end
|
||||
|
||||
class LockedPage < ActiveRecord::Base
|
||||
acts_as_versioned \
|
||||
:inheritance_column => :version_type,
|
||||
:foreign_key => :page_id,
|
||||
:table_name => :locked_pages_revisions,
|
||||
:class_name => 'LockedPageRevision',
|
||||
:version_column => :lock_version,
|
||||
:limit => 2,
|
||||
:if_changed => :title,
|
||||
:extend => LockedPageExtension
|
||||
end
|
||||
|
||||
class SpecialLockedPage < LockedPage
|
||||
end
|
||||
|
||||
class Author < ActiveRecord::Base
|
||||
has_many :pages
|
||||
end
|
16
vendor/plugins/acts_as_versioned/test/fixtures/page_versions.yml
vendored
Normal file
16
vendor/plugins/acts_as_versioned/test/fixtures/page_versions.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
welcome_2:
|
||||
id: 1
|
||||
page_id: 1
|
||||
title: Welcome to the weblog
|
||||
body: Such a lovely day
|
||||
version: 24
|
||||
author_id: 1
|
||||
revisor_id: 1
|
||||
welcome_1:
|
||||
id: 2
|
||||
page_id: 1
|
||||
title: Welcome to the weblg
|
||||
body: Such a lovely day
|
||||
version: 23
|
||||
author_id: 2
|
||||
revisor_id: 2
|
8
vendor/plugins/acts_as_versioned/test/fixtures/pages.yml
vendored
Normal file
8
vendor/plugins/acts_as_versioned/test/fixtures/pages.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
welcome:
|
||||
id: 1
|
||||
title: Welcome to the weblog
|
||||
body: Such a lovely day
|
||||
version: 24
|
||||
author_id: 1
|
||||
revisor_id: 1
|
||||
created_on: "2008-01-01 00:00:00"
|
6
vendor/plugins/acts_as_versioned/test/fixtures/widget.rb
vendored
Normal file
6
vendor/plugins/acts_as_versioned/test/fixtures/widget.rb
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
class Widget < ActiveRecord::Base
|
||||
acts_as_versioned :sequence_name => 'widgets_seq', :association_options => {
|
||||
:dependent => :nullify, :order => 'version desc'
|
||||
}
|
||||
non_versioned_columns << 'foo'
|
||||
end
|
46
vendor/plugins/acts_as_versioned/test/migration_test.rb
vendored
Normal file
46
vendor/plugins/acts_as_versioned/test/migration_test.rb
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
require File.join(File.dirname(__FILE__), 'abstract_unit')
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
class Thing < ActiveRecord::Base
|
||||
attr_accessor :version
|
||||
acts_as_versioned
|
||||
end
|
||||
|
||||
class MigrationTest < ActiveSupport::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
def teardown
|
||||
if ActiveRecord::Base.connection.respond_to?(:initialize_schema_information)
|
||||
ActiveRecord::Base.connection.initialize_schema_information
|
||||
ActiveRecord::Base.connection.update "UPDATE schema_info SET version = 0"
|
||||
else
|
||||
ActiveRecord::Base.connection.initialize_schema_migrations_table
|
||||
ActiveRecord::Base.connection.assume_migrated_upto_version(0)
|
||||
end
|
||||
|
||||
Thing.connection.drop_table "things" rescue nil
|
||||
Thing.connection.drop_table "thing_versions" rescue nil
|
||||
Thing.reset_column_information
|
||||
end
|
||||
|
||||
def test_versioned_migration
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' }
|
||||
# take 'er up
|
||||
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
|
||||
t = Thing.create :title => 'blah blah', :price => 123.45, :type => 'Thing'
|
||||
assert_equal 1, t.versions.size
|
||||
|
||||
# check that the price column has remembered its value correctly
|
||||
assert_equal t.price, t.versions.first.price
|
||||
assert_equal t.title, t.versions.first.title
|
||||
assert_equal t[:type], t.versions.first[:type]
|
||||
|
||||
# make sure that the precision of the price column has been preserved
|
||||
assert_equal 7, Thing::Version.columns.find{|c| c.name == "price"}.precision
|
||||
assert_equal 2, Thing::Version.columns.find{|c| c.name == "price"}.scale
|
||||
|
||||
# now lets take 'er back down
|
||||
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/')
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Thing.create :title => 'blah blah' }
|
||||
end
|
||||
end
|
||||
end
|
82
vendor/plugins/acts_as_versioned/test/schema.rb
vendored
Normal file
82
vendor/plugins/acts_as_versioned/test/schema.rb
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
ActiveRecord::Schema.define(:version => 0) do
|
||||
create_table :pages, :force => true do |t|
|
||||
t.column :version, :integer
|
||||
t.column :title, :string, :limit => 255
|
||||
t.column :body, :text
|
||||
t.column :created_on, :datetime
|
||||
t.column :updated_on, :datetime
|
||||
t.column :author_id, :integer
|
||||
t.column :revisor_id, :integer
|
||||
end
|
||||
|
||||
create_table :page_versions, :force => true do |t|
|
||||
t.column :page_id, :integer
|
||||
t.column :version, :integer
|
||||
t.column :title, :string, :limit => 255
|
||||
t.column :body, :text
|
||||
t.column :created_on, :datetime
|
||||
t.column :updated_on, :datetime
|
||||
t.column :author_id, :integer
|
||||
t.column :revisor_id, :integer
|
||||
end
|
||||
|
||||
add_index :page_versions, [:page_id, :version], :unique => true
|
||||
|
||||
create_table :authors, :force => true do |t|
|
||||
t.column :page_id, :integer
|
||||
t.column :name, :string
|
||||
end
|
||||
|
||||
create_table :locked_pages, :force => true do |t|
|
||||
t.column :lock_version, :integer
|
||||
t.column :title, :string, :limit => 255
|
||||
t.column :body, :text
|
||||
t.column :type, :string, :limit => 255
|
||||
end
|
||||
|
||||
create_table :locked_pages_revisions, :force => true do |t|
|
||||
t.column :page_id, :integer
|
||||
t.column :lock_version, :integer
|
||||
t.column :title, :string, :limit => 255
|
||||
t.column :body, :text
|
||||
t.column :version_type, :string, :limit => 255
|
||||
t.column :updated_at, :datetime
|
||||
end
|
||||
|
||||
add_index :locked_pages_revisions, [:page_id, :lock_version], :unique => true
|
||||
|
||||
create_table :widgets, :force => true do |t|
|
||||
t.column :name, :string, :limit => 50
|
||||
t.column :foo, :string
|
||||
t.column :version, :integer
|
||||
t.column :updated_at, :datetime
|
||||
end
|
||||
|
||||
create_table :widget_versions, :force => true do |t|
|
||||
t.column :widget_id, :integer
|
||||
t.column :name, :string, :limit => 50
|
||||
t.column :version, :integer
|
||||
t.column :updated_at, :datetime
|
||||
end
|
||||
|
||||
add_index :widget_versions, [:widget_id, :version], :unique => true
|
||||
|
||||
create_table :landmarks, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :latitude, :float
|
||||
t.column :longitude, :float
|
||||
t.column :doesnt_trigger_version,:string
|
||||
t.column :version, :integer
|
||||
end
|
||||
|
||||
create_table :landmark_versions, :force => true do |t|
|
||||
t.column :landmark_id, :integer
|
||||
t.column :name, :string
|
||||
t.column :latitude, :float
|
||||
t.column :longitude, :float
|
||||
t.column :doesnt_trigger_version,:string
|
||||
t.column :version, :integer
|
||||
end
|
||||
|
||||
add_index :landmark_versions, [:landmark_id, :version], :unique => true
|
||||
end
|
370
vendor/plugins/acts_as_versioned/test/versioned_test.rb
vendored
Normal file
370
vendor/plugins/acts_as_versioned/test/versioned_test.rb
vendored
Normal file
|
@ -0,0 +1,370 @@
|
|||
require File.join(File.dirname(__FILE__), 'abstract_unit')
|
||||
require File.join(File.dirname(__FILE__), 'fixtures/page')
|
||||
require File.join(File.dirname(__FILE__), 'fixtures/widget')
|
||||
|
||||
class VersionedTest < ActiveSupport::TestCase
|
||||
fixtures :pages, :page_versions, :locked_pages, :locked_pages_revisions, :authors, :landmarks, :landmark_versions
|
||||
set_fixture_class :page_versions => Page::Version
|
||||
|
||||
def test_saves_versioned_copy
|
||||
p = Page.create! :title => 'first title', :body => 'first body'
|
||||
assert !p.new_record?
|
||||
assert_equal 1, p.versions.size
|
||||
assert_equal 1, p.version
|
||||
assert_instance_of Page.versioned_class, p.versions.first
|
||||
end
|
||||
|
||||
def test_saves_without_revision
|
||||
p = pages(:welcome)
|
||||
old_versions = p.versions.count
|
||||
|
||||
p.save_without_revision
|
||||
|
||||
p.without_revision do
|
||||
p.update_attributes :title => 'changed'
|
||||
end
|
||||
|
||||
assert_equal old_versions, p.versions.count
|
||||
end
|
||||
|
||||
def test_rollback_with_version_number
|
||||
p = pages(:welcome)
|
||||
assert_equal 24, p.version
|
||||
assert_equal 'Welcome to the weblog', p.title
|
||||
|
||||
assert p.revert_to!(23), "Couldn't revert to 23"
|
||||
assert_equal 23, p.version
|
||||
assert_equal 'Welcome to the weblg', p.title
|
||||
end
|
||||
|
||||
def test_versioned_class_name
|
||||
assert_equal 'Version', Page.versioned_class_name
|
||||
assert_equal 'LockedPageRevision', LockedPage.versioned_class_name
|
||||
end
|
||||
|
||||
def test_versioned_class
|
||||
assert_equal Page::Version, Page.versioned_class
|
||||
assert_equal LockedPage::LockedPageRevision, LockedPage.versioned_class
|
||||
end
|
||||
|
||||
def test_special_methods
|
||||
assert_nothing_raised { pages(:welcome).feeling_good? }
|
||||
assert_nothing_raised { pages(:welcome).versions.first.feeling_good? }
|
||||
assert_nothing_raised { locked_pages(:welcome).hello_world }
|
||||
assert_nothing_raised { locked_pages(:welcome).versions.first.hello_world }
|
||||
end
|
||||
|
||||
def test_rollback_with_version_class
|
||||
p = pages(:welcome)
|
||||
assert_equal 24, p.version
|
||||
assert_equal 'Welcome to the weblog', p.title
|
||||
|
||||
assert p.revert_to!(p.versions.find_by_version(23)), "Couldn't revert to 23"
|
||||
assert_equal 23, p.version
|
||||
assert_equal 'Welcome to the weblg', p.title
|
||||
end
|
||||
|
||||
def test_rollback_fails_with_invalid_revision
|
||||
p = locked_pages(:welcome)
|
||||
assert !p.revert_to!(locked_pages(:thinking))
|
||||
end
|
||||
|
||||
def test_saves_versioned_copy_with_options
|
||||
p = LockedPage.create! :title => 'first title'
|
||||
assert !p.new_record?
|
||||
assert_equal 1, p.versions.size
|
||||
assert_instance_of LockedPage.versioned_class, p.versions.first
|
||||
end
|
||||
|
||||
def test_rollback_with_version_number_with_options
|
||||
p = locked_pages(:welcome)
|
||||
assert_equal 'Welcome to the weblog', p.title
|
||||
assert_equal 'LockedPage', p.versions.first.version_type
|
||||
|
||||
assert p.revert_to!(p.versions.first.lock_version), "Couldn't revert to 23"
|
||||
assert_equal 'Welcome to the weblg', p.title
|
||||
assert_equal 'LockedPage', p.versions.first.version_type
|
||||
end
|
||||
|
||||
def test_rollback_with_version_class_with_options
|
||||
p = locked_pages(:welcome)
|
||||
assert_equal 'Welcome to the weblog', p.title
|
||||
assert_equal 'LockedPage', p.versions.first.version_type
|
||||
|
||||
assert p.revert_to!(p.versions.first), "Couldn't revert to 1"
|
||||
assert_equal 'Welcome to the weblg', p.title
|
||||
assert_equal 'LockedPage', p.versions.first.version_type
|
||||
end
|
||||
|
||||
def test_saves_versioned_copy_with_sti
|
||||
p = SpecialLockedPage.create! :title => 'first title'
|
||||
assert !p.new_record?
|
||||
assert_equal 1, p.versions.size
|
||||
assert_instance_of LockedPage.versioned_class, p.versions.first
|
||||
assert_equal 'SpecialLockedPage', p.versions.first.version_type
|
||||
end
|
||||
|
||||
def test_rollback_with_version_number_with_sti
|
||||
p = locked_pages(:thinking)
|
||||
assert_equal 'So I was thinking', p.title
|
||||
|
||||
assert p.revert_to!(p.versions.first.lock_version), "Couldn't revert to 1"
|
||||
assert_equal 'So I was thinking!!!', p.title
|
||||
assert_equal 'SpecialLockedPage', p.versions.first.version_type
|
||||
end
|
||||
|
||||
def test_lock_version_works_with_versioning
|
||||
p = locked_pages(:thinking)
|
||||
p2 = LockedPage.find(p.id)
|
||||
|
||||
p.title = 'fresh title'
|
||||
p.save
|
||||
assert_equal 2, p.versions.size # limit!
|
||||
|
||||
assert_raises(ActiveRecord::StaleObjectError) do
|
||||
p2.title = 'stale title'
|
||||
p2.save
|
||||
end
|
||||
end
|
||||
|
||||
def test_version_if_condition
|
||||
p = Page.create! :title => "title"
|
||||
assert_equal 1, p.version
|
||||
|
||||
Page.feeling_good = false
|
||||
p.save
|
||||
assert_equal 1, p.version
|
||||
Page.feeling_good = true
|
||||
end
|
||||
|
||||
def test_version_if_condition2
|
||||
# set new if condition
|
||||
Page.class_eval do
|
||||
def new_feeling_good() title[0..0] == 'a'; end
|
||||
alias_method :old_feeling_good, :feeling_good?
|
||||
alias_method :feeling_good?, :new_feeling_good
|
||||
end
|
||||
|
||||
p = Page.create! :title => "title"
|
||||
assert_equal 1, p.version # version does not increment
|
||||
assert_equal 1, p.versions.count
|
||||
|
||||
p.update_attributes(:title => 'new title')
|
||||
assert_equal 1, p.version # version does not increment
|
||||
assert_equal 1, p.versions.count
|
||||
|
||||
p.update_attributes(:title => 'a title')
|
||||
assert_equal 2, p.version
|
||||
assert_equal 2, p.versions.count
|
||||
|
||||
# reset original if condition
|
||||
Page.class_eval { alias_method :feeling_good?, :old_feeling_good }
|
||||
end
|
||||
|
||||
def test_version_if_condition_with_block
|
||||
# set new if condition
|
||||
old_condition = Page.version_condition
|
||||
Page.version_condition = Proc.new { |page| page.title[0..0] == 'b' }
|
||||
|
||||
p = Page.create! :title => "title"
|
||||
assert_equal 1, p.version # version does not increment
|
||||
assert_equal 1, p.versions.count
|
||||
|
||||
p.update_attributes(:title => 'a title')
|
||||
assert_equal 1, p.version # version does not increment
|
||||
assert_equal 1, p.versions.count
|
||||
|
||||
p.update_attributes(:title => 'b title')
|
||||
assert_equal 2, p.version
|
||||
assert_equal 2, p.versions.count
|
||||
|
||||
# reset original if condition
|
||||
Page.version_condition = old_condition
|
||||
end
|
||||
|
||||
def test_version_no_limit
|
||||
p = Page.create! :title => "title", :body => 'first body'
|
||||
p.save
|
||||
p.save
|
||||
5.times do |i|
|
||||
p.title = "title#{i}"
|
||||
p.save
|
||||
assert_equal "title#{i}", p.title
|
||||
assert_equal (i+2), p.version
|
||||
end
|
||||
end
|
||||
|
||||
def test_version_max_limit
|
||||
p = LockedPage.create! :title => "title"
|
||||
p.update_attributes(:title => "title1")
|
||||
p.update_attributes(:title => "title2")
|
||||
5.times do |i|
|
||||
p.title = "title#{i}"
|
||||
p.save
|
||||
assert_equal "title#{i}", p.title
|
||||
assert_equal (i+4), p.lock_version
|
||||
assert p.versions(true).size <= 2, "locked version can only store 2 versions"
|
||||
end
|
||||
end
|
||||
|
||||
def test_track_altered_attributes_default_value
|
||||
assert !Page.track_altered_attributes
|
||||
assert LockedPage.track_altered_attributes
|
||||
assert SpecialLockedPage.track_altered_attributes
|
||||
end
|
||||
|
||||
def test_track_altered_attributes
|
||||
p = LockedPage.create! :title => "title"
|
||||
assert_equal 1, p.lock_version
|
||||
assert_equal 1, p.versions(true).size
|
||||
|
||||
p.body = 'whoa'
|
||||
assert !p.save_version?
|
||||
p.save
|
||||
assert_equal 2, p.lock_version # still increments version because of optimistic locking
|
||||
assert_equal 1, p.versions(true).size
|
||||
|
||||
p.title = 'updated title'
|
||||
assert p.save_version?
|
||||
p.save
|
||||
assert_equal 3, p.lock_version
|
||||
assert_equal 1, p.versions(true).size # version 1 deleted
|
||||
|
||||
p.title = 'updated title!'
|
||||
assert p.save_version?
|
||||
p.save
|
||||
assert_equal 4, p.lock_version
|
||||
assert_equal 2, p.versions(true).size # version 1 deleted
|
||||
end
|
||||
|
||||
def test_find_versions
|
||||
assert_equal 1, locked_pages(:welcome).versions.find(:all, :conditions => ['title LIKE ?', '%weblog%']).size
|
||||
end
|
||||
|
||||
def test_find_version
|
||||
assert_equal page_versions(:welcome_1), pages(:welcome).versions.find_by_version(23)
|
||||
end
|
||||
|
||||
def test_with_sequence
|
||||
assert_equal 'widgets_seq', Widget.versioned_class.sequence_name
|
||||
3.times { Widget.create! :name => 'new widget' }
|
||||
assert_equal 3, Widget.count
|
||||
assert_equal 3, Widget.versioned_class.count
|
||||
end
|
||||
|
||||
def test_has_many_through
|
||||
assert_equal [authors(:caged), authors(:mly)], pages(:welcome).authors
|
||||
end
|
||||
|
||||
def test_has_many_through_with_custom_association
|
||||
assert_equal [authors(:caged), authors(:mly)], pages(:welcome).revisors
|
||||
end
|
||||
|
||||
def test_referential_integrity
|
||||
pages(:welcome).destroy
|
||||
assert_equal 0, Page.count
|
||||
assert_equal 0, Page::Version.count
|
||||
end
|
||||
|
||||
def test_association_options
|
||||
association = Page.reflect_on_association(:versions)
|
||||
options = association.options
|
||||
assert_equal :delete_all, options[:dependent]
|
||||
|
||||
association = Widget.reflect_on_association(:versions)
|
||||
options = association.options
|
||||
assert_equal :nullify, options[:dependent]
|
||||
assert_equal 'version desc', options[:order]
|
||||
assert_equal 'widget_id', options[:foreign_key]
|
||||
|
||||
widget = Widget.create! :name => 'new widget'
|
||||
assert_equal 1, Widget.count
|
||||
assert_equal 1, Widget.versioned_class.count
|
||||
widget.destroy
|
||||
assert_equal 0, Widget.count
|
||||
assert_equal 1, Widget.versioned_class.count
|
||||
end
|
||||
|
||||
def test_versioned_records_should_belong_to_parent
|
||||
page = pages(:welcome)
|
||||
page_version = page.versions.last
|
||||
assert_equal page, page_version.page
|
||||
end
|
||||
|
||||
def test_unaltered_attributes
|
||||
landmarks(:washington).attributes = landmarks(:washington).attributes.except("id")
|
||||
assert !landmarks(:washington).changed?
|
||||
end
|
||||
|
||||
def test_unchanged_string_attributes
|
||||
landmarks(:washington).attributes = landmarks(:washington).attributes.except("id").inject({}) { |params, (key, value)| params.update(key => value.to_s) }
|
||||
assert !landmarks(:washington).changed?
|
||||
end
|
||||
|
||||
def test_should_find_earliest_version
|
||||
assert_equal page_versions(:welcome_1), pages(:welcome).versions.earliest
|
||||
end
|
||||
|
||||
def test_should_find_latest_version
|
||||
assert_equal page_versions(:welcome_2), pages(:welcome).versions.latest
|
||||
end
|
||||
|
||||
def test_should_find_previous_version
|
||||
assert_equal page_versions(:welcome_1), page_versions(:welcome_2).previous
|
||||
assert_equal page_versions(:welcome_1), pages(:welcome).versions.before(page_versions(:welcome_2))
|
||||
end
|
||||
|
||||
def test_should_find_next_version
|
||||
assert_equal page_versions(:welcome_2), page_versions(:welcome_1).next
|
||||
assert_equal page_versions(:welcome_2), pages(:welcome).versions.after(page_versions(:welcome_1))
|
||||
end
|
||||
|
||||
def test_should_find_version_count
|
||||
assert_equal 2, pages(:welcome).versions.size
|
||||
end
|
||||
|
||||
def test_if_changed_creates_version_if_a_listed_column_is_changed
|
||||
landmarks(:washington).name = "Washington"
|
||||
assert landmarks(:washington).changed?
|
||||
assert landmarks(:washington).altered?
|
||||
end
|
||||
|
||||
def test_if_changed_creates_version_if_all_listed_columns_are_changed
|
||||
landmarks(:washington).name = "Washington"
|
||||
landmarks(:washington).latitude = 1.0
|
||||
landmarks(:washington).longitude = 1.0
|
||||
assert landmarks(:washington).changed?
|
||||
assert landmarks(:washington).altered?
|
||||
end
|
||||
|
||||
def test_if_changed_does_not_create_new_version_if_unlisted_column_is_changed
|
||||
landmarks(:washington).doesnt_trigger_version = "This should not trigger version"
|
||||
assert landmarks(:washington).changed?
|
||||
assert !landmarks(:washington).altered?
|
||||
end
|
||||
|
||||
def test_without_locking_temporarily_disables_optimistic_locking
|
||||
enabled1 = false
|
||||
block_called = false
|
||||
|
||||
ActiveRecord::Base.lock_optimistically = true
|
||||
LockedPage.without_locking do
|
||||
enabled1 = ActiveRecord::Base.lock_optimistically
|
||||
block_called = true
|
||||
end
|
||||
enabled2 = ActiveRecord::Base.lock_optimistically
|
||||
|
||||
assert block_called
|
||||
assert !enabled1
|
||||
assert enabled2
|
||||
end
|
||||
|
||||
def test_without_locking_reverts_optimistic_locking_settings_if_block_raises_exception
|
||||
assert_raises(RuntimeError) do
|
||||
LockedPage.without_locking do
|
||||
raise RuntimeError, "oh noes"
|
||||
end
|
||||
end
|
||||
assert ActiveRecord::Base.lock_optimistically
|
||||
end
|
||||
end
|
2
vendor/plugins/country_code_select/.gitignore
vendored
Normal file
2
vendor/plugins/country_code_select/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
coverage
|
||||
rdoc
|
20
vendor/plugins/country_code_select/MIT-LICENSE
vendored
Normal file
20
vendor/plugins/country_code_select/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2008 Adam Meehan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
vendor/plugins/country_code_select/README
vendored
Normal file
9
vendor/plugins/country_code_select/README
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
== Country Code Select
|
||||
|
||||
A simple country code select helper. Works exactly the same as country_select but uses country codes instead.
|
||||
|
||||
country_code_select(:user, :country, [[ 'US', 'United States' ], [ 'CA', 'Canada' ]])
|
||||
|
||||
== Copyright/License
|
||||
|
||||
Copyright (c) 2008 Russ Smith, released under the MIT license.
|
9
vendor/plugins/country_code_select/README.rdoc
vendored
Normal file
9
vendor/plugins/country_code_select/README.rdoc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
== Country Code Select
|
||||
|
||||
A simple country code select helper. Works exactly the same as country_select but uses country codes instead.
|
||||
|
||||
country_code_select(:user, :country, [[ 'US', 'United States' ], [ 'CA', 'Canada' ]])
|
||||
|
||||
== Copyright/License
|
||||
|
||||
Copyright (c) 2008 Russ Smith, released under the MIT license.
|
31
vendor/plugins/country_code_select/Rakefile
vendored
Normal file
31
vendor/plugins/country_code_select/Rakefile
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'spec/rake/spectask'
|
||||
|
||||
desc 'Default: run specs'
|
||||
task :default => :spec
|
||||
|
||||
spec_files = Rake::FileList["spec/**/*_spec.rb"]
|
||||
|
||||
desc "Run specs"
|
||||
Spec::Rake::SpecTask.new do |t|
|
||||
t.spec_files = spec_files
|
||||
t.spec_opts = ["-c"]
|
||||
end
|
||||
|
||||
desc "Generate code coverage"
|
||||
Spec::Rake::SpecTask.new(:coverage) do |t|
|
||||
t.spec_files = spec_files
|
||||
t.rcov = true
|
||||
t.rcov_opts = ['--exclude', 'spec,/var/lib/gems']
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the country_code_select plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'CountryCodeSelect'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
1
vendor/plugins/country_code_select/init.rb
vendored
Normal file
1
vendor/plugins/country_code_select/init.rb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
require 'country_code_select'
|
8
vendor/plugins/country_code_select/lib/country_code_select.rb
vendored
Normal file
8
vendor/plugins/country_code_select/lib/country_code_select.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'country_code_select/countries'
|
||||
require 'country_code_select/form_builder'
|
||||
require 'country_code_select/form_helpers'
|
||||
require 'country_code_select/instance_tag'
|
||||
|
||||
ActionView::Base.send(:include, CountryCodeSelect::FormHelpers)
|
||||
ActionView::Helpers::InstanceTag.send(:include, CountryCodeSelect::InstanceTag)
|
||||
ActionView::Helpers::FormBuilder.send(:include, CountryCodeSelect::FormBuilder)
|
42
vendor/plugins/country_code_select/lib/country_code_select/countries.rb
vendored
Normal file
42
vendor/plugins/country_code_select/lib/country_code_select/countries.rb
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
module CountryCodeSelect
|
||||
module Countries
|
||||
COUNTRIES = [["Afghanistan", "AF"], ["Albania", "AL"], ["Algeria", "DZ"], ["American Samoa", "AS"], ["Andorra", "AD"], ["Angola", "AO"],
|
||||
["Anguilla", "AI"], ["Antarctica", "AQ"], ["Antigua and Barbuda", "AG"], ["Argentina", "AR"], ["Armenia", "AM"], ["Aruba", "AW"],
|
||||
["Australia", "AU"], ["Austria", "AT"], ["Azerbaidjan", "AZ"], ["Bahamas", "BS"], ["Bahrain", "BH"], ["Banglades", "BD"], ["Barbados", "BB"],
|
||||
["Belarus", "BY"], ["Belgium", "BE"], ["Belize", "BZ"], ["Benin", "BJ"], ["Bermuda", "BM"], ["Bolivia", "BO"], ["Bosnia-Herzegovina", "BA"],
|
||||
["Botswana", "BW"], ["Bouvet Island", "BV"], ["Brazil", "BR"], ["British Indian O. Terr.", "IO"], ["Brunei Darussalam", "BN"], ["Bulgaria", "BG"],
|
||||
["Burkina Faso", "BF"], ["Burundi", "BI"], ["Buthan", "BT"], ["Cambodia", "KH"], ["Cameroon", "CM"], ["Canada", "CA"], ["Cape Verde", "CV"],
|
||||
["Cayman Islands", "KY"], ["Central African Rep.", "CF"], ["Chad", "TD"], ["Chile", "CL"], ["China", "CN"], ["Christmas Island", "CX"],
|
||||
["Cocos (Keeling) Isl.", "CC"], ["Colombia", "CO"], ["Comoros", "KM"], ["Congo", "CG"], ["Cook Islands", "CK"], ["Costa Rica", "CR"],
|
||||
["Croatia", "HR"], ["Cuba", "CU"], ["Cyprus", "CY"], ["Czech Republic", "CZ"], ["Czechoslovakia", "CS"], ["Denmark", "DK"], ["Djibouti", "DJ"],
|
||||
["Dominica", "DM"], ["Dominican Republic", "DO"], ["East Timor", "TP"], ["Ecuador", "EC"], ["Egypt", "EG"], ["El Salvador", "SV"],
|
||||
["Equatorial Guinea", "GQ"], ["Estonia", "EE"], ["Ethiopia", "ET"], ["European Union", "EU"], ["Falkland Isl.(UK)", "FK"], ["Faroe Islands", "FO"], ["Fiji", "FJ"],
|
||||
["Finland", "FI"], ["France", "FR"], ["France (European Ter.)", "FX"], ["French Southern Terr.", "TF"], ["Gabon", "GA"], ["Gambia", "GM"],
|
||||
["Georgia", "GE"], ["Germany", "DE"], ["Ghana", "GH"], ["Gibraltar", "GI"], ["Great Britain (UK)", "GB"], ["Greece", "GR"], ["Greenland", "GL"],
|
||||
["Grenada", "GD"], ["Guadeloupe (Fr.)", "GP"], ["Guam (US)", "GU"], ["Guatemala", "GT"], ["Guinea", "GN"], ["Guinea Bissau", "GW"],
|
||||
["Guyana", "GY"], ["Guyana (Fr.)", "GF"], ["Haiti", "HT"], ["Heard & McDonald Isl.", "HM"], ["Honduras", "HN"], ["Hong Kong", "HK"],
|
||||
["Hungary", "HU"], ["Iceland", "IS"], ["India", "IN"], ["Indonesia", "ID"], ["Iran", "IR"], ["Iraq", "IQ"], ["Ireland", "IE"], ["Israel", "IL"],
|
||||
["Italy", "IT"], ["Ivory Coast", "CI"], ["Jamaica", "JM"], ["Japan", "JP"], ["Jordan", "JO"], ["Kazachstan", "KZ"], ["Kenya", "KE"],
|
||||
["Kirgistan", "KG"], ["Kiribati", "KI"], ["Korea (North)", "KP"], ["Korea (South)", "KR"], ["Kuwait", "KW"], ["Laos", "LA"], ["Latvia", "LV"],
|
||||
["Lebanon", "LB"], ["Lesotho", "LS"], ["Liberia", "LR"], ["Libya", "LY"], ["Liechtenstein", "LI"], ["Lithuania", "LT"], ["Luxembourg", "LU"],
|
||||
["Macau", "MO"], ["Madagascar", "MG"], ["Malawi", "MW"], ["Malaysia", "MY"], ["Maldives", "MV"], ["Mali", "ML"], ["Malta", "MT"],
|
||||
["Marshall Islands", "MH"], ["Martinique (Fr.)", "MQ"], ["Mauritania", "MR"], ["Mauritius", "MU"], ["Mexico", "MX"], ["Micronesia", "FM"],
|
||||
["Moldavia", "MD"], ["Monaco", "MC"], ["Mongolia", "MN"], ["Montserrat", "MS"], ["Morocco", "MA"], ["Mozambique", "MZ"], ["Myanmar", "MM"],
|
||||
["Namibia", "NA"], ["Nauru", "NR"], ["Nepal", "NP"], ["Netherland Antilles", "AN"], ["Netherlands", "NL"], ["Neutral Zone", "NT"],
|
||||
["New Caledonia (Fr.)", "NC"], ["New Zealand", "NZ"], ["Nicaragua", "NI"], ["Niger", "NE"], ["Nigeria", "NG"], ["Niue", "NU"],
|
||||
["Norfolk Island", "NF"], ["Northern Mariana Isl.", "MP"], ["Norway", "NO"], ["Oman", "OM"], ["Pakistan", "PK"], ["Palau", "PW"],
|
||||
["Panama", "PA"], ["Papua New", "PG"], ["Paraguay", "PY"], ["Peru", "PE"], ["Philippines", "PH"], ["Pitcairn", "PN"], ["Poland", "PL"],
|
||||
["Polynesia (Fr.)", "PF"], ["Portugal", "PT"], ["Puerto Rico (US)", "PR"], ["Qatar", "QA"], ["Reunion (Fr.)", "RE"], ["Romania", "RO"],
|
||||
["Russian Federation", "RU"], ["Rwanda", "RW"], ["Saint Lucia", "LC"], ["Samoa", "WS"], ["San Marino", "SM"], ["Saudi Arabia", "SA"],
|
||||
["Senegal", "SN"], ["Seychelles", "SC"], ["Sierra Leone", "SL"], ["Singapore", "SG"], ["Slovak Republic", "SK"], ["Slovenia", "SI"],
|
||||
["Solomon Islands", "SB"], ["Somalia", "SO"], ["South Africa", "ZA"], ["Soviet Union", "SU"], ["Spain", "ES"], ["Sri Lanka", "LK"],
|
||||
["St. Helena", "SH"], ["St. Pierre & Miquelon", "PM"], ["St. Tome and Principe", "ST"], ["St.Kitts Nevis Anguilla", "KN"],
|
||||
["St.Vincent & Grenadines", "VC"], ["Sudan", "SD"], ["Suriname", "SR"], ["Svalbard & Jan Mayen Is", "SJ"], ["Swaziland", "SZ"], ["Sweden", "SE"],
|
||||
["Switzerland", "CH"], ["Syria", "SY"], ["Tadjikistan", "TJ"], ["Taiwan", "TW"], ["Tanzania", "TZ"], ["Thailand", "TH"], ["Togo", "TG"],
|
||||
["Tokelau", "TK"], ["Tonga", "TO"], ["Trinidad & Tobago", "TT"], ["Tunisia", "TN"], ["Turkey", "TR"], ["Turkmenistan", "TM"],
|
||||
["Turks & Caicos Islands", "TC"], ["Tuvalu", "TV"], ["Uganda", "UG"], ["Ukraine", "UA"], ["United Arab Emirates", "AE"], ["United Kingdom", "GB"],
|
||||
["United States", "US"], ["Uruguay", "UY"], ["US Minor outlying Isl.", "UM"], ["Uzbekistan", "UZ"], ["Vanuatu", "VU"], ["Vatican City State", "VA"],
|
||||
["Venezuela", "VE"], ["Vietnam", "VN"], ["Virgin Islands (British)", "VG"], ["Virgin Islands (US)", "VI"], ["Wallis & Futuna Islands", "WF"],
|
||||
["Western Sahara", "EH"], ["Yemen", "YE"], ["Yugoslavia", "YU"], ["Zaire", "ZR"], ["Zambia", "ZM"], ["Zimbabwe", "ZW"]]
|
||||
end
|
||||
end
|
7
vendor/plugins/country_code_select/lib/country_code_select/form_builder.rb
vendored
Normal file
7
vendor/plugins/country_code_select/lib/country_code_select/form_builder.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
module CountryCodeSelect
|
||||
module FormBuilder
|
||||
def country_code_select(method, priority_countries = nil, options= {})
|
||||
@template.country_code_select(@object_name, method, priority_countries, options.merge(:object => @object))
|
||||
end
|
||||
end
|
||||
end
|
7
vendor/plugins/country_code_select/lib/country_code_select/form_helpers.rb
vendored
Normal file
7
vendor/plugins/country_code_select/lib/country_code_select/form_helpers.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
module CountryCodeSelect
|
||||
module FormHelpers
|
||||
def country_code_select(object_name, method, priority_countries = nil, options = {})
|
||||
ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_country_code_select_tag(priority_countries, options)
|
||||
end
|
||||
end
|
||||
end
|
27
vendor/plugins/country_code_select/lib/country_code_select/instance_tag.rb
vendored
Normal file
27
vendor/plugins/country_code_select/lib/country_code_select/instance_tag.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
module CountryCodeSelect
|
||||
module InstanceTag
|
||||
include Countries
|
||||
|
||||
def to_country_code_select_tag(priority_countries, options = {})
|
||||
country_code_select(priority_countries, options)
|
||||
end
|
||||
|
||||
# Adapted from Rails country_select. Just uses country codes instead of full names.
|
||||
def country_code_select(priority_countries, options)
|
||||
selected = object.send(@method_name)
|
||||
|
||||
countries = ''
|
||||
if priority_countries
|
||||
countries += options_for_select(priority_countries, selected)
|
||||
countries += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
|
||||
end
|
||||
|
||||
countries = countries + options_for_select(COUNTRIES, selected)
|
||||
if Rails::VERSION::STRING.to_f < 3
|
||||
content_tag(:select, countries, options.merge(:id => "#{@object_name}_#{@method_name}", :name => "#{@object_name}[#{@method_name}]"))
|
||||
else
|
||||
content_tag(:select, countries, options.merge(:id => "#{@object_name}_#{@method_name}", :name => "#{@object_name}[#{@method_name}]"), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
vendor/plugins/country_code_select/spec/form_builder_spec.rb
vendored
Normal file
7
vendor/plugins/country_code_select/spec/form_builder_spec.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
|
||||
describe CountryCodeSelect::FormHelpers do
|
||||
it "should include country_code_select method" do
|
||||
ActionView::Helpers::FormBuilder.instance_methods.should include('country_code_select')
|
||||
end
|
||||
end
|
26
vendor/plugins/country_code_select/spec/form_helpers_spec.rb
vendored
Normal file
26
vendor/plugins/country_code_select/spec/form_helpers_spec.rb
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
|
||||
describe CountryCodeSelect::FormHelpers do
|
||||
describe "country_code_select" do
|
||||
include CountryCodeSelect::FormHelpers
|
||||
|
||||
before(:each) do
|
||||
@user = mock('User', :country => nil)
|
||||
end
|
||||
|
||||
it "should output a select field with countries" do
|
||||
output = country_code_select(:user, :country)
|
||||
output.should have_tag('select[id=user_country]')
|
||||
end
|
||||
|
||||
it "should output a select field with priority countries" do
|
||||
output = country_code_select(:user, :country, [ 'US', 'United States' ])
|
||||
output.should have_tag('option[value=US]')
|
||||
end
|
||||
|
||||
it "should output a select field with passed attributes" do
|
||||
output = country_code_select(:user, :country, [ 'US', 'United States' ], :class => 'custom_class')
|
||||
output.should have_tag('select[class=custom_class]')
|
||||
end
|
||||
end
|
||||
end
|
31
vendor/plugins/country_code_select/spec/rspec-rails/MIT-LICENSE
vendored
Normal file
31
vendor/plugins/country_code_select/spec/rspec-rails/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
====================================================================
|
||||
== RSpec
|
||||
Copyright (c) 2005-2007 The RSpec Development Team
|
||||
====================================================================
|
||||
== ARTS
|
||||
Copyright (c) 2006 Kevin Clark, Jake Howerton
|
||||
====================================================================
|
||||
== ZenTest
|
||||
Copyright (c) 2001-2006 Ryan Davis, Eric Hodel, Zen Spider Software
|
||||
====================================================================
|
||||
== AssertSelect
|
||||
Copyright (c) 2006 Assaf Arkin
|
||||
====================================================================
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
130
vendor/plugins/country_code_select/spec/rspec-rails/assert_select.rb
vendored
Normal file
130
vendor/plugins/country_code_select/spec/rspec-rails/assert_select.rb
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
# This is a wrapper of assert_select for rspec.
|
||||
|
||||
module Spec # :nodoc:
|
||||
module Rails
|
||||
module Matchers
|
||||
|
||||
class AssertSelect #:nodoc:
|
||||
|
||||
def initialize(assertion, spec_scope, *args, &block)
|
||||
@assertion = assertion
|
||||
@spec_scope = spec_scope
|
||||
@args = args
|
||||
@block = block
|
||||
end
|
||||
|
||||
def matches?(response_or_text, &block)
|
||||
if ActionController::TestResponse === response_or_text and
|
||||
response_or_text.headers.key?('Content-Type') and
|
||||
response_or_text.headers['Content-Type'].to_sym == :xml
|
||||
@args.unshift(HTML::Document.new(response_or_text.body, false, true).root)
|
||||
elsif String === response_or_text
|
||||
@args.unshift(HTML::Document.new(response_or_text).root)
|
||||
end
|
||||
@block = block if block
|
||||
begin
|
||||
@spec_scope.send(@assertion, *@args, &@block)
|
||||
rescue ::Test::Unit::AssertionFailedError => @error
|
||||
end
|
||||
|
||||
@error.nil?
|
||||
end
|
||||
|
||||
def failure_message; @error.message; end
|
||||
def negative_failure_message; "should not #{description}, but did"; end
|
||||
|
||||
def description
|
||||
{
|
||||
:assert_select => "have tag#{format_args(*@args)}",
|
||||
:assert_select_email => "send email#{format_args(*@args)}",
|
||||
}[@assertion]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format_args(*args)
|
||||
return "" if args.empty?
|
||||
return "(#{arg_list(*args)})"
|
||||
end
|
||||
|
||||
def arg_list(*args)
|
||||
args.collect do |arg|
|
||||
arg.respond_to?(:description) ? arg.description : arg.inspect
|
||||
end.join(", ")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# response.should have_tag(*args, &block)
|
||||
# string.should have_tag(*args, &block)
|
||||
#
|
||||
# wrapper for assert_select with additional support for using
|
||||
# css selectors to set expectation on Strings. Use this in
|
||||
# helper specs, for example, to set expectations on the results
|
||||
# of helper methods.
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# # in a controller spec
|
||||
# response.should have_tag("div", "some text")
|
||||
#
|
||||
# # in a helper spec (person_address_tag is a method in the helper)
|
||||
# person_address_tag.should have_tag("input#person_address")
|
||||
#
|
||||
# see documentation for assert_select at http://api.rubyonrails.org/
|
||||
def have_tag(*args, &block)
|
||||
AssertSelect.new(:assert_select, self, *args, &block)
|
||||
end
|
||||
|
||||
# wrapper for a nested assert_select
|
||||
#
|
||||
# response.should have_tag("div#form") do
|
||||
# with_tag("input#person_name[name=?]", "person[name]")
|
||||
# end
|
||||
#
|
||||
# see documentation for assert_select at http://api.rubyonrails.org/
|
||||
def with_tag(*args, &block)
|
||||
should have_tag(*args, &block)
|
||||
end
|
||||
|
||||
# wrapper for a nested assert_select with false
|
||||
#
|
||||
# response.should have_tag("div#1") do
|
||||
# without_tag("span", "some text that shouldn't be there")
|
||||
# end
|
||||
#
|
||||
# see documentation for assert_select at http://api.rubyonrails.org/
|
||||
def without_tag(*args, &block)
|
||||
should_not have_tag(*args, &block)
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# response.should have_rjs(*args, &block)
|
||||
#
|
||||
# wrapper for assert_select_rjs
|
||||
#
|
||||
# see documentation for assert_select_rjs at http://api.rubyonrails.org/
|
||||
def have_rjs(*args, &block)
|
||||
AssertSelect.new(:assert_select_rjs, self, *args, &block)
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# response.should send_email(*args, &block)
|
||||
#
|
||||
# wrapper for assert_select_email
|
||||
#
|
||||
# see documentation for assert_select_email at http://api.rubyonrails.org/
|
||||
def send_email(*args, &block)
|
||||
AssertSelect.new(:assert_select_email, self, *args, &block)
|
||||
end
|
||||
|
||||
# wrapper for assert_select_encoded
|
||||
#
|
||||
# see documentation for assert_select_encoded at http://api.rubyonrails.org/
|
||||
def with_encoded(*args, &block)
|
||||
should AssertSelect.new(:assert_select_encoded, self, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1
vendor/plugins/country_code_select/spec/rspec-rails/rspec-rails.rb
vendored
Normal file
1
vendor/plugins/country_code_select/spec/rspec-rails/rspec-rails.rb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
require 'rspec-rails/assert_select'
|
6
vendor/plugins/country_code_select/spec/spec.opts
vendored
Normal file
6
vendor/plugins/country_code_select/spec/spec.opts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
--colour
|
||||
--format
|
||||
progress
|
||||
--loadby
|
||||
mtime
|
||||
--reverse
|
16
vendor/plugins/country_code_select/spec/spec_helper.rb
vendored
Normal file
16
vendor/plugins/country_code_select/spec/spec_helper.rb
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
$: << File.dirname(__FILE__) + '/../lib' << File.dirname(__FILE__)
|
||||
require 'rubygems'
|
||||
require 'spec'
|
||||
require 'spec/interop/test'
|
||||
|
||||
require 'active_support'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_process'
|
||||
require 'action_view'
|
||||
|
||||
require 'rspec-rails/rspec-rails'
|
||||
require 'country_code_select'
|
||||
|
||||
Spec::Runner.configure do |config|
|
||||
config.include Spec::Rails::Matchers
|
||||
end
|
20
vendor/plugins/dynamic_form/MIT-LICENSE
vendored
Normal file
20
vendor/plugins/dynamic_form/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2010 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
13
vendor/plugins/dynamic_form/README
vendored
Normal file
13
vendor/plugins/dynamic_form/README
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
DynamicForm
|
||||
===========
|
||||
|
||||
DynamicForm holds a few helpers method to help you deal with your models, they are:
|
||||
|
||||
* input(record, method, options = {})
|
||||
* form(record, options = {})
|
||||
* error_message_on(object, method, options={})
|
||||
* error_messages_for(record, options={})
|
||||
|
||||
It also adds f.error_messages and f.error_messages_on to your form builders.
|
||||
|
||||
Copyright (c) 2010 David Heinemeier Hansson, released under the MIT license
|
10
vendor/plugins/dynamic_form/Rakefile
vendored
Normal file
10
vendor/plugins/dynamic_form/Rakefile
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
require 'rake/testtask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the active_model_helper plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'test'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
end
|
12
vendor/plugins/dynamic_form/dynamic_form.gemspec
vendored
Normal file
12
vendor/plugins/dynamic_form/dynamic_form.gemspec
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
Gem::Specification.new do |s|
|
||||
s.name = 'dynamic_form'
|
||||
s.version = '1.0.0'
|
||||
s.author = 'David Heinemeier Hansson'
|
||||
s.email = 'david@loudthinking.com'
|
||||
s.summary = 'Deprecated dynamic form helpers: input, form, error_messages_for, error_messages_on'
|
||||
|
||||
s.add_dependency('rails', '>= 3.0.0')
|
||||
|
||||
s.files = Dir['lib/**/*']
|
||||
s.require_path = 'lib'
|
||||
end
|
1
vendor/plugins/dynamic_form/init.rb
vendored
Normal file
1
vendor/plugins/dynamic_form/init.rb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
require 'dynamic_form'
|
300
vendor/plugins/dynamic_form/lib/action_view/helpers/dynamic_form.rb
vendored
Normal file
300
vendor/plugins/dynamic_form/lib/action_view/helpers/dynamic_form.rb
vendored
Normal file
|
@ -0,0 +1,300 @@
|
|||
require 'action_view/helpers'
|
||||
require 'active_support/i18n'
|
||||
require 'active_support/core_ext/enumerable'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
# The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
|
||||
# method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
|
||||
# is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
|
||||
# In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
|
||||
module DynamicForm
|
||||
# Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
|
||||
# has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
|
||||
#
|
||||
# input("post", "title")
|
||||
# # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
||||
def input(record_name, method, options = {})
|
||||
InstanceTag.new(record_name, method, self).to_tag(options)
|
||||
end
|
||||
|
||||
# Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
|
||||
# has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
|
||||
#
|
||||
# form("post")
|
||||
#
|
||||
# would yield a form like the following (modulus formatting):
|
||||
#
|
||||
# <form action='/posts/create' method='post'>
|
||||
# <p>
|
||||
# <label for="post_title">Title</label><br />
|
||||
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
||||
# </p>
|
||||
# <p>
|
||||
# <label for="post_body">Body</label><br />
|
||||
# <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
|
||||
# </p>
|
||||
# <input name="commit" type="submit" value="Create" />
|
||||
# </form>
|
||||
#
|
||||
# It's possible to specialize the form builder by using a different action name and by supplying another
|
||||
# block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
|
||||
#
|
||||
# form("entry",
|
||||
# :action => "sign",
|
||||
# :input_block => Proc.new { |record, column|
|
||||
# "#{column.human_name}: #{input(record, column.name)}<br />"
|
||||
# })
|
||||
#
|
||||
# would yield a form like the following (modulus formatting):
|
||||
#
|
||||
# <form action="/entries/sign" method="post">
|
||||
# Message:
|
||||
# <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
|
||||
# <input name="commit" type="submit" value="Sign" />
|
||||
# </form>
|
||||
#
|
||||
# It's also possible to add additional content to the form by giving it a block, such as:
|
||||
#
|
||||
# form("entry", :action => "sign") do |form|
|
||||
# form << content_tag("b", "Department")
|
||||
# form << collection_select("department", "id", @departments, "id", "name")
|
||||
# end
|
||||
#
|
||||
# The following options are available:
|
||||
#
|
||||
# * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
|
||||
# * <tt>:input_block</tt> - Specialize the output using a different block, see above.
|
||||
# * <tt>:method</tt> - The method used when submitting the form (default: +post+).
|
||||
# * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
|
||||
# * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
|
||||
def form(record_name, options = {})
|
||||
record = instance_variable_get("@#{record_name}")
|
||||
record = convert_to_model(record)
|
||||
|
||||
options = options.symbolize_keys
|
||||
options[:action] ||= record.persisted? ? "update" : "create"
|
||||
action = url_for(:action => options[:action], :id => record)
|
||||
|
||||
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
|
||||
|
||||
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
|
||||
contents.safe_concat hidden_field(record_name, :id) if record.persisted?
|
||||
contents.safe_concat all_input_tags(record, record_name, options)
|
||||
yield contents if block_given?
|
||||
contents.safe_concat submit_tag(submit_value)
|
||||
contents.safe_concat('</form>')
|
||||
end
|
||||
|
||||
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
|
||||
# This error message is wrapped in a <tt>DIV</tt> tag by default or with <tt>:html_tag</tt> if specified,
|
||||
# which can be extended to include a <tt>:prepend_text</tt> and/or <tt>:append_text</tt> (to properly explain
|
||||
# the error), and a <tt>:css_class</tt> to style it accordingly. +object+ should either be the name of an
|
||||
# instance variable or the actual object. The method can be passed in either as a string or a symbol.
|
||||
# As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
|
||||
#
|
||||
# <%= error_message_on "post", "title" %>
|
||||
# # => <div class="formError">can't be empty</div>
|
||||
#
|
||||
# <%= error_message_on @post, :title %>
|
||||
# # => <div class="formError">can't be empty</div>
|
||||
#
|
||||
# <%= error_message_on "post", "title",
|
||||
# :prepend_text => "Title simply ",
|
||||
# :append_text => " (or it won't work).",
|
||||
# :html_tag => "span",
|
||||
# :css_class => "inputError" %>
|
||||
# # => <span class="inputError">Title simply can't be empty (or it won't work).</span>
|
||||
def error_message_on(object, method, *args)
|
||||
options = args.extract_options!
|
||||
unless args.empty?
|
||||
ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
|
||||
'prepend_text, append_text, html_tag, and css_class arguments', caller)
|
||||
|
||||
options[:prepend_text] = args[0] || ''
|
||||
options[:append_text] = args[1] || ''
|
||||
options[:html_tag] = args[2] || 'div'
|
||||
options[:css_class] = args[3] || 'formError'
|
||||
end
|
||||
options.reverse_merge!(:prepend_text => '', :append_text => '', :html_tag => 'div', :css_class => 'formError')
|
||||
|
||||
object = convert_to_model(object)
|
||||
|
||||
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
|
||||
(errors = obj.errors[method]).presence
|
||||
content_tag(options[:html_tag],
|
||||
(options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
|
||||
:class => options[:css_class]
|
||||
)
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a string with a <tt>DIV</tt> containing all of the error messages for the objects located as instance variables by the names
|
||||
# given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are
|
||||
# provided.
|
||||
#
|
||||
# This <tt>DIV</tt> can be tailored by the following options:
|
||||
#
|
||||
# * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
|
||||
# * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
|
||||
# * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
|
||||
# * <tt>:object</tt> - The object (or array of objects) for which to display errors,
|
||||
# if you need to escape the instance variable convention.
|
||||
# * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
|
||||
# If <tt>:object_name</tt> is not set, the name of the first object will be used.
|
||||
# * <tt>:header_message</tt> - The message in the header of the error div. Pass +nil+
|
||||
# or an empty string to avoid the header message altogether. (Default: "X errors
|
||||
# prohibited this object from being saved").
|
||||
# * <tt>:message</tt> - The explanation message after the header message and before
|
||||
# the error list. Pass +nil+ or an empty string to avoid the explanation message
|
||||
# altogether. (Default: "There were problems with the following fields:").
|
||||
#
|
||||
# To specify the display for one object, you simply provide its name as a parameter.
|
||||
# For example, for the <tt>@user</tt> model:
|
||||
#
|
||||
# error_messages_for 'user'
|
||||
#
|
||||
# You can also supply an object:
|
||||
#
|
||||
# error_messages_for @user
|
||||
#
|
||||
# This will use the last part of the model name in the presentation. For instance, if
|
||||
# this is a MyKlass::User object, this will use "user" as the name in the String. This
|
||||
# is taken from MyKlass::User.model_name.human, which can be overridden.
|
||||
#
|
||||
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
|
||||
# will be the name used in the header message:
|
||||
#
|
||||
# error_messages_for 'user_common', 'user', :object_name => 'user'
|
||||
#
|
||||
# You can also use a number of objects, which will have the same naming semantics
|
||||
# as a single object.
|
||||
#
|
||||
# error_messages_for @user, @post
|
||||
#
|
||||
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
|
||||
# object (or array of objects to use):
|
||||
#
|
||||
# error_messages_for 'user', :object => @question.user
|
||||
#
|
||||
# NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
|
||||
# you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
|
||||
# instance yourself and set it up. View the source of this method to see how easy it is.
|
||||
def error_messages_for(*params)
|
||||
options = params.extract_options!.symbolize_keys
|
||||
|
||||
objects = Array.wrap(options.delete(:object) || params).map do |object|
|
||||
object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model)
|
||||
object = convert_to_model(object)
|
||||
|
||||
if object.class.respond_to?(:model_name)
|
||||
options[:object_name] ||= object.class.model_name.human.downcase
|
||||
end
|
||||
|
||||
object
|
||||
end
|
||||
|
||||
objects.compact!
|
||||
count = objects.inject(0) {|sum, object| sum + object.errors.count }
|
||||
|
||||
unless count.zero?
|
||||
html = {}
|
||||
[:id, :class].each do |key|
|
||||
if options.include?(key)
|
||||
value = options[key]
|
||||
html[key] = value unless value.blank?
|
||||
else
|
||||
html[key] = 'errorExplanation'
|
||||
end
|
||||
end
|
||||
options[:object_name] ||= params.first
|
||||
|
||||
I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale|
|
||||
header_message = if options.include?(:header_message)
|
||||
options[:header_message]
|
||||
else
|
||||
locale.t :header, :count => count, :model => options[:object_name].to_s.gsub('_', ' ')
|
||||
end
|
||||
|
||||
message = options.include?(:message) ? options[:message] : locale.t(:body)
|
||||
|
||||
error_messages = objects.sum do |object|
|
||||
object.errors.full_messages.map do |msg|
|
||||
content_tag(:li, msg)
|
||||
end
|
||||
end.join.html_safe
|
||||
|
||||
contents = ''
|
||||
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
|
||||
contents << content_tag(:p, message) unless message.blank?
|
||||
contents << content_tag(:ul, error_messages)
|
||||
|
||||
content_tag(:div, contents.html_safe, html)
|
||||
end
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def all_input_tags(record, record_name, options)
|
||||
input_block = options[:input_block] || default_input_block
|
||||
record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
|
||||
end
|
||||
|
||||
def default_input_block
|
||||
Proc.new { |record, column| %(<p><label for="#{record}_#{column.name}">#{column.human_name}</label><br />#{input(record, column.name)}</p>) }
|
||||
end
|
||||
|
||||
module InstanceTagMethods
|
||||
def to_tag(options = {})
|
||||
case column_type
|
||||
when :string
|
||||
field_type = @method_name.include?("password") ? "password" : "text"
|
||||
to_input_field_tag(field_type, options)
|
||||
when :text
|
||||
to_text_area_tag(options)
|
||||
when :integer, :float, :decimal
|
||||
to_input_field_tag("text", options)
|
||||
when :date
|
||||
to_date_select_tag(options)
|
||||
when :datetime, :timestamp
|
||||
to_datetime_select_tag(options)
|
||||
when :time
|
||||
to_time_select_tag(options)
|
||||
when :boolean
|
||||
to_boolean_select_tag(options)
|
||||
end
|
||||
end
|
||||
|
||||
def column_type
|
||||
object.send(:column_for_attribute, @method_name).type
|
||||
end
|
||||
end
|
||||
|
||||
module FormBuilderMethods
|
||||
def error_message_on(method, *args)
|
||||
@template.error_message_on(@object || @object_name, method, *args)
|
||||
end
|
||||
|
||||
def error_messages(options = {})
|
||||
@template.error_messages_for(@object_name, objectify_options(options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class InstanceTag
|
||||
include DynamicForm::InstanceTagMethods
|
||||
end
|
||||
|
||||
class FormBuilder
|
||||
include DynamicForm::FormBuilderMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
I18n.load_path << File.expand_path("../../locale/en.yml", __FILE__)
|
8
vendor/plugins/dynamic_form/lib/action_view/locale/en.yml
vendored
Normal file
8
vendor/plugins/dynamic_form/lib/action_view/locale/en.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
en:
|
||||
errors:
|
||||
template:
|
||||
header:
|
||||
one: "1 error prohibited this %{model} from being saved"
|
||||
other: "%{count} errors prohibited this %{model} from being saved"
|
||||
# The variable :count is also available
|
||||
body: "There were problems with the following fields:"
|
5
vendor/plugins/dynamic_form/lib/dynamic_form.rb
vendored
Normal file
5
vendor/plugins/dynamic_form/lib/dynamic_form.rb
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'action_view/helpers/dynamic_form'
|
||||
|
||||
class ActionView::Base
|
||||
include DynamicForm
|
||||
end
|
42
vendor/plugins/dynamic_form/test/dynamic_form_i18n_test.rb
vendored
Normal file
42
vendor/plugins/dynamic_form/test/dynamic_form_i18n_test.rb
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
require 'test_helper'
|
||||
|
||||
class DynamicFormI18nTest < Test::Unit::TestCase
|
||||
include ActionView::Context
|
||||
include ActionView::Helpers::DynamicForm
|
||||
|
||||
attr_reader :request
|
||||
|
||||
def setup
|
||||
@object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
|
||||
@object.stubs :to_model => @object
|
||||
@object.stubs :class => stub(:model_name => stub(:human => ""))
|
||||
|
||||
@object_name = 'book_seller'
|
||||
@object_name_without_underscore = 'book seller'
|
||||
|
||||
stubs(:content_tag).returns 'content_tag'
|
||||
|
||||
I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
|
||||
I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
|
||||
end
|
||||
|
||||
def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
|
||||
I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').never
|
||||
error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en')
|
||||
end
|
||||
|
||||
def test_error_messages_for_given_no_header_option_it_translates_header_message
|
||||
I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns 'header message'
|
||||
error_messages_for(:object => @object, :locale => 'en')
|
||||
end
|
||||
|
||||
def test_error_messages_for_given_a_message_option_it_does_not_translate_message
|
||||
I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).never
|
||||
error_messages_for(:object => @object, :message => 'message', :locale => 'en')
|
||||
end
|
||||
|
||||
def test_error_messages_for_given_no_message_option_it_translates_message
|
||||
I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
|
||||
error_messages_for(:object => @object, :locale => 'en')
|
||||
end
|
||||
end
|
370
vendor/plugins/dynamic_form/test/dynamic_form_test.rb
vendored
Normal file
370
vendor/plugins/dynamic_form/test/dynamic_form_test.rb
vendored
Normal file
File diff suppressed because one or more lines are too long
9
vendor/plugins/dynamic_form/test/test_helper.rb
vendored
Normal file
9
vendor/plugins/dynamic_form/test/test_helper.rb
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
require 'active_support'
|
||||
require 'active_support/core_ext'
|
||||
require 'action_view'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_case'
|
||||
require 'active_model'
|
||||
require 'action_view/helpers/dynamic_form'
|
20
vendor/plugins/has_view_count/MIT-LICENSE
vendored
Normal file
20
vendor/plugins/has_view_count/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2009 Citrus Media Group / Spencer Steffen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
53
vendor/plugins/has_view_count/README.textile
vendored
Normal file
53
vendor/plugins/has_view_count/README.textile
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
h1. HasViewCount
|
||||
|
||||
h3. Adds a polymorphic association that allows any model to have a unique visitor count (based on IP address)
|
||||
|
||||
|
||||
|
||||
h2. Installation
|
||||
|
||||
<pre><code>script/plugin install git://github.com/citrus/has_view_count.git
|
||||
- or -
|
||||
git submodule add git://github.com/citrus/has_view_count.git vendor/plugins/has_view_count
|
||||
|
||||
script/generate has_view_count
|
||||
rake db:migrate
|
||||
restart mongrel
|
||||
</code></pre>
|
||||
|
||||
_This will generate_
|
||||
|
||||
<pre><code>db/migrate/XXXXXXXXXXXXXX_has_view_count_migration.rb
|
||||
app/models/view_count.rb
|
||||
</code></pre>
|
||||
|
||||
|
||||
***************************************************************************
|
||||
|
||||
h2. Example
|
||||
|
||||
|
||||
h4. Model:
|
||||
|
||||
<pre><code>class Post < ActiveRecord::Base
|
||||
has_view_count
|
||||
end
|
||||
</code></pre>
|
||||
|
||||
|
||||
h4. Controller:
|
||||
|
||||
<pre><code>class PostsController < ApplicationController
|
||||
after_filter :record_view_count, :only => :show
|
||||
...
|
||||
...
|
||||
...
|
||||
private
|
||||
def record_view_count
|
||||
@post.record_view_count(request.remote_ip, logged_in?) # use logged_in? if you have restful_authentication installed
|
||||
end
|
||||
end
|
||||
</code></pre>
|
||||
|
||||
|
||||
_Copyright (c) 2009 Citrus Media Group / Spencer Steffen, released under the MIT license_
|
12
vendor/plugins/has_view_count/generators/has_view_count/has_view_count_generator.rb
vendored
Normal file
12
vendor/plugins/has_view_count/generators/has_view_count/has_view_count_generator.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
class HasViewCountGenerator < Rails::Generator::Base
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
m.template 'model.rb', 'app/models/view_count.rb'
|
||||
end
|
||||
end
|
||||
|
||||
def file_name
|
||||
"has_view_count_migration"
|
||||
end
|
||||
end
|
14
vendor/plugins/has_view_count/generators/has_view_count/templates/migration.rb
vendored
Normal file
14
vendor/plugins/has_view_count/generators/has_view_count/templates/migration.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
class HasViewCountMigration < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :view_counts do |t|
|
||||
t.references :viewable, :polymorphic => true
|
||||
t.string :ip_address
|
||||
t.boolean :logged_in
|
||||
t.datetime :created_at
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :view_counts
|
||||
end
|
||||
end
|
10
vendor/plugins/has_view_count/generators/has_view_count/templates/model.rb
vendored
Normal file
10
vendor/plugins/has_view_count/generators/has_view_count/templates/model.rb
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
class ViewCount < ActiveRecord::Base
|
||||
|
||||
belongs_to :viewable, :polymorphic => true
|
||||
validates_uniqueness_of :ip_address, :scope => [ :viewable_id, :viewable_type, :viewed_on ]
|
||||
|
||||
def viewed_on
|
||||
self.created_at.srftime('%x')
|
||||
end
|
||||
|
||||
end
|
10
vendor/plugins/has_view_count/has_view_count.gemspec
vendored
Normal file
10
vendor/plugins/has_view_count/has_view_count.gemspec
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{has_view_count}
|
||||
s.version = "0.8.0"
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Citrus Media Group << Spencer Steffen"]
|
||||
s.date = %q{2009-07-08}
|
||||
s.description = %q{This plugin makes for easy page view counting.}
|
||||
end
|
1
vendor/plugins/has_view_count/init.rb
vendored
Normal file
1
vendor/plugins/has_view_count/init.rb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
require File.join(Rails.root, 'vendor', 'plugins', 'has_view_count', 'lib', 'has_view_count.rb')
|
37
vendor/plugins/has_view_count/lib/has_view_count.rb
vendored
Normal file
37
vendor/plugins/has_view_count/lib/has_view_count.rb
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
module Citrus
|
||||
module HasViewCount
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def has_view_count
|
||||
has_many :view_counts, :as => :viewable, :dependent => :destroy
|
||||
include Citrus::HasViewCount::InstanceMethods
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def record_view_count(ip_address, logged_in = false)
|
||||
self.view_counts.create(:viewable => self, :ip_address => ip_address, :logged_in => logged_in)
|
||||
return self
|
||||
end
|
||||
|
||||
def view_count
|
||||
self.view_counts.length
|
||||
end
|
||||
|
||||
def view_count_string(str = "view")
|
||||
return "#{view_count} #{str.singularize}" if view_count == 1
|
||||
return "#{view_count} #{str.pluralize}" unless view_count == 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
ActiveRecord::Base.send(:include, Citrus::HasViewCount)
|
296
vendor/plugins/rcon/bin/rcontool
vendored
Normal file
296
vendor/plugins/rcon/bin/rcontool
vendored
Normal file
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/env ruby
|
||||
################################################################
|
||||
#
|
||||
# rcontool - shell interface to rcon commands
|
||||
#
|
||||
# (C) 2006 Erik Hollensbe, License details below
|
||||
#
|
||||
# Use 'rcontool -h' for usage instructions.
|
||||
#
|
||||
# The compilation of software known as rcontool is distributed under the
|
||||
# following terms:
|
||||
# Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source form, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
#
|
||||
################################################################
|
||||
|
||||
#
|
||||
# rubygems hack
|
||||
#
|
||||
|
||||
begin
|
||||
require 'rubygems'
|
||||
rescue LoadError => e
|
||||
end
|
||||
begin
|
||||
require 'rcon'
|
||||
require 'ip'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "rcontool requires the rcon and ip libraries be installed."
|
||||
$stderr.puts "You can find them both via rubygems or at http://rubyforge.org."
|
||||
exit -1
|
||||
end
|
||||
|
||||
RCONTOOL_VERSION = '0.1.0'
|
||||
|
||||
require 'optparse'
|
||||
require 'ostruct'
|
||||
|
||||
#
|
||||
# Manages our options
|
||||
#
|
||||
|
||||
def get_options
|
||||
options = OpenStruct.new
|
||||
# ip address (IP::Address object)
|
||||
options.ip_address = nil
|
||||
# port (integer)
|
||||
options.port = nil
|
||||
# password
|
||||
options.password = nil
|
||||
# protocol type (one of :hlds, :source, :oldquake, :newquake)
|
||||
options.protocol_type = nil
|
||||
# verbose, spit out extra information
|
||||
options.verbose = false
|
||||
# command to execute on the server
|
||||
options.command = nil
|
||||
|
||||
optparse = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: #{File.basename $0} <ip_address:port> <command> [options]"
|
||||
opts.separator ""
|
||||
opts.separator "Options:"
|
||||
|
||||
opts.on("--ip-address [ADDRESS]",
|
||||
"Provide an IP address to connect to. Does not take a port.") do |ip_address|
|
||||
if ! options.ip_address.nil?
|
||||
$stderr.puts "Error: you have already provided an IP Address."
|
||||
$stderr.puts opts
|
||||
exit -1
|
||||
end
|
||||
|
||||
options.ip_address = IP::Address.new(ip_address)
|
||||
end
|
||||
|
||||
opts.on("-r", "--port [PORT]",
|
||||
"Port to connect to.") do |port|
|
||||
if ! options.port.nil?
|
||||
$stderr.puts "Error: you have already provided a port."
|
||||
$stderr.puts opts
|
||||
exit -1
|
||||
end
|
||||
|
||||
options.port = port.to_i
|
||||
end
|
||||
|
||||
opts.on("-c", "--command [COMMAND]",
|
||||
"Command to run on the server.") do |command|
|
||||
if ! options.command.nil?
|
||||
$stderr.puts "Error: you have already provided a command."
|
||||
$stderr.puts opts
|
||||
exit -1
|
||||
end
|
||||
|
||||
options.command = command
|
||||
end
|
||||
|
||||
opts.on("-p", "--password [PASSWORD]",
|
||||
"Provide a password on the command line.") do |password|
|
||||
options.password = password
|
||||
end
|
||||
|
||||
opts.on("-f", "--password-from [FILENAME]",
|
||||
"Get the password from a file (use '/dev/fd/0' or '/dev/stdin' to read from Standard Input).") do |filename|
|
||||
if !filename.nil?
|
||||
f = File.open(filename)
|
||||
options.password = f.gets.chomp
|
||||
f.close
|
||||
else
|
||||
$stderr.puts "Error: filename (from -f) is not valid."
|
||||
$stderr.puts opts
|
||||
exit -1
|
||||
end
|
||||
end
|
||||
|
||||
opts.on("-t", "--protocol-type [TYPE]", [:hlds, :source, :oldquake, :newquake],
|
||||
"Type of rcon connection to make: (hlds, source, oldquake, newquake).",
|
||||
" Note: oldquake is quake1/quakeworld, newquake is quake2/3.") do |protocol_type|
|
||||
options.protocol_type = protocol_type
|
||||
end
|
||||
|
||||
opts.on("-v", "--[no-]verbose",
|
||||
"Run verbosely, print information about each packet recieved and turnaround times.") do |verbose|
|
||||
options.verbose = verbose
|
||||
end
|
||||
|
||||
opts.on("-h", "--help",
|
||||
"This help message.") do
|
||||
$stderr.puts opts
|
||||
exit -1
|
||||
end
|
||||
|
||||
opts.on("--version", "Print the version information.") do
|
||||
$stderr.puts "This is rcontool version #{RCONTOOL_VERSION},"
|
||||
$stderr.puts "it is located at #{File.expand_path $0}."
|
||||
exit -1
|
||||
end
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Note: IP, port, protocol type, password and command are required to function."
|
||||
opts.separator ""
|
||||
opts.separator "Examples (all are equivalent):"
|
||||
opts.separator "\t#{File.basename($0)} 10.0.0.11 status -t hlds -r 27015 -p foobar"
|
||||
opts.separator "\techo 'foobar' | #{File.basename($0)} 10.0.0.11:27015 status -t hlds -f /dev/stdin"
|
||||
opts.separator "\t#{File.basename($0)} --ip-address 10.0.0.11 --port 27015 -c status -t hlds -f file_with_password"
|
||||
opts.separator ""
|
||||
|
||||
end
|
||||
|
||||
################################################################
|
||||
#
|
||||
# This hackery is to help facilitate the bareword options if
|
||||
# they exist, while still allowing for the option parser
|
||||
# to work properly.
|
||||
#
|
||||
################################################################
|
||||
|
||||
s1 = ARGV.shift
|
||||
s2 = ARGV.shift
|
||||
|
||||
begin
|
||||
options.ip_address = IP::Address::IPv4.new(s1)
|
||||
options.command = s2
|
||||
rescue IP::AddressException => e
|
||||
# attempt to split it first... not sure how to best handle this situation
|
||||
begin
|
||||
ip,port = s1.split(/:/, 2)
|
||||
options.ip_address = IP::Address::IPv4.new(ip)
|
||||
options.port = port.to_i
|
||||
options.command = s2
|
||||
rescue Exception => e
|
||||
end
|
||||
|
||||
if [options.ip_address, options.port].include? nil
|
||||
ARGV.unshift(s2)
|
||||
ARGV.unshift(s1)
|
||||
end
|
||||
end
|
||||
|
||||
optparse.parse!
|
||||
|
||||
if [options.ip_address, options.protocol_type, options.port, options.password, options.command].include? nil
|
||||
$stderr.puts optparse
|
||||
exit -1
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
def verbose(string)
|
||||
$stderr.puts string if $options.verbose
|
||||
end
|
||||
|
||||
def dump_source_packet(packet)
|
||||
if $options.verbose
|
||||
verbose "Request ID: #{packet.request_id}"
|
||||
verbose "Packet Size: #{packet.packet_size}"
|
||||
verbose "Response Type: #{packet.command_type}"
|
||||
end
|
||||
end
|
||||
|
||||
################################################################
|
||||
#
|
||||
# start main block
|
||||
#
|
||||
################################################################
|
||||
|
||||
$options = get_options
|
||||
|
||||
################################################################
|
||||
#
|
||||
# Source query
|
||||
#
|
||||
################################################################
|
||||
|
||||
if $options.protocol_type == :source
|
||||
verbose "Protocol type 'SOURCE' selected."
|
||||
|
||||
rcon = RCon::Query::Source.new($options.ip_address.ip_address, $options.port)
|
||||
|
||||
# if we have a verbose request, give all the information we can about
|
||||
# the query, including the packet information.
|
||||
rcon.return_packets = $options.verbose
|
||||
|
||||
verbose "Attempting authentication to #{$options.ip_address.ip_address}:#{$options.port} with password '#{$options.password}'"
|
||||
|
||||
value = rcon.auth $options.password
|
||||
|
||||
dump_source_packet value
|
||||
|
||||
if ($options.verbose && value.command_type == RCon::Packet::Source::RESPONSE_AUTH) || value
|
||||
verbose "Authentication succeeded. Sending command: '#{$options.command}'"
|
||||
|
||||
value = rcon.command $options.command
|
||||
|
||||
dump_source_packet value
|
||||
verbose ""
|
||||
|
||||
if $options.verbose
|
||||
puts value.string1
|
||||
else
|
||||
puts value
|
||||
end
|
||||
|
||||
exit 0
|
||||
else
|
||||
$stderr.puts "Authentication failed."
|
||||
exit 1
|
||||
end
|
||||
|
||||
################################################################
|
||||
#
|
||||
# Original Query
|
||||
#
|
||||
################################################################
|
||||
|
||||
else
|
||||
rcon = nil
|
||||
case $options.protocol_type
|
||||
when :hlds
|
||||
verbose "Protocol type 'HLDS' selected"
|
||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
||||
RCon::Query::Original::HLDS)
|
||||
when :oldquake
|
||||
verbose "Protocol type 'OLDQUAKE' selected"
|
||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
||||
RCon::Query::Original::QUAKEWORLD)
|
||||
when :newquake
|
||||
verbose "Protocol type 'NEWQUAKE' selected"
|
||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
||||
RCon::Query::Original::NEWQUAKE)
|
||||
end
|
||||
verbose "Attempting transmission to #{$options.ip_address.ip_address}:#{$options.port}"
|
||||
verbose "Using password: '#{$options.password}' and sending command: '#{$options.command}'"
|
||||
verbose ""
|
||||
string = rcon.command($options.command)
|
||||
|
||||
puts string
|
||||
exit 0
|
||||
end
|
||||
|
499
vendor/plugins/rcon/lib/rcon.rb
vendored
Normal file
499
vendor/plugins/rcon/lib/rcon.rb
vendored
Normal file
|
@ -0,0 +1,499 @@
|
|||
# encoding: US-ASCII
|
||||
|
||||
require 'socket'
|
||||
|
||||
#
|
||||
# RCon is a module to work with Quake 1/2/3, Half-Life, and Half-Life
|
||||
# 2 (Source Engine) RCon (Remote Console) protocols.
|
||||
#
|
||||
# Version:: 0.2.0
|
||||
# Author:: Erik Hollensbe <erik@hollensbe.org>
|
||||
# License:: BSD
|
||||
# Contact:: erik@hollensbe.org
|
||||
# Copyright:: Copyright (c) 2005-2006 Erik Hollensbe
|
||||
#
|
||||
# The relevant modules to query RCon are in the RCon::Query namespace,
|
||||
# under RCon::Query::Original (for Quake 1/2/3 and Half-Life), and
|
||||
# RCon::Query::Source (for HL2 and CS: Source, and other Source Engine
|
||||
# games). The RCon::Packet namespace is used to manage complex packet
|
||||
# structures if required. The Original protocol does not require
|
||||
# this, but Source does.
|
||||
#
|
||||
# Usage is fairly simple:
|
||||
#
|
||||
# # Note: Other classes have different constructors
|
||||
#
|
||||
# rcon = RCon::Query::Source.new("10.0.0.1", 27015)
|
||||
#
|
||||
# rcon.auth("foobar") # source only
|
||||
#
|
||||
# rcon.command("mp_friendlyfire") => "mp_friendlyfire = 1"
|
||||
#
|
||||
# rcon.cvar("mp_friendlyfire") => 1
|
||||
#
|
||||
#--
|
||||
#
|
||||
# The compilation of software known as rcon.rb is distributed under the
|
||||
# following terms:
|
||||
# Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source form, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
#++
|
||||
|
||||
|
||||
class RCon
|
||||
class Packet
|
||||
# placeholder so ruby doesn't bitch
|
||||
end
|
||||
class Query
|
||||
|
||||
#
|
||||
# Convenience method to scrape input from cvar output and return that data.
|
||||
# Returns integers as a numeric type if possible.
|
||||
#
|
||||
# ex: rcon.cvar("mp_friendlyfire") => 1
|
||||
#
|
||||
|
||||
def cvar(cvar_name)
|
||||
response = command(cvar_name)
|
||||
match = /^.+?\s(?:is|=)\s"([^"]+)".*$/.match response
|
||||
match = match[1]
|
||||
if /\D/.match match
|
||||
return match
|
||||
else
|
||||
return match.to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# RCon::Packet::Source generates a packet structure useful for
|
||||
# RCon::Query::Source protocol queries.
|
||||
#
|
||||
# This class is primarily used internally, but is available if you
|
||||
# want to do something more advanced with the Source RCon
|
||||
# protocol.
|
||||
#
|
||||
# Use at your own risk.
|
||||
#
|
||||
|
||||
class RCon::Packet::Source
|
||||
# execution command
|
||||
COMMAND_EXEC = 2
|
||||
# auth command
|
||||
COMMAND_AUTH = 3
|
||||
# auth response
|
||||
RESPONSE_AUTH = 2
|
||||
# normal response
|
||||
RESPONSE_NORM = 0
|
||||
# packet trailer
|
||||
TRAILER = "\x00\x00"
|
||||
|
||||
# size of the packet (10 bytes for header + string1 length)
|
||||
attr_accessor :packet_size
|
||||
# Request Identifier, used in managing multiple requests at once
|
||||
attr_accessor :request_id
|
||||
# Type of command, normally COMMAND_AUTH or COMMAND_EXEC. In response packets, RESPONSE_AUTH or RESPONSE_NORM
|
||||
attr_accessor :command_type
|
||||
# First string, the only used one in the protocol, contains
|
||||
# commands and responses. Null terminated.
|
||||
attr_accessor :string1
|
||||
# Second string, unused by the protocol. Null terminated.
|
||||
attr_accessor :string2
|
||||
|
||||
#
|
||||
# Generate a command packet to be sent to an already
|
||||
# authenticated RCon connection. Takes the command as an
|
||||
# argument.
|
||||
#
|
||||
def command(string)
|
||||
@request_id = rand(1000)
|
||||
@string1 = string
|
||||
@string2 = TRAILER
|
||||
@command_type = COMMAND_EXEC
|
||||
|
||||
@packet_size = build_packet.length
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an authentication packet to be sent to a newly
|
||||
# started RCon connection. Takes the RCon password as an
|
||||
# argument.
|
||||
#
|
||||
def auth(string)
|
||||
@request_id = rand(1000)
|
||||
@string1 = string
|
||||
@string2 = TRAILER
|
||||
@command_type = COMMAND_AUTH
|
||||
|
||||
@packet_size = build_packet.length
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
#
|
||||
# Builds a packet ready to deliver, without the size prepended.
|
||||
# Used to calculate the packet size, use #to_s to get the packet
|
||||
# that srcds actually needs.
|
||||
#
|
||||
def build_packet
|
||||
return [@request_id, @command_type, @string1, @string2].pack("VVa#{@string1.length}a2")
|
||||
end
|
||||
|
||||
# Returns a string representation of the packet, useful for
|
||||
# sending and debugging. This include the packet size.
|
||||
def to_s
|
||||
packet = build_packet
|
||||
@packet_size = packet.length
|
||||
return [@packet_size].pack("V") + packet
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# RCon::Query::Original queries Quake 1/2/3 and Half-Life servers
|
||||
# with the rcon protocol. This protocol travels over UDP to the
|
||||
# game server port, and requires an initial authentication step,
|
||||
# the information of which is provided at construction time.
|
||||
#
|
||||
# Some of the work here (namely the RCon packet structure) was taken
|
||||
# from the KKRcon code, which is written in perl.
|
||||
#
|
||||
# One query per authentication is allowed.
|
||||
#
|
||||
|
||||
class RCon::Query::Original < RCon::Query
|
||||
# HLDS-Based Servers
|
||||
HLDS = "l"
|
||||
# QuakeWorld/Quake 1 Servers
|
||||
QUAKEWORLD = "n"
|
||||
# Quake 2/3 Servers
|
||||
NEWQUAKE = ""
|
||||
|
||||
# Request to be sent to server
|
||||
attr_reader :request
|
||||
# Response from server
|
||||
attr_reader :response
|
||||
# Challenge ID (served by server-side of connection)
|
||||
attr_reader :challenge_id
|
||||
# UDPSocket object
|
||||
attr_reader :socket
|
||||
# Host of connection
|
||||
attr_reader :host
|
||||
# Port of connection
|
||||
attr_reader :port
|
||||
# RCon password
|
||||
attr_reader :password
|
||||
# type of server
|
||||
attr_reader :server_type
|
||||
|
||||
#
|
||||
# Creates a RCon::Query::Original object for use.
|
||||
#
|
||||
# The type (the default of which is HLDS), has multiple possible
|
||||
# values:
|
||||
#
|
||||
# HLDS - Half Life 1 (will not work with older versions of HLDS)
|
||||
#
|
||||
# QUAKEWORLD - QuakeWorld/Quake 1
|
||||
#
|
||||
# NEWQUAKE - Quake 2/3 (and many derivatives)
|
||||
#
|
||||
|
||||
def initialize(host, port, password, type=HLDS)
|
||||
@host = host
|
||||
@port = port
|
||||
@password = password
|
||||
@server_type = type
|
||||
end
|
||||
|
||||
#
|
||||
# Sends a request given as the argument, and returns the
|
||||
# response as a string.
|
||||
#
|
||||
def command(request)
|
||||
@request = request
|
||||
@challenge_id = nil
|
||||
|
||||
establish_connection
|
||||
|
||||
@socket.print "\xFF" * 4 + "challenge rcon\n\x00"
|
||||
|
||||
tmp = retrieve_socket_data
|
||||
challenge_id = /challenge rcon (\d+)/.match tmp
|
||||
if challenge_id
|
||||
@challenge_id = challenge_id[1]
|
||||
end
|
||||
|
||||
if @challenge_id.nil?
|
||||
raise RCon::NetworkException.new("RCon challenge ID never returned: wrong rcon password?")
|
||||
end
|
||||
|
||||
@socket.print "\xFF" * 4 + "rcon #{@challenge_id} \"#{@password}\" #{@request}\n\x00"
|
||||
@response = retrieve_socket_data
|
||||
|
||||
@response.sub!(/^\xFF\xFF\xFF\xFF#{@server_type}/, "")
|
||||
@response.sub!(/\x00+$/, "")
|
||||
|
||||
return @response
|
||||
end
|
||||
|
||||
#
|
||||
# Disconnects the RCon connection.
|
||||
#
|
||||
def disconnect
|
||||
if @socket
|
||||
@socket.close
|
||||
@socket = nil
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Establishes the connection.
|
||||
#
|
||||
def establish_connection
|
||||
if @socket.nil?
|
||||
@socket = UDPSocket.new
|
||||
@socket.connect(@host, @port)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Generic method to pull data from the socket.
|
||||
#
|
||||
|
||||
def retrieve_socket_data
|
||||
return "" if @socket.nil?
|
||||
|
||||
retval = ""
|
||||
loop do
|
||||
break unless IO.select([@socket], nil, nil, 10)
|
||||
packet = @socket.recv(8192)
|
||||
retval << packet
|
||||
break if packet.length < 8192
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# RCon::Query::Source sends queries to a "Source" Engine server,
|
||||
# such as Half-Life 2: Deathmatch, Counter-Strike: Source, or Day
|
||||
# of Defeat: Source.
|
||||
#
|
||||
# Note that one authentication packet needs to be sent to send
|
||||
# multiple commands. Sending multiple authentication packets may
|
||||
# damage the current connection and require it to be reset.
|
||||
#
|
||||
# Note: If the attribute 'return_packets' is set to true, the full
|
||||
# RCon::Packet::Source object is returned, instead of just a string
|
||||
# with the headers stripped. Useful for debugging.
|
||||
#
|
||||
|
||||
class RCon::Query::Source < RCon::Query
|
||||
# RCon::Packet::Source object that was sent as a result of the last query
|
||||
attr_reader :packet
|
||||
# TCPSocket object
|
||||
attr_reader :socket
|
||||
# Host of connection
|
||||
attr_reader :host
|
||||
# Port of connection
|
||||
attr_reader :port
|
||||
# Authentication Status
|
||||
attr_reader :authed
|
||||
# return full packet, or just data?
|
||||
attr_accessor :return_packets
|
||||
|
||||
#
|
||||
# Given a host and a port (dotted-quad or hostname OK), creates
|
||||
# a RCon::Query::Source object. Note that this will still
|
||||
# require an authentication packet (see the auth() method)
|
||||
# before commands can be sent.
|
||||
#
|
||||
|
||||
def initialize(host, port)
|
||||
@host = host
|
||||
@port = port
|
||||
@socket = nil
|
||||
@packet = nil
|
||||
@authed = false
|
||||
@return_packets = false
|
||||
end
|
||||
|
||||
#
|
||||
# See RCon::Query#cvar.
|
||||
#
|
||||
|
||||
def cvar(cvar_name)
|
||||
return_packets = @return_packets
|
||||
@return_packets = false
|
||||
response = super
|
||||
@return_packets = return_packets
|
||||
return response
|
||||
end
|
||||
|
||||
#
|
||||
# Sends a RCon command to the server. May be used multiple times
|
||||
# after an authentication is successful.
|
||||
#
|
||||
# See the class-level documentation on the 'return_packet' attribute
|
||||
# for return values. The default is to return a string containing
|
||||
# the response.
|
||||
#
|
||||
|
||||
def command(command)
|
||||
|
||||
if ! @authed
|
||||
raise RCon::NetworkException.new("You must authenticate the connection successfully before sending commands.")
|
||||
end
|
||||
|
||||
@packet = RCon::Packet::Source.new
|
||||
@packet.command(command)
|
||||
|
||||
@socket.print @packet.to_s
|
||||
rpacket = build_response_packet
|
||||
|
||||
if rpacket.command_type != RCon::Packet::Source::RESPONSE_NORM
|
||||
raise RCon::NetworkException.new("error sending command: #{rpacket.command_type}")
|
||||
end
|
||||
|
||||
if @return_packets
|
||||
return rpacket
|
||||
else
|
||||
return rpacket.string1
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Requests authentication from the RCon server, given a
|
||||
# password. Is only expected to be used once.
|
||||
#
|
||||
# See the class-level documentation on the 'return_packet' attribute
|
||||
# for return values. The default is to return a true value if auth
|
||||
# succeeded.
|
||||
#
|
||||
|
||||
def auth(password)
|
||||
establish_connection
|
||||
|
||||
@packet = RCon::Packet::Source.new
|
||||
@packet.auth(password)
|
||||
|
||||
@socket.print @packet.to_s
|
||||
# on auth, one junk packet is sent
|
||||
rpacket = nil
|
||||
2.times { rpacket = build_response_packet }
|
||||
|
||||
if rpacket.command_type != RCon::Packet::Source::RESPONSE_AUTH
|
||||
raise RCon::NetworkException.new("error authenticating: #{rpacket.command_type}")
|
||||
end
|
||||
|
||||
@authed = true
|
||||
if @return_packets
|
||||
return rpacket
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :authenticate, :auth
|
||||
|
||||
#
|
||||
# Disconnects from the Source server.
|
||||
#
|
||||
|
||||
def disconnect
|
||||
if @socket
|
||||
@socket.close
|
||||
@socket = nil
|
||||
@authed = false
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Builds a RCon::Packet::Source packet based on the response
|
||||
# given by the server.
|
||||
#
|
||||
def build_response_packet
|
||||
rpacket = RCon::Packet::Source.new
|
||||
total_size = 0
|
||||
request_id = 0
|
||||
type = 0
|
||||
response = ""
|
||||
message = ""
|
||||
|
||||
|
||||
loop do
|
||||
break unless IO.select([@socket], nil, nil, 10)
|
||||
|
||||
#
|
||||
# TODO: clean this up - read everything and then unpack.
|
||||
#
|
||||
|
||||
tmp = @socket.recv(14)
|
||||
if tmp.nil?
|
||||
return nil
|
||||
end
|
||||
size, request_id, type, message = tmp.unpack("VVVa*")
|
||||
total_size += size
|
||||
|
||||
# special case for authentication
|
||||
break if message.sub!(/\x00\x00$/, "")
|
||||
|
||||
response << message
|
||||
|
||||
# the 'size - 10' here accounts for the fact that we've snarfed 14 bytes,
|
||||
# the size (which is 4 bytes) is not counted, yet represents the rest
|
||||
# of the packet (which we have already taken 10 bytes from)
|
||||
|
||||
tmp = @socket.recv(size - 10)
|
||||
response << tmp
|
||||
response.sub!(/\x00\x00$/, "")
|
||||
end
|
||||
|
||||
rpacket.packet_size = total_size
|
||||
rpacket.request_id = request_id
|
||||
rpacket.command_type = type
|
||||
|
||||
# strip nulls (this is actually the end of string1 and string2)
|
||||
rpacket.string1 = response.sub(/\x00\x00$/, "")
|
||||
return rpacket
|
||||
end
|
||||
|
||||
# establishes a connection to the server.
|
||||
def establish_connection
|
||||
if @socket.nil?
|
||||
@socket = TCPSocket.new(@host, @port)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Exception class for network errors
|
||||
class RCon::NetworkException < Exception
|
||||
end
|
13
vendor/plugins/rcon/rcon.gemspec
vendored
Normal file
13
vendor/plugins/rcon/rcon.gemspec
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
spec = Gem::Specification.new
|
||||
spec.name = "rcon"
|
||||
spec.version = "0.2.1"
|
||||
spec.author = "Erik Hollensbe"
|
||||
spec.email = "erik@hollensbe.org"
|
||||
spec.summary = "Ruby class to work with Quake 1/2/3, Half-Life and Source Engine rcon (remote console)"
|
||||
spec.has_rdoc = true
|
||||
spec.autorequire = "rcon"
|
||||
spec.bindir = 'bin'
|
||||
spec.executables << 'rcontool'
|
||||
spec.add_dependency('ip', '>= 0.2.1')
|
||||
spec.files = Dir['lib/rcon.rb'] + Dir['bin/rcontool']
|
||||
spec.rubyforge_project = 'rcon'
|
1360
vendor/plugins/rcon/setup.rb
vendored
Normal file
1360
vendor/plugins/rcon/setup.rb
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue