diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 5407e140..00000000 --- a/Rakefile +++ /dev/null @@ -1,202 +0,0 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -$VERBOSE = nil -TEST_CHANGES_SINCE = Time.now - 600 - -desc "Run all the tests on a fresh test database" -task :default => [ :test_units, :test_functional ] - - -desc 'Require application environment.' -task :environment do - unless defined? RAILS_ROOT - require File.dirname(__FILE__) + '/config/environment' - end -end - -desc "Generate API documentation, show coding stats" -task :doc => [ :appdoc, :stats ] - - -# Look up tests for recently modified sources. -def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) - FileList[source_pattern].map do |path| - if File.mtime(path) > touched_since - test = "#{test_path}/#{File.basename(path, '.rb')}_test.rb" - test if File.exists?(test) - end - end.compact -end - -desc 'Test recent changes.' -Rake::TestTask.new(:recent => [ :clone_structure_to_test ]) do |t| - since = TEST_CHANGES_SINCE - touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } + - recent_tests('app/models/*.rb', 'test/unit', since) + - recent_tests('app/controllers/*.rb', 'test/functional', since) - - t.libs << 'test' - t.verbose = true - t.test_files = touched.uniq -end -task :test_recent => [ :clone_structure_to_test ] - -desc "Run the unit tests in test/unit" -Rake::TestTask.new("test_units") { |t| - t.libs << "test" - t.pattern = 'test/unit/**/*_test.rb' - t.verbose = true -} -task :test_units => [ :clone_structure_to_test ] - -desc "Run the functional tests in test/functional" -Rake::TestTask.new("test_functional") { |t| - t.libs << "test" - t.pattern = 'test/functional/**/*_test.rb' - t.verbose = true -} -task :test_functional => [ :clone_structure_to_test ] - -desc "Generate documentation for the application" -Rake::RDocTask.new("appdoc") { |rdoc| - rdoc.rdoc_dir = 'doc/app' - rdoc.title = "Rails Application Documentation" - rdoc.options << '--line-numbers --inline-source' - rdoc.rdoc_files.include('doc/README_FOR_APP') - rdoc.rdoc_files.include('app/**/*.rb') -} - -desc "Generate documentation for the Rails framework" -Rake::RDocTask.new("apidoc") { |rdoc| - rdoc.rdoc_dir = 'doc/api' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] - rdoc.title = "Rails Framework Documentation" - rdoc.options << '--line-numbers --inline-source' - rdoc.rdoc_files.include('README') - rdoc.rdoc_files.include('CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE') - rdoc.rdoc_files.include('vendor/rails/activerecord/README') - rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb') - rdoc.rdoc_files.exclude('vendor/rails/activerecord/lib/active_record/vendor/*') - rdoc.rdoc_files.include('vendor/rails/actionpack/README') - rdoc.rdoc_files.include('vendor/rails/actionpack/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_controller/**/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_view/**/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionmailer/README') - rdoc.rdoc_files.include('vendor/rails/actionmailer/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/actionmailer/lib/action_mailer/base.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/README') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/api/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/client/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/container/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/dispatcher/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/protocol/*.rb') - rdoc.rdoc_files.include('vendor/rails/actionwebservice/lib/action_web_service/support/*.rb') - rdoc.rdoc_files.include('vendor/rails/activesupport/README') - rdoc.rdoc_files.include('vendor/rails/activesupport/CHANGELOG') - rdoc.rdoc_files.include('vendor/rails/activesupport/lib/active_support/**/*.rb') -} - -desc "Report code statistics (KLOCs, etc) from the application" -task :stats => [ :environment ] do - require 'code_statistics' - CodeStatistics.new( - ["Helpers", "app/helpers"], - ["Controllers", "app/controllers"], - ["APIs", "app/apis"], - ["Components", "components"], - ["Functionals", "test/functional"], - ["Models", "app/models"], - ["Units", "test/unit"] - ).to_s -end - -desc "Recreate the test databases from the development structure" -task :clone_structure_to_test => [ :db_structure_dump, :purge_test_database ] do - abcs = ActiveRecord::Base.configurations - case abcs["test"]["adapter"] - when "mysql" - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| - ActiveRecord::Base.connection.execute(table) - end - when "postgresql" - ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] - ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] - ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] - `psql -U "#{abcs["test"]["username"]}" -f db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` - when "sqlite", "sqlite3" - `#{abcs[RAILS_ENV]["adapter"]} #{abcs["test"]["dbfile"]} < db/#{RAILS_ENV}_structure.sql` - when "sqlserver" - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - else - raise "Unknown database adapter '#{abcs["test"]["adapter"]}'" - end -end - -desc "Dump the database structure to a SQL file" -task :db_structure_dump => :environment do - abcs = ActiveRecord::Base.configurations - case abcs[RAILS_ENV]["adapter"] - when "mysql" - ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) - File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } - when "postgresql" - ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"] - ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"] - ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"] - `pg_dump -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{abcs[RAILS_ENV]["database"]}` - when "sqlite", "sqlite3" - `#{abcs[RAILS_ENV]["adapter"]} #{abcs[RAILS_ENV]["dbfile"]} .schema > db/#{RAILS_ENV}_structure.sql` - when "sqlserver" - `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` - `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` - else - raise "Unknown database adapter '#{abcs["test"]["adapter"]}'" - end -end - -desc "Empty the test database" -task :purge_test_database => :environment do - abcs = ActiveRecord::Base.configurations - case abcs["test"]["adapter"] - when "mysql" - ActiveRecord::Base.establish_connection(:test) - ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"]) - when "postgresql" - ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] - ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] - ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] - `dropdb -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}` - `createdb -T template0 -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}` - when "sqlite","sqlite3" - File.delete(abcs["test"]["dbfile"]) if File.exist?(abcs["test"]["dbfile"]) - when "sqlserver" - dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - else - raise "Unknown database adapter '#{abcs["test"]["adapter"]}'" - end -end - -desc "Clears all *.log files in log/" -task :clear_logs => :environment do - FileList["log/*.log"].each do |log_file| - f = File.open(log_file, "w") - f.close - end -end - -desc "Migrate the database according to the migrate scripts in db/migrate (only supported on PG/MySQL). A specific version can be targetted with VERSION=x" -task :migrate => :environment do - ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/db/migrate/', ENV["VERSION"] ? ENV["VERSION"].to_i : nil) -end \ No newline at end of file diff --git a/app/models/page_observer.rb b/app/models/page_observer.rb new file mode 100644 index 00000000..2ff58b64 --- /dev/null +++ b/app/models/page_observer.rb @@ -0,0 +1,15 @@ +# This class maintains the state of wiki references for newly created or newly deleted pages +class PageObserver < ActiveRecord::Observer + + def after_create(page) + WikiReference.update_all("link_type = '#{WikiReference::LINKED_PAGE}'", + ['referenced_page_name = ?', page.name]) + end + + def before_destroy(page) + WikiReference.delete_all ['page_id = ?', page.id] + WikiReference.update_all("link_type = '#{WikiReference::WANTED_PAGE}'", + ['referenced_page_name = ?', page.name]) + end + +end \ No newline at end of file diff --git a/app/models/wiki_reference.rb b/app/models/wiki_reference.rb new file mode 100644 index 00000000..074096bd --- /dev/null +++ b/app/models/wiki_reference.rb @@ -0,0 +1,50 @@ +class WikiReference < ActiveRecord::Base + + LINKED_PAGE = 'L' + WANTED_PAGE = 'W' + INCLUDED_PAGE = 'I' + + belongs_to :page + validates_inclusion_of :link_type, :in => [LINKED_PAGE, WANTED_PAGE, INCLUDED_PAGE] + + def self.link_type(web, page_name) + web.has_page?(page_name) ? LINKED_PAGE : WANTED_PAGE + end + + def self.pages_that_reference(page_name) + query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' + + 'WHERE wiki_references.referenced_page_name = ?' + names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] } + end + + def self.pages_that_link_to(page_name) + query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' + + 'WHERE wiki_references.referenced_page_name = ? ' + + "AND wiki_references.link_type in ('#{LINKED_PAGE}', '#{WANTED_PAGE}')" + names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] } + end + + def self.pages_that_include(page_name) + query = 'SELECT name FROM pages JOIN wiki_references ON pages.id = wiki_references.page_id ' + + 'WHERE wiki_references.referenced_page_name = ? ' + + "AND wiki_references.link_type = '#{INCLUDED_PAGE}'" + names = connection.select_all(sanitize_sql([query, page_name])).map { |row| row['name'] } + end + + def wiki_link? + linked_page? or wanted_page? + end + + def linked_page? + link_type == LINKED_PAGE + end + + def wanted_page? + link_type == WANTED_PAGE + end + + def included_page? + link_type == INCLUDED_PAGE + end + +end diff --git a/db/wiki_references.erbsql b/db/wiki_references.erbsql new file mode 100644 index 00000000..e4485b0c --- /dev/null +++ b/db/wiki_references.erbsql @@ -0,0 +1,9 @@ +CREATE TABLE wiki_references ( + id <%= @pk %>, + created_at <%= @datetime %> NOT NULL, + updated_at <%= @datetime %> NOT NULL, + + page_id INTEGER NOT NULL, + referenced_page_name VARCHAR NOT NULL, + link_type CHAR(1) NOT NULL +) <%= create_options %>; \ No newline at end of file diff --git a/lib/page_renderer.rb b/lib/page_renderer.rb index 61428bd2..b4a3877b 100644 --- a/lib/page_renderer.rb +++ b/lib/page_renderer.rb @@ -89,8 +89,9 @@ class PageRenderer def render(options = {}) result = WikiContent.new(@revision, @@url_generator, options).render! WikiReference.delete_all ['page_id = ?', @revision.page_id] - @revision.page.wiki_references.delete - + + references = @revision.page.wiki_references + wiki_word_chunks = result.find_chunks(WikiChunk::WikiLink) wiki_words = wiki_word_chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq @@ -101,10 +102,14 @@ class PageRenderer else link_type = WikiReference.link_type(@revision.page.web, referenced_page_name) end - @revision.page.wiki_references.create({ - :referenced_page_name => referenced_page_name, - :link_type => link_type - }) + references.create :referenced_page_name => referenced_page_name, :link_type => link_type + end + + include_chunks = result.find_chunks(Include) + includes = include_chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq + includes.each do |included_page_name| + references.create :referenced_page_name => included_page_name, + :link_type => WikiReference::INCLUDED_PAGE end result end diff --git a/script/reset_references b/script/reset_references new file mode 100644 index 00000000..0e21b0c9 --- /dev/null +++ b/script/reset_references @@ -0,0 +1,25 @@ +ENV['RAILS_ENV'] = ARGV.first || 'development' + +$stderr.puts "Loading Rails for #{ENV['RAILS_ENV']} environment..." +require File.dirname(__FILE__) + '/../config/environment' + +class StubUrlGenerator + def make_link(*args) + 'StubLink' + end +end + +PageRenderer.setup_url_generator(StubUrlGenerator.new) +WikiReference.delete_all + +Web.find_all.each do |web| + web.pages.find(:all, :order => 'name').each do |page| + $stderr.puts "Processing page '#{page.name}'" + begin + PageRenderer.new(page.current_revision).display_content + rescue => e + puts e + puts e.backtrace + end + end +end \ No newline at end of file diff --git a/test/fixtures/wiki_references.yml b/test/fixtures/wiki_references.yml new file mode 100644 index 00000000..136f6aaa --- /dev/null +++ b/test/fixtures/wiki_references.yml @@ -0,0 +1,95 @@ +my_way_1: + id: 1 + page_id: 2 + referenced_page_name: MyWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +smart_engine_1: + id: 2 + page_id: 3 + referenced_page_name: SmartEngine + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +that_way_1: + id: 3 + page_id: 4 + referenced_page_name: ThatWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +home_page_1: + id: 4 + page_id: 1 + referenced_page_name: HisWay + link_type: W + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +home_page_2: + id: 5 + page_id: 1 + referenced_page_name: MyWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +home_page_3: + id: 6 + page_id: 1 + referenced_page_name: ThatWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +home_page_4: + id: 7 + page_id: 1 + referenced_page_name: SmartEngine + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +first_page_1: + id: 8 + page_id: 6 + referenced_page_name: HisWay + link_type: W + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +first_page_2: + id: 9 + page_id: 6 + referenced_page_name: MyWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +first_page_3: + id: 10 + page_id: 6 + referenced_page_name: ThatWay + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +first_page_4: + id: 11 + page_id: 6 + referenced_page_name: OverThere + link_type: W + created_at: <%= Time.now %> + updated_at: <%= Time.now %> + +first_page_5: + id: 12 + page_id: 6 + referenced_page_name: SmartEngine + link_type: L + created_at: <%= Time.now %> + updated_at: <%= Time.now %> diff --git a/test/unit/page_renderer_test.rb b/test/unit/page_renderer_test.rb index 32c172db..1288ea85 100644 --- a/test/unit/page_renderer_test.rb +++ b/test/unit/page_renderer_test.rb @@ -326,8 +326,7 @@ class PageRendererTest < Test::Unit::TestCase end - # Tests for the caching of wiki references and categories - def test_references_creation + def test_references_creation_links new_page = @web.add_page('NewPage', 'HomePage NewPage', Time.local(2004, 4, 4, 16, 50), 'AlexeyVerkhovsky', test_renderer) @@ -339,6 +338,16 @@ class PageRendererTest < Test::Unit::TestCase assert_equal WikiReference::LINKED_PAGE, references[1].link_type end + def test_references_creation_includes + new_page = @web.add_page('NewPage', '[[!include IncludedPage]]', + Time.local(2004, 4, 4, 16, 50), 'AlexeyVerkhovsky', test_renderer) + + references = new_page.wiki_references(true) + assert_equal 1, references.size + assert_equal 'IncludedPage', references[0].referenced_page_name + assert_equal WikiReference::INCLUDED_PAGE, references[0].link_type + end + private def add_sample_pages