From 1da0386bf9e1ca3fbd0d9d3ae69cdc7a8cdc26fa Mon Sep 17 00:00:00 2001 From: Cuong Tran Date: Wed, 27 Feb 2008 12:06:49 -0600 Subject: [PATCH] first import from http://repo.pragprog.com/svn/Public/plugins/annotate_models --- History.txt | 4 + License.txt | 20 + Manifest.txt | 24 + README.txt | 32 + Rakefile | 4 + config/hoe.rb | 70 + config/requirements.rb | 17 + lib/annotate_models.rb | 126 ++ lib/annotate_models/version.rb | 9 + log/debug.log | 0 pkg/annotate_models-0.0.1.gem | Bin 0 -> 27648 bytes pkg/annotate_models-0.0.1.tgz | Bin 0 -> 25043 bytes pkg/annotate_models-0.0.1/History.txt | 4 + pkg/annotate_models-0.0.1/License.txt | 20 + pkg/annotate_models-0.0.1/Manifest.txt | 24 + pkg/annotate_models-0.0.1/README.txt | 32 + pkg/annotate_models-0.0.1/Rakefile | 4 + pkg/annotate_models-0.0.1/config/hoe.rb | 70 + .../config/requirements.rb | 17 + .../lib/annotate_models.rb | 126 ++ .../lib/annotate_models/version.rb | 9 + pkg/annotate_models-0.0.1/log/debug.log | 0 pkg/annotate_models-0.0.1/script/destroy | 14 + pkg/annotate_models-0.0.1/script/generate | 14 + pkg/annotate_models-0.0.1/script/txt2html | 74 + pkg/annotate_models-0.0.1/setup.rb | 1585 +++++++++++++++++ .../tasks/deployment.rake | 34 + .../tasks/environment.rake | 7 + pkg/annotate_models-0.0.1/tasks/website.rake | 17 + .../test/test_annotate_models.rb | 11 + pkg/annotate_models-0.0.1/test/test_helper.rb | 2 + pkg/annotate_models-0.0.1/website/index.html | 93 + pkg/annotate_models-0.0.1/website/index.txt | 39 + .../javascripts/rounded_corners_lite.inc.js | 285 +++ .../website/stylesheets/screen.css | 138 ++ .../website/template.rhtml | 48 + script/destroy | 14 + script/generate | 14 + script/txt2html | 74 + setup.rb | 1585 +++++++++++++++++ tasks/deployment.rake | 34 + tasks/environment.rake | 7 + tasks/website.rake | 17 + test/test_annotate_models.rb | 11 + test/test_helper.rb | 2 + website/index.html | 93 + website/index.txt | 39 + .../javascripts/rounded_corners_lite.inc.js | 285 +++ website/stylesheets/screen.css | 138 ++ website/template.rhtml | 48 + 50 files changed, 5334 insertions(+) create mode 100644 History.txt create mode 100644 License.txt create mode 100644 Manifest.txt create mode 100644 README.txt create mode 100644 Rakefile create mode 100644 config/hoe.rb create mode 100644 config/requirements.rb create mode 100644 lib/annotate_models.rb create mode 100644 lib/annotate_models/version.rb create mode 100644 log/debug.log create mode 100644 pkg/annotate_models-0.0.1.gem create mode 100644 pkg/annotate_models-0.0.1.tgz create mode 100644 pkg/annotate_models-0.0.1/History.txt create mode 100644 pkg/annotate_models-0.0.1/License.txt create mode 100644 pkg/annotate_models-0.0.1/Manifest.txt create mode 100644 pkg/annotate_models-0.0.1/README.txt create mode 100644 pkg/annotate_models-0.0.1/Rakefile create mode 100644 pkg/annotate_models-0.0.1/config/hoe.rb create mode 100644 pkg/annotate_models-0.0.1/config/requirements.rb create mode 100644 pkg/annotate_models-0.0.1/lib/annotate_models.rb create mode 100644 pkg/annotate_models-0.0.1/lib/annotate_models/version.rb create mode 100644 pkg/annotate_models-0.0.1/log/debug.log create mode 100755 pkg/annotate_models-0.0.1/script/destroy create mode 100755 pkg/annotate_models-0.0.1/script/generate create mode 100755 pkg/annotate_models-0.0.1/script/txt2html create mode 100644 pkg/annotate_models-0.0.1/setup.rb create mode 100644 pkg/annotate_models-0.0.1/tasks/deployment.rake create mode 100644 pkg/annotate_models-0.0.1/tasks/environment.rake create mode 100644 pkg/annotate_models-0.0.1/tasks/website.rake create mode 100644 pkg/annotate_models-0.0.1/test/test_annotate_models.rb create mode 100644 pkg/annotate_models-0.0.1/test/test_helper.rb create mode 100644 pkg/annotate_models-0.0.1/website/index.html create mode 100644 pkg/annotate_models-0.0.1/website/index.txt create mode 100644 pkg/annotate_models-0.0.1/website/javascripts/rounded_corners_lite.inc.js create mode 100644 pkg/annotate_models-0.0.1/website/stylesheets/screen.css create mode 100644 pkg/annotate_models-0.0.1/website/template.rhtml create mode 100755 script/destroy create mode 100755 script/generate create mode 100755 script/txt2html create mode 100644 setup.rb create mode 100644 tasks/deployment.rake create mode 100644 tasks/environment.rake create mode 100644 tasks/website.rake create mode 100644 test/test_annotate_models.rb create mode 100644 test/test_helper.rb create mode 100644 website/index.html create mode 100644 website/index.txt create mode 100644 website/javascripts/rounded_corners_lite.inc.js create mode 100644 website/stylesheets/screen.css create mode 100644 website/template.rhtml diff --git a/History.txt b/History.txt new file mode 100644 index 000000000..829aaa051 --- /dev/null +++ b/History.txt @@ -0,0 +1,4 @@ +== 0.0.1 2008-02-27 + +* 1 major enhancement: + * Initial release diff --git a/License.txt b/License.txt new file mode 100644 index 000000000..10aab6490 --- /dev/null +++ b/License.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008 FIXME full name + +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. \ No newline at end of file diff --git a/Manifest.txt b/Manifest.txt new file mode 100644 index 000000000..c93acc96f --- /dev/null +++ b/Manifest.txt @@ -0,0 +1,24 @@ +History.txt +License.txt +Manifest.txt +README.txt +Rakefile +config/hoe.rb +config/requirements.rb +lib/annotate_models.rb +lib/annotate_models/version.rb +log/debug.log +script/destroy +script/generate +script/txt2html +setup.rb +tasks/deployment.rake +tasks/environment.rake +tasks/website.rake +test/test_annotate_models.rb +test/test_helper.rb +website/index.html +website/index.txt +website/javascripts/rounded_corners_lite.inc.js +website/stylesheets/screen.css +website/template.rhtml diff --git a/README.txt b/README.txt new file mode 100644 index 000000000..9eed7154d --- /dev/null +++ b/README.txt @@ -0,0 +1,32 @@ +AnnotateModels +============== + +Add a comment summarizing the current schema to the top of each ActiveRecord model source file. + + # Schema as of Sun Feb 26 21:58:32 CST 2006 (schema version 7) + # + # id :integer(11) not null + # quantity :integer(11) + # product_id :integer(11) + # unit_price :float + # order_id :integer(11) + # + + class LineItem < ActiveRecord::Base + belongs_to :product + + end + +Note that this code will blow away the initial comment block in your models if it looks like it was +previously added by annotate models, so you don't want to add additional text to an automatically +created comment block. + +Author: + Dave Thomas + Pragmatic Programmers, LLC + +Released under the same license as Ruby. No Support. No Warranty. + +Modifications by: + - alex@pivotallabs.com + - ctran@pragmaquest.com diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..e469154c2 --- /dev/null +++ b/Rakefile @@ -0,0 +1,4 @@ +require 'config/requirements' +require 'config/hoe' # setup Hoe + all gem configuration + +Dir['tasks/**/*.rake'].each { |rake| load rake } \ No newline at end of file diff --git a/config/hoe.rb b/config/hoe.rb new file mode 100644 index 000000000..53c8ee35b --- /dev/null +++ b/config/hoe.rb @@ -0,0 +1,70 @@ +require 'annotate_models/version' + +AUTHOR = 'Dave Thomas' # can also be an array of Authors +EMAIL = "ctran@pragmaquest.com" +DESCRIPTION = "Add a comment summarizing the current schema to the top of each ActiveRecord model source file" +GEM_NAME = 'annotate_models' # what ppl will type to install your gem +RUBYFORGE_PROJECT = 'annotate-models' # The unix name for your project +HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org" +DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}" + +@config_file = "~/.rubyforge/user-config.yml" +@config = nil +RUBYFORGE_USERNAME = "unknown" +def rubyforge_username + unless @config + begin + @config = YAML.load(File.read(File.expand_path(@config_file))) + rescue + puts <<-EOS +ERROR: No rubyforge config file found: #{@config_file} +Run 'rubyforge setup' to prepare your env for access to Rubyforge + - See http://newgem.rubyforge.org/rubyforge.html for more details + EOS + exit + end + end + RUBYFORGE_USERNAME.replace @config["username"] +end + + +REV = nil +# UNCOMMENT IF REQUIRED: +# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil +VERS = AnnotateModels::VERSION::STRING + (REV ? ".#{REV}" : "") +RDOC_OPTS = ['--quiet', '--title', 'annotate_models documentation', + "--opname", "index.html", + "--line-numbers", + "--main", "README", + "--inline-source"] + +class Hoe + def extra_deps + @extra_deps.reject! { |x| Array(x).first == 'hoe' } + @extra_deps + end +end + +# Generate all the Rake tasks +# Run 'rake -T' to see list of generated tasks (from gem root directory) +hoe = Hoe.new(GEM_NAME, VERS) do |p| + p.developer(AUTHOR, EMAIL) + p.description = DESCRIPTION + p.summary = DESCRIPTION + p.url = HOMEPATH + p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT + p.test_globs = ["test/**/test_*.rb"] + p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean. + + # == Optional + p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n") + #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ] + + #p.spec_extras = {} # A hash of extra values to set in the gemspec. + +end + +CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n") +PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}" +hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc') +hoe.rsync_args = '-av --delete --ignore-errors' \ No newline at end of file diff --git a/config/requirements.rb b/config/requirements.rb new file mode 100644 index 000000000..5430ea626 --- /dev/null +++ b/config/requirements.rb @@ -0,0 +1,17 @@ +require 'fileutils' +include FileUtils + +require 'rubygems' +%w[rake hoe newgem rubigen].each do |req_gem| + begin + require req_gem + rescue LoadError + puts "This Rakefile requires the '#{req_gem}' RubyGem." + puts "Installation: gem install #{req_gem} -y" + exit + end +end + +$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib])) + +require 'annotate_models' \ No newline at end of file diff --git a/lib/annotate_models.rb b/lib/annotate_models.rb new file mode 100644 index 000000000..3495e5ecf --- /dev/null +++ b/lib/annotate_models.rb @@ -0,0 +1,126 @@ +$:.unshift File.dirname(__FILE__) + +module AnnotateModels + RAILS_ROOT = '.' + MODEL_DIR = File.join(RAILS_ROOT, "app/models") + FIXTURE_DIR = File.join(RAILS_ROOT, "test/fixtures") + PREFIX = "== Schema Information" + + # Simple quoting for the default column value + def self.quote(value) + case value + when NilClass then "NULL" + when TrueClass then "TRUE" + when FalseClass then "FALSE" + when Float, Fixnum, Bignum then value.to_s + # BigDecimals need to be output in a non-normalized form and quoted. + when BigDecimal then value.to_s('F') + else + value.inspect + end + end + + # Use the column information in an ActiveRecord class + # to create a comment block containing a line for + # each column. The line contains the column name, + # the type (and length), and any optional attributes + def self.get_schema_info(klass, header) + info = "# #{header}\n#\n" + info << "# Table name: #{klass.table_name}\n#\n" + + max_size = klass.column_names.collect{|name| name.size}.max + 1 + klass.columns.each do |col| + attrs = [] + attrs << "default(#{quote(col.default)})" if col.default + attrs << "not null" unless col.null + attrs << "primary key" if col.name == klass.primary_key + + col_type = col.type.to_s + if col_type == "decimal" + col_type << "(#{col.precision}, #{col.scale})" + else + col_type << "(#{col.limit})" if col.limit + end + info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s\n", col.name, col_type, attrs.join(", ")) + end + + info << "#\n\n" + end + + # Add a schema block to a file. If the file already contains + # a schema info block (a comment starting + # with "Schema as of ..."), remove it first. + + def self.annotate_one_file(file_name, info_block) + if File.exist?(file_name) + content = File.read(file_name) + + # Remove old schema info + content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '') + + # Write it back + File.open(file_name, "w") { |f| f.puts info_block + content } + end + end + + # Given the name of an ActiveRecord class, create a schema + # info block (basically a comment containing information + # on the columns and their types) and put it at the front + # of the model and fixture source files. + + def self.annotate(klass, header) + info = get_schema_info(klass, header) + + model_file_name = File.join(MODEL_DIR, klass.name.underscore + ".rb") + annotate_one_file(model_file_name, info) + + fixture_file_name = File.join(FIXTURE_DIR, klass.table_name + ".yml") + annotate_one_file(fixture_file_name, info) + end + + # Return a list of the model files to annotate. If we have + # command line arguments, they're assumed to be either + # the underscore or CamelCase versions of model names. + # Otherwise we take all the model files in the + # app/models directory. + def self.get_model_names + models = ARGV.dup + models.shift + + if models.empty? + Dir.chdir(MODEL_DIR) do + models = Dir["**/*.rb"] + end + end + models + end + + # We're passed a name of things that might be + # ActiveRecord models. If we can find the class, and + # if its a subclass of ActiveRecord::Base, + # then pas it to the associated block + + def self.do_annotations + header = PREFIX.dup + version = ActiveRecord::Migrator.current_version rescue 0 + if version > 0 + header << "\n# Schema version: #{version}" + end + + annotated = [] + self.get_model_names.each do |m| + class_name = m.sub(/\.rb$/,'').camelize + begin + klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) } + if klass < ActiveRecord::Base && !klass.abstract_class? + annotated << class_name + self.annotate(klass, header) + end + rescue Exception => e + puts "Unable to annotate #{class_name}: #{e.message}" + end + + end + puts "Annotated #{annotated.join(', ')}" + end +end \ No newline at end of file diff --git a/lib/annotate_models/version.rb b/lib/annotate_models/version.rb new file mode 100644 index 000000000..a9452fba1 --- /dev/null +++ b/lib/annotate_models/version.rb @@ -0,0 +1,9 @@ +module AnnotateModels #:nodoc: + module VERSION #:nodoc: + MAJOR = 0 + MINOR = 0 + TINY = 1 + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/log/debug.log b/log/debug.log new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/annotate_models-0.0.1.gem b/pkg/annotate_models-0.0.1.gem new file mode 100644 index 0000000000000000000000000000000000000000..df60cb932ebbece16c05de9f4c8f530be4878c56 GIT binary patch literal 27648 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 CZR&*p literal 0 HcmV?d00001 diff --git a/pkg/annotate_models-0.0.1.tgz b/pkg/annotate_models-0.0.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8f56112b8ec384c449e2a1ec54aa2e0e642813d7 GIT binary patch literal 25043 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 literal 0 HcmV?d00001 diff --git a/pkg/annotate_models-0.0.1/History.txt b/pkg/annotate_models-0.0.1/History.txt new file mode 100644 index 000000000..829aaa051 --- /dev/null +++ b/pkg/annotate_models-0.0.1/History.txt @@ -0,0 +1,4 @@ +== 0.0.1 2008-02-27 + +* 1 major enhancement: + * Initial release diff --git a/pkg/annotate_models-0.0.1/License.txt b/pkg/annotate_models-0.0.1/License.txt new file mode 100644 index 000000000..10aab6490 --- /dev/null +++ b/pkg/annotate_models-0.0.1/License.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008 FIXME full name + +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. \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/Manifest.txt b/pkg/annotate_models-0.0.1/Manifest.txt new file mode 100644 index 000000000..c93acc96f --- /dev/null +++ b/pkg/annotate_models-0.0.1/Manifest.txt @@ -0,0 +1,24 @@ +History.txt +License.txt +Manifest.txt +README.txt +Rakefile +config/hoe.rb +config/requirements.rb +lib/annotate_models.rb +lib/annotate_models/version.rb +log/debug.log +script/destroy +script/generate +script/txt2html +setup.rb +tasks/deployment.rake +tasks/environment.rake +tasks/website.rake +test/test_annotate_models.rb +test/test_helper.rb +website/index.html +website/index.txt +website/javascripts/rounded_corners_lite.inc.js +website/stylesheets/screen.css +website/template.rhtml diff --git a/pkg/annotate_models-0.0.1/README.txt b/pkg/annotate_models-0.0.1/README.txt new file mode 100644 index 000000000..9eed7154d --- /dev/null +++ b/pkg/annotate_models-0.0.1/README.txt @@ -0,0 +1,32 @@ +AnnotateModels +============== + +Add a comment summarizing the current schema to the top of each ActiveRecord model source file. + + # Schema as of Sun Feb 26 21:58:32 CST 2006 (schema version 7) + # + # id :integer(11) not null + # quantity :integer(11) + # product_id :integer(11) + # unit_price :float + # order_id :integer(11) + # + + class LineItem < ActiveRecord::Base + belongs_to :product + + end + +Note that this code will blow away the initial comment block in your models if it looks like it was +previously added by annotate models, so you don't want to add additional text to an automatically +created comment block. + +Author: + Dave Thomas + Pragmatic Programmers, LLC + +Released under the same license as Ruby. No Support. No Warranty. + +Modifications by: + - alex@pivotallabs.com + - ctran@pragmaquest.com diff --git a/pkg/annotate_models-0.0.1/Rakefile b/pkg/annotate_models-0.0.1/Rakefile new file mode 100644 index 000000000..e469154c2 --- /dev/null +++ b/pkg/annotate_models-0.0.1/Rakefile @@ -0,0 +1,4 @@ +require 'config/requirements' +require 'config/hoe' # setup Hoe + all gem configuration + +Dir['tasks/**/*.rake'].each { |rake| load rake } \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/config/hoe.rb b/pkg/annotate_models-0.0.1/config/hoe.rb new file mode 100644 index 000000000..53c8ee35b --- /dev/null +++ b/pkg/annotate_models-0.0.1/config/hoe.rb @@ -0,0 +1,70 @@ +require 'annotate_models/version' + +AUTHOR = 'Dave Thomas' # can also be an array of Authors +EMAIL = "ctran@pragmaquest.com" +DESCRIPTION = "Add a comment summarizing the current schema to the top of each ActiveRecord model source file" +GEM_NAME = 'annotate_models' # what ppl will type to install your gem +RUBYFORGE_PROJECT = 'annotate-models' # The unix name for your project +HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org" +DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}" + +@config_file = "~/.rubyforge/user-config.yml" +@config = nil +RUBYFORGE_USERNAME = "unknown" +def rubyforge_username + unless @config + begin + @config = YAML.load(File.read(File.expand_path(@config_file))) + rescue + puts <<-EOS +ERROR: No rubyforge config file found: #{@config_file} +Run 'rubyforge setup' to prepare your env for access to Rubyforge + - See http://newgem.rubyforge.org/rubyforge.html for more details + EOS + exit + end + end + RUBYFORGE_USERNAME.replace @config["username"] +end + + +REV = nil +# UNCOMMENT IF REQUIRED: +# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil +VERS = AnnotateModels::VERSION::STRING + (REV ? ".#{REV}" : "") +RDOC_OPTS = ['--quiet', '--title', 'annotate_models documentation', + "--opname", "index.html", + "--line-numbers", + "--main", "README", + "--inline-source"] + +class Hoe + def extra_deps + @extra_deps.reject! { |x| Array(x).first == 'hoe' } + @extra_deps + end +end + +# Generate all the Rake tasks +# Run 'rake -T' to see list of generated tasks (from gem root directory) +hoe = Hoe.new(GEM_NAME, VERS) do |p| + p.developer(AUTHOR, EMAIL) + p.description = DESCRIPTION + p.summary = DESCRIPTION + p.url = HOMEPATH + p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT + p.test_globs = ["test/**/test_*.rb"] + p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean. + + # == Optional + p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n") + #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ] + + #p.spec_extras = {} # A hash of extra values to set in the gemspec. + +end + +CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n") +PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}" +hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc') +hoe.rsync_args = '-av --delete --ignore-errors' \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/config/requirements.rb b/pkg/annotate_models-0.0.1/config/requirements.rb new file mode 100644 index 000000000..5430ea626 --- /dev/null +++ b/pkg/annotate_models-0.0.1/config/requirements.rb @@ -0,0 +1,17 @@ +require 'fileutils' +include FileUtils + +require 'rubygems' +%w[rake hoe newgem rubigen].each do |req_gem| + begin + require req_gem + rescue LoadError + puts "This Rakefile requires the '#{req_gem}' RubyGem." + puts "Installation: gem install #{req_gem} -y" + exit + end +end + +$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib])) + +require 'annotate_models' \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/lib/annotate_models.rb b/pkg/annotate_models-0.0.1/lib/annotate_models.rb new file mode 100644 index 000000000..3495e5ecf --- /dev/null +++ b/pkg/annotate_models-0.0.1/lib/annotate_models.rb @@ -0,0 +1,126 @@ +$:.unshift File.dirname(__FILE__) + +module AnnotateModels + RAILS_ROOT = '.' + MODEL_DIR = File.join(RAILS_ROOT, "app/models") + FIXTURE_DIR = File.join(RAILS_ROOT, "test/fixtures") + PREFIX = "== Schema Information" + + # Simple quoting for the default column value + def self.quote(value) + case value + when NilClass then "NULL" + when TrueClass then "TRUE" + when FalseClass then "FALSE" + when Float, Fixnum, Bignum then value.to_s + # BigDecimals need to be output in a non-normalized form and quoted. + when BigDecimal then value.to_s('F') + else + value.inspect + end + end + + # Use the column information in an ActiveRecord class + # to create a comment block containing a line for + # each column. The line contains the column name, + # the type (and length), and any optional attributes + def self.get_schema_info(klass, header) + info = "# #{header}\n#\n" + info << "# Table name: #{klass.table_name}\n#\n" + + max_size = klass.column_names.collect{|name| name.size}.max + 1 + klass.columns.each do |col| + attrs = [] + attrs << "default(#{quote(col.default)})" if col.default + attrs << "not null" unless col.null + attrs << "primary key" if col.name == klass.primary_key + + col_type = col.type.to_s + if col_type == "decimal" + col_type << "(#{col.precision}, #{col.scale})" + else + col_type << "(#{col.limit})" if col.limit + end + info << sprintf("# %-#{max_size}.#{max_size}s:%-15.15s %s\n", col.name, col_type, attrs.join(", ")) + end + + info << "#\n\n" + end + + # Add a schema block to a file. If the file already contains + # a schema info block (a comment starting + # with "Schema as of ..."), remove it first. + + def self.annotate_one_file(file_name, info_block) + if File.exist?(file_name) + content = File.read(file_name) + + # Remove old schema info + content.sub!(/^# #{PREFIX}.*?\n(#.*\n)*\n/, '') + + # Write it back + File.open(file_name, "w") { |f| f.puts info_block + content } + end + end + + # Given the name of an ActiveRecord class, create a schema + # info block (basically a comment containing information + # on the columns and their types) and put it at the front + # of the model and fixture source files. + + def self.annotate(klass, header) + info = get_schema_info(klass, header) + + model_file_name = File.join(MODEL_DIR, klass.name.underscore + ".rb") + annotate_one_file(model_file_name, info) + + fixture_file_name = File.join(FIXTURE_DIR, klass.table_name + ".yml") + annotate_one_file(fixture_file_name, info) + end + + # Return a list of the model files to annotate. If we have + # command line arguments, they're assumed to be either + # the underscore or CamelCase versions of model names. + # Otherwise we take all the model files in the + # app/models directory. + def self.get_model_names + models = ARGV.dup + models.shift + + if models.empty? + Dir.chdir(MODEL_DIR) do + models = Dir["**/*.rb"] + end + end + models + end + + # We're passed a name of things that might be + # ActiveRecord models. If we can find the class, and + # if its a subclass of ActiveRecord::Base, + # then pas it to the associated block + + def self.do_annotations + header = PREFIX.dup + version = ActiveRecord::Migrator.current_version rescue 0 + if version > 0 + header << "\n# Schema version: #{version}" + end + + annotated = [] + self.get_model_names.each do |m| + class_name = m.sub(/\.rb$/,'').camelize + begin + klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) } + if klass < ActiveRecord::Base && !klass.abstract_class? + annotated << class_name + self.annotate(klass, header) + end + rescue Exception => e + puts "Unable to annotate #{class_name}: #{e.message}" + end + + end + puts "Annotated #{annotated.join(', ')}" + end +end \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/lib/annotate_models/version.rb b/pkg/annotate_models-0.0.1/lib/annotate_models/version.rb new file mode 100644 index 000000000..a9452fba1 --- /dev/null +++ b/pkg/annotate_models-0.0.1/lib/annotate_models/version.rb @@ -0,0 +1,9 @@ +module AnnotateModels #:nodoc: + module VERSION #:nodoc: + MAJOR = 0 + MINOR = 0 + TINY = 1 + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/pkg/annotate_models-0.0.1/log/debug.log b/pkg/annotate_models-0.0.1/log/debug.log new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/annotate_models-0.0.1/script/destroy b/pkg/annotate_models-0.0.1/script/destroy new file mode 100755 index 000000000..e48464df5 --- /dev/null +++ b/pkg/annotate_models-0.0.1/script/destroy @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] +RubiGen::Scripts::Destroy.new.run(ARGV) diff --git a/pkg/annotate_models-0.0.1/script/generate b/pkg/annotate_models-0.0.1/script/generate new file mode 100755 index 000000000..c27f65593 --- /dev/null +++ b/pkg/annotate_models-0.0.1/script/generate @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] +RubiGen::Scripts::Generate.new.run(ARGV) diff --git a/pkg/annotate_models-0.0.1/script/txt2html b/pkg/annotate_models-0.0.1/script/txt2html new file mode 100755 index 000000000..b9200f8a0 --- /dev/null +++ b/pkg/annotate_models-0.0.1/script/txt2html @@ -0,0 +1,74 @@ +#!/usr/bin/env ruby + +require 'rubygems' +begin + require 'newgem' +rescue LoadError + puts "\n\nGenerating the website requires the newgem RubyGem" + puts "Install: gem install newgem\n\n" + exit(1) +end +require 'redcloth' +require 'syntax/convertors/html' +require 'erb' +require File.dirname(__FILE__) + '/../lib/annotate_models/version.rb' + +version = AnnotateModels::VERSION::STRING +download = 'http://rubyforge.org/projects/annotate_models' + +class Fixnum + def ordinal + # teens + return 'th' if (10..19).include?(self % 100) + # others + case self % 10 + when 1: return 'st' + when 2: return 'nd' + when 3: return 'rd' + else return 'th' + end + end +end + +class Time + def pretty + return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}" + end +end + +def convert_syntax(syntax, source) + return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^
|
$!,'') +end + +if ARGV.length >= 1 + src, template = ARGV + template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml') + +else + puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html") + exit! +end + +template = ERB.new(File.open(template).read) + +title = nil +body = nil +File.open(src) do |fsrc| + title_text = fsrc.readline + body_text = fsrc.read + syntax_items = [] + body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)!m){ + ident = syntax_items.length + element, syntax, source = $1, $2, $3 + syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}" + "syntax-temp-#{ident}" + } + title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip + body = RedCloth.new(body_text).to_html + body.gsub!(%r!(?:
)?syntax-temp-(\d+)(?:
)?!){ syntax_items[$1.to_i] } +end +stat = File.stat(src) +created = stat.ctime +modified = stat.mtime + +$stdout << template.result(binding) diff --git a/pkg/annotate_models-0.0.1/setup.rb b/pkg/annotate_models-0.0.1/setup.rb new file mode 100644 index 000000000..424a5f37c --- /dev/null +++ b/pkg/annotate_models-0.0.1/setup.rb @@ -0,0 +1,1585 @@ +# +# setup.rb +# +# Copyright (c) 2000-2005 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# + +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 + module Enumerable + alias map collect + end +end + +unless File.respond_to?(:read) # Ruby 1.6 + def File.read(fname) + open(fname) {|f| + return f.read + } + end +end + +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end + +def File.binread(fname) + open(fname, 'rb') {|f| + return f.read + } +end + +# for corrupted Windows' stat(2) +def File.dir?(path) + File.directory?((path[-1,1] == '/') ? path : path + '/') +end + + +class ConfigTable + + include Enumerable + + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end + + attr_accessor :install_prefix + attr_accessor :config_opt + + attr_writer :verbose + + def verbose? + @verbose + end + + attr_writer :no_harm + + def no_harm? + @no_harm + end + + def [](key) + lookup(key).resolve(self) + end + + def []=(key, val) + lookup(key).set val + end + + def names + @items.map {|i| i.name } + end + + def each(&block) + @items.each(&block) + end + + def key?(name) + @table.key?(name) + end + + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" + end + + def add(item) + @items.push item + @table[item.name] = item + end + + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item + end + + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end + end + + def savefile + '.config' + end + + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end + end + + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } + end + + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end + end + + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + major = c['MAJOR'].to_i + minor = c['MINOR'].to_i + teeny = c['TEENY'].to_i + version = "#{major}.#{minor}" + + # ruby ver. >= 1.4.4? + newpath_p = ((major >= 2) or + ((major == 1) and + ((minor >= 5) or + ((minor == 4) and (teeny >= 4))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" + end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } + + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' + end + + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end + end + + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + @items.freeze + @table.freeze + @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ + end + + def parse_opt(opt) + m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] + end + + def dllext + @rbconfig['DLEXT'] + end + + def value_config?(name) + lookup(name).value? + end + + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + + attr_reader :name + attr_reader :description + + attr_accessor :default + alias help_default default + + def help_opt + "--#{@name}=#{@template}" + end + + def value? + true + end + + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end + + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end + end + + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end + end + + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end + end + + class ProgramItem < Item + def config_type + 'program' + end + end + + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end + end + + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end + end + + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val + end + end + + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end + end + +end # class ConfigTable + + +# This module requires: #verbose?, #no_harm? +module FileOperations + + def mkdir_p(dirname, prefix = nil) + dirname = prefix + File.expand_path(dirname) if prefix + $stderr.puts "mkdir -p #{dirname}" if verbose? + return if no_harm? + + # Does not check '/', it's too abnormal. + dirs = File.expand_path(dirname).split(%r<(?=/)>) + if /\A[a-z]:\z/i =~ dirs[0] + disk = dirs.shift + dirs[0] = disk + dirs[0] + end + dirs.each_index do |idx| + path = dirs[0..idx].join('') + Dir.mkdir path unless File.dir?(path) + end + end + + def rm_f(path) + $stderr.puts "rm -f #{path}" if verbose? + return if no_harm? + force_remove_file path + end + + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? + return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end + + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath + else + force_remove_file entpath + end + end + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end + end + + def move_file(src, dest) + force_remove_file dest + begin + File.rename src, dest + rescue + File.open(dest, 'wb') {|f| + f.write File.binread(src) + } + File.chmod File.stat(src).mode, dest + File.unlink src + end + end + + def force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + + def install(from, dest, mode, prefix = nil) + $stderr.puts "install #{from} #{dest}" if verbose? + return if no_harm? + + realdest = prefix ? prefix + File.expand_path(dest) : dest + realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) + str = File.binread(from) + if diff?(str, realdest) + verbose_off { + rm_f realdest if File.exist?(realdest) + } + File.open(realdest, 'wb') {|f| + f.write str + } + File.chmod mode, realdest + + File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| + if prefix + f.puts realdest.sub(prefix, '') + else + f.puts realdest + end + } + end + end + + def diff?(new_content, path) + return true unless File.exist?(path) + new_content != File.binread(path) + end + + def command(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" + end + + def ruby(*args) + command config('rubyprog'), *args + end + + def make(task = nil) + command(*[config('makeprog'), task].compact) + end + + def extdir?(dir) + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") + end + + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } + } + end + + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) + + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT + } + end + +end + + +# This module requires: #srcdir_root, #objdir_root, #relpath +module HookScriptAPI + + def get_config(key) + @config[key] + end + + alias config get_config + + # obsolete: use metaconfig to change configuration + def set_config(key, val) + @config[key] = val + end + + # + # srcdir/objdir (works only in the package directory) + # + + def curr_srcdir + "#{srcdir_root()}/#{relpath()}" + end + + def curr_objdir + "#{objdir_root()}/#{relpath()}" + end + + def srcfile(path) + "#{curr_srcdir()}/#{path}" + end + + def srcexist?(path) + File.exist?(srcfile(path)) + end + + def srcdirectory?(path) + File.dir?(srcfile(path)) + end + + def srcfile?(path) + File.file?(srcfile(path)) + end + + def srcentries(path = '.') + Dir.open("#{curr_srcdir()}/#{path}") {|d| + return d.to_a - %w(. ..) + } + end + + def srcfiles(path = '.') + srcentries(path).select {|fname| + File.file?(File.join(curr_srcdir(), path, fname)) + } + end + + def srcdirectories(path = '.') + srcentries(path).select {|fname| + File.dir?(File.join(curr_srcdir(), path, fname)) + } + end + +end + + +class ToplevelInstaller + + Version = '3.4.1' + Copyright = 'Copyright (c) 2000-2005 Minero Aoki' + + TASKS = [ + [ 'all', 'do config, setup, then install' ], + [ 'config', 'saves your configurations' ], + [ 'show', 'shows current configuration' ], + [ 'setup', 'compiles ruby extentions and others' ], + [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], + [ 'clean', "does `make clean' for each extention" ], + [ 'distclean',"does `make distclean' for each extention" ] + ] + + def ToplevelInstaller.invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke + end + + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') + end + + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::Config::CONFIG + end + + def initialize(ardir_root, config) + @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] + end + + def inspect + "#<#{self.class} #{__id__()}>" + end + + def invoke + run_metaconfigs + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install + else + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" + end + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end + + def init_installers + @installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + # + # Hook Script API bases + # + + def srcdir_root + @ardir + end + + def objdir_root + '.' + end + + def relpath + '.' + end + + # + # Option Parsing + # + + def parsearg_global + while arg = ARGV.shift + case arg + when /\A\w+\z/ + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) + return arg + when '-q', '--quiet' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' + print_usage $stdout + exit 0 + when '--version' + puts "#{File.basename($0)} version #{Version}" + exit 0 + when '--copyright' + puts Copyright + exit 0 + else + setup_rb_error "unknown global option '#{arg}'" + end + end + nil + end + + def valid_task?(t) + valid_task_re() =~ t + end + + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end + + def parsearg_no_options + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end + end + + alias parsearg_show parsearg_no_options + alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options + alias parsearg_clean parsearg_no_options + alias parsearg_distclean parsearg_no_options + + def parsearg_config + evalopt = [] + set = [] + @config.config_opt = [] + while i = ARGV.shift + if /\A--?\z/ =~ i + @config.config_opt = ARGV.dup + break + end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value + else + evalopt.push [name, value] + end + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) + end + end + + def parsearg_install + @config.no_harm = false + @config.install_prefix = '' + while a = ARGV.shift + case a + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] + path = File.expand_path(path) unless path[0,1] == '/' + @config.install_prefix = path + else + setup_rb_error "install: unknown option #{a}" + end + end + end + + def print_usage(out) + out.puts 'Typical Installation Procedure:' + out.puts " $ ruby #{File.basename $0} config" + out.puts " $ ruby #{File.basename $0} setup" + out.puts " # ruby #{File.basename $0} install (may require root privilege)" + out.puts + out.puts 'Detailed Usage:' + out.puts " ruby #{File.basename $0} " + out.puts " ruby #{File.basename $0} [] []" + + fmt = " %-24s %s\n" + out.puts + out.puts 'Global options:' + out.printf fmt, '-q,--quiet', 'suppress message outputs' + out.printf fmt, ' --verbose', 'output messages verbosely' + out.printf fmt, ' --help', 'print this message' + out.printf fmt, ' --version', 'print version and quit' + out.printf fmt, ' --copyright', 'print copyright and quit' + out.puts + out.puts 'Tasks:' + TASKS.each do |name, desc| + out.printf fmt, name, desc + end + + fmt = " %-24s %s [%s]\n" + out.puts + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default + end + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" + out.puts + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path prefix', '' + out.puts + end + + # + # Task Handlers + # + + def exec_config + @installer.exec_config + @config.save # must be final + end + + def exec_setup + @installer.exec_setup + end + + def exec_install + @installer.exec_install + end + + def exec_test + @installer.exec_test + end + + def exec_show + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? + end + end + + def exec_clean + @installer.exec_clean + end + + def exec_distclean + @installer.exec_distclean + end + +end # class ToplevelInstaller + + +class ToplevelInstallerMulti < ToplevelInstaller + + include FileOperations + + def initialize(ardir_root, config) + super + @packages = directories_of("#{@ardir}/packages") + raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig", self + @packages.each do |name| + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" + end + end + + attr_reader :packages + + def packages=(list) + raise 'package list is empty' if list.empty? + list.each do |name| + raise "directory packages/#{name} does not exist"\ + unless File.dir?("#{@ardir}/packages/#{name}") + end + @packages = list + end + + def init_installers + @installers = {} + @packages.each do |pack| + @installers[pack] = Installer.new(@config, + "#{@ardir}/packages/#{pack}", + "packages/#{pack}") + end + with = extract_selection(config('with')) + without = extract_selection(config('without')) + @selected = @installers.keys.select {|name| + (with.empty? or with.include?(name)) \ + and not without.include?(name) + } + end + + def extract_selection(list) + a = list.split(/,/) + a.each do |name| + setup_rb_error "no such package: #{name}" unless @installers.key?(name) + end + a + end + + def print_usage(f) + super + f.puts 'Inluded packages:' + f.puts ' ' + @packages.sort.join(' ') + f.puts + end + + # + # Task Handlers + # + + def exec_config + run_hook 'pre-config' + each_selected_installers {|inst| inst.exec_config } + run_hook 'post-config' + @config.save # must be final + end + + def exec_setup + run_hook 'pre-setup' + each_selected_installers {|inst| inst.exec_setup } + run_hook 'post-setup' + end + + def exec_install + run_hook 'pre-install' + each_selected_installers {|inst| inst.exec_install } + run_hook 'post-install' + end + + def exec_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + + def exec_clean + rm_f @config.savefile + run_hook 'pre-clean' + each_selected_installers {|inst| inst.exec_clean } + run_hook 'post-clean' + end + + def exec_distclean + rm_f @config.savefile + run_hook 'pre-distclean' + each_selected_installers {|inst| inst.exec_distclean } + run_hook 'post-distclean' + end + + # + # lib + # + + def each_selected_installers + Dir.mkdir 'packages' unless File.dir?('packages') + @selected.each do |pack| + $stderr.puts "Processing the package `#{pack}' ..." if verbose? + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") + Dir.chdir "packages/#{pack}" + yield @installers[pack] + Dir.chdir '../..' + end + end + + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + +end # class ToplevelInstallerMulti + + +class Installer + + FILETYPES = %w( bin lib ext data conf man ) + + include FileOperations + include HookScriptAPI + + def initialize(config, srcroot, objroot) + @config = config + @srcdir = File.expand_path(srcroot) + @objdir = File.expand_path(objroot) + @currdir = '.' + end + + def inspect + "#<#{self.class} #{File.basename(@srcdir)}>" + end + + def noop(rel) + end + + # + # Hook Script API base methods + # + + def srcdir_root + @srcdir + end + + def objdir_root + @objdir + end + + def relpath + @currdir + end + + # + # Config Access + # + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + + def verbose_off + begin + save, @config.verbose = @config.verbose?, false + yield + ensure + @config.verbose = save + end + end + + # + # TASK config + # + + def exec_config + exec_task_traverse 'config' + end + + alias config_dir_bin noop + alias config_dir_lib noop + + def config_dir_ext(rel) + extconf if extdir?(curr_srcdir()) + end + + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop + + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt + end + + # + # TASK setup + # + + def exec_setup + exec_task_traverse 'setup' + end + + def setup_dir_bin(rel) + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" + end + end + + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop + + def update_shebang_line(path) + return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + + def open_atomic_writer(path, &block) + tmpfile = File.basename(path) + '.tmp' + begin + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) + ensure + File.unlink tmpfile if File.exist?(tmpfile) + end + end + + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end + + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args + + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end + end + + # + # TASK install + # + + def exec_install + rm_f 'InstalledFiles' + exec_task_traverse 'install' + end + + def install_dir_bin(rel) + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 + end + + def install_dir_lib(rel) + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 + end + + def install_dir_ext(rel) + return unless extdir?(curr_srcdir()) + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", + 0555 + end + + def install_dir_data(rel) + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 + end + + def install_files(list, dest, mode) + mkdir_p dest, @config.install_prefix + list.each do |fname| + install fname, dest, mode, @config.install_prefix + end + end + + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) + end + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + + # picked up many entries from cvs-1.11.1/src/ignore.c + JUNK_FILES = %w( + core RCSLOG tags TAGS .make.state + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb + *~ *.old *.bak *.BAK *.orig *.rej _$* *$ + + *.org *.in .* + ) + + def existfiles + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) + end + + def hookfiles + %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| + %w( config setup install clean ).map {|t| sprintf(fmt, t) } + }.flatten + end + + def glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } + end + + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } + end + + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } + + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run + end + + # + # TASK clean + # + + def exec_clean + exec_task_traverse 'clean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop + + def clean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'clean' if File.file?('Makefile') + end + + # + # TASK distclean + # + + def exec_distclean + exec_task_traverse 'distclean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias distclean_dir_bin noop + alias distclean_dir_lib noop + + def distclean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'distclean' if File.file?('Makefile') + end + + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + + # + # Traversing + # + + def exec_task_traverse(task) + run_hook "pre-#{task}" + FILETYPES.each do |type| + if type == 'ext' and config('without-ext') == 'yes' + $stderr.puts 'skipping ext/* by user option' if verbose? + next + end + traverse task, type, "#{task}_dir_#{type}" + end + run_hook "post-#{task}" + end + + def traverse(task, rel, mid) + dive_into(rel) { + run_hook "pre-#{task}" + __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') + directories_of(curr_srcdir()).each do |d| + traverse task, "#{rel}/#{d}", mid + end + run_hook "post-#{task}" + } + end + + def dive_into(rel) + return unless File.dir?("#{@srcdir}/#{rel}") + + dir = File.basename(rel) + Dir.mkdir dir unless File.dir?(dir) + prevdir = Dir.pwd + Dir.chdir dir + $stderr.puts '---> ' + rel if verbose? + @currdir = rel + yield + Dir.chdir prevdir + $stderr.puts '<--- ' + rel if verbose? + @currdir = File.dirname(rel) + end + + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer + + +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + +if $0 == __FILE__ + begin + ToplevelInstaller.invoke + rescue SetupError + raise if $DEBUG + $stderr.puts $!.message + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." + exit 1 + end +end diff --git a/pkg/annotate_models-0.0.1/tasks/deployment.rake b/pkg/annotate_models-0.0.1/tasks/deployment.rake new file mode 100644 index 000000000..2f437425b --- /dev/null +++ b/pkg/annotate_models-0.0.1/tasks/deployment.rake @@ -0,0 +1,34 @@ +desc 'Release the website and new gem version' +task :deploy => [:check_version, :website, :release] do + puts "Remember to create SVN tag:" + puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " + + "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} " + puts "Suggested comment:" + puts "Tagging release #{CHANGES}" +end + +desc 'Runs tasks website_generate and install_gem as a local deployment of the gem' +task :local_deploy => [:website_generate, :install_gem] + +task :check_version do + unless ENV['VERSION'] + puts 'Must pass a VERSION=x.y.z release version' + exit + end + unless ENV['VERSION'] == VERS + puts "Please update your version.rb to match the release version, currently #{VERS}" + exit + end +end + +desc 'Install the package as a gem, without generating documentation(ri/rdoc)' +task :install_gem_no_doc => [:clean, :package] do + sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri" +end + +namespace :manifest do + desc 'Recreate Manifest.txt to include ALL files' + task :refresh do + `rake check_manifest | patch -p0 > Manifest.txt` + end +end \ No newline at end of file diff --git a/pkg/annotate_models-0.0.1/tasks/environment.rake b/pkg/annotate_models-0.0.1/tasks/environment.rake new file mode 100644 index 000000000..691ed3b65 --- /dev/null +++ b/pkg/annotate_models-0.0.1/tasks/environment.rake @@ -0,0 +1,7 @@ +task :ruby_env do + RUBY_APP = if RUBY_PLATFORM =~ /java/ + "jruby" + else + "ruby" + end unless defined? RUBY_APP +end diff --git a/pkg/annotate_models-0.0.1/tasks/website.rake b/pkg/annotate_models-0.0.1/tasks/website.rake new file mode 100644 index 000000000..93e03faa6 --- /dev/null +++ b/pkg/annotate_models-0.0.1/tasks/website.rake @@ -0,0 +1,17 @@ +desc 'Generate website files' +task :website_generate => :ruby_env do + (Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt| + sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} } + end +end + +desc 'Upload website files to rubyforge' +task :website_upload do + host = "#{rubyforge_username}@rubyforge.org" + remote_dir = "/var/www/gforge-projects/#{PATH}/" + local_dir = 'website' + sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}} +end + +desc 'Generate and upload website files' +task :website => [:website_generate, :website_upload, :publish_docs] diff --git a/pkg/annotate_models-0.0.1/test/test_annotate_models.rb b/pkg/annotate_models-0.0.1/test/test_annotate_models.rb new file mode 100644 index 000000000..628b94b65 --- /dev/null +++ b/pkg/annotate_models-0.0.1/test/test_annotate_models.rb @@ -0,0 +1,11 @@ +require File.dirname(__FILE__) + '/test_helper.rb' + +class TestAnnotateModels < Test::Unit::TestCase + + def setup + end + + def test_truth + assert true + end +end diff --git a/pkg/annotate_models-0.0.1/test/test_helper.rb b/pkg/annotate_models-0.0.1/test/test_helper.rb new file mode 100644 index 000000000..30f83d987 --- /dev/null +++ b/pkg/annotate_models-0.0.1/test/test_helper.rb @@ -0,0 +1,2 @@ +require 'test/unit' +require File.dirname(__FILE__) + '/../lib/annotate_models' diff --git a/pkg/annotate_models-0.0.1/website/index.html b/pkg/annotate_models-0.0.1/website/index.html new file mode 100644 index 000000000..1a5da3bc3 --- /dev/null +++ b/pkg/annotate_models-0.0.1/website/index.html @@ -0,0 +1,93 @@ + + + + + + + annotate_models + + + + + + +
+ +

annotate_models

+
+

Get Version

+ 0.0.1 +
+

→ ‘annotate_models’

+ + +

What

+ + +

Installing

+ + +

sudo gem install annotate_models

+ + +

The basics

+ + +

Demonstration of usage

+ + +

Forum

+ + +

http://groups.google.com/group/annotate_models

+ + +

TODO – create Google Group – annotate_models

+ + +

How to submit patches

+ + +

Read the 8 steps for fixing other people’s code and for section 8b: Submit patch to Google Groups, use the Google Group above.

+ + +

The trunk repository is svn://rubyforge.org/var/svn/annotate_models/trunk for anonymous access.

+ + +

License

+ + +

This code is free to use under the terms of the MIT license.

+ + +

Contact

+ + +

Comments are welcome. Send an email to FIXME full name email via the forum

+

+ FIXME full name, 27th February 2008
+ Theme extended from Paul Battley +

+
+ + + + + diff --git a/pkg/annotate_models-0.0.1/website/index.txt b/pkg/annotate_models-0.0.1/website/index.txt new file mode 100644 index 000000000..2c699468b --- /dev/null +++ b/pkg/annotate_models-0.0.1/website/index.txt @@ -0,0 +1,39 @@ +h1. annotate_models + +h1. → 'annotate_models' + + +h2. What + + +h2. Installing + +
sudo gem install annotate_models
+ +h2. The basics + + +h2. Demonstration of usage + + + +h2. Forum + +"http://groups.google.com/group/annotate_models":http://groups.google.com/group/annotate_models + +TODO - create Google Group - annotate_models + +h2. How to submit patches + +Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above. + +The trunk repository is svn://rubyforge.org/var/svn/annotate_models/trunk for anonymous access. + +h2. License + +This code is free to use under the terms of the MIT license. + +h2. Contact + +Comments are welcome. Send an email to "FIXME full name":mailto:FIXME email via the "forum":http://groups.google.com/group/annotate_models + diff --git a/pkg/annotate_models-0.0.1/website/javascripts/rounded_corners_lite.inc.js b/pkg/annotate_models-0.0.1/website/javascripts/rounded_corners_lite.inc.js new file mode 100644 index 000000000..afc3ea327 --- /dev/null +++ b/pkg/annotate_models-0.0.1/website/javascripts/rounded_corners_lite.inc.js @@ -0,0 +1,285 @@ + + /**************************************************************** + * * + * curvyCorners * + * ------------ * + * * + * This script generates rounded corners for your divs. * + * * + * Version 1.2.9 * + * Copyright (c) 2006 Cameron Cooke * + * By: Cameron Cooke and Tim Hutchison. * + * * + * * + * Website: http://www.curvycorners.net * + * Email: info@totalinfinity.com * + * Forum: http://www.curvycorners.net/forum/ * + * * + * * + * This library is free software; you can redistribute * + * it and/or modify it under the terms of the GNU * + * Lesser General Public License as published by the * + * Free Software Foundation; either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the * + * implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU Lesser General Public * + * License for more details. * + * * + * You should have received a copy of the GNU Lesser * + * General Public License along with this library; * + * Inc., 59 Temple Place, Suite 330, Boston, * + * MA 02111-1307 USA * + * * + ****************************************************************/ + +var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners() +{ if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string") +{ var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);} +else +{ var startIndex = 1; var boxCol = arguments;} +var curvyCornersCol = new Array(); if(arguments[0].validTags) +var validElements = arguments[0].validTags; else +var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++) +{ var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false) +{ curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);} +} +this.objects = curvyCornersCol; this.applyCornersToAll = function() +{ for(var x = 0, k = this.objects.length; x < k; x++) +{ this.objects[x].applyCorners();} +} +} +function curvyObject() +{ this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderTopWidth", "border-top-width"); var borderColour = get_style(this.box, "borderTopColor", "border-top-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingTop", "padding-top"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0) +this.box.innerHTML = ""; this.applyCorners = function() +{ for(var t = 0; t < 2; t++) +{ switch(t) +{ case 0: +if(this.settings.tl || this.settings.tr) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);} +break; case 1: +if(this.settings.bl || this.settings.br) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);} +break;} +} +if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners) +{ if(i > -1 < 4) +{ var cc = corners[i]; if(!this.settings[cc]) +{ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) +{ var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "") +newCorner.style.backgroundColor = this.boxColour; else +newCorner.style.backgroundImage = this.backgroundImage; switch(cc) +{ case "tl": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px" +newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;} +} +} +else +{ if(this.masterCorners[this.settings[cc].radius]) +{ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);} +else +{ var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) +{ if((intx +1) >= borderRadius) +var y1 = -1; else +var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j) +{ if((intx) >= borderRadius) +var y2 = -1; else +var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j) +var y3 = -1; else +var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);} +if((intx) >= j) +var y4 = -1; else +var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j) +{ for(var inty = (y1 + 1); inty < y2; inty++) +{ if(this.settings.antiAlias) +{ if(this.backgroundImage != "") +{ var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30) +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);} +else +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);} +} +else +{ var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);} +} +} +if(this.settings.antiAlias) +{ if(y3 >= y2) +{ if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);} +} +else +{ if(y3 >= y1) +{ this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);} +} +var outsideColour = this.borderColour;} +else +{ var outsideColour = this.boxColour; var y3 = y1;} +if(this.settings.antiAlias) +{ for(var inty = (y3 + 1); inty < y4; inty++) +{ this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);} +} +} +this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);} +if(cc != "br") +{ for(var t = 0, k = newCorner.childNodes.length; t < k; t++) +{ var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";} +if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";} +switch(cc) +{ case "tr": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;} +} +} +} +if(newCorner) +{ switch(cc) +{ case "tl": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;} +} +} +} +var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius) +radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff) +{ if(z == "t" || z == "b") +{ if(radiusDiff[z]) +{ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px" +newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType) +{ case "tl": +newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr": +newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl": +newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br": +newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;} +} +var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z) +{ case "t": +if(this.topContainer) +{ if(this.settings.tl.radius && this.settings.tr.radius) +{ newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);} +this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";} +break; case "b": +if(this.bottomContainer) +{ if(this.settings.bl.radius && this.settings.br.radius) +{ newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);} +} +break;} +} +} +if(this.settings.autoPad == true && this.boxPadding > 0) +{ var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding) +contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding) +contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);} +} +this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) +{ var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "") +{ pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";} +else +{ pixel.style.backgroundColor = colour;} +if (transAmount != 100) +setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);} +} +function insertAfter(parent, node, referenceNode) +{ parent.insertBefore(node, referenceNode.nextSibling);} +function BlendColour(Col1, Col2, Col1Fraction) +{ var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);} +function IntToHex(strNum) +{ base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;} +function MakeHex(x) +{ if((x >= 0) && (x <= 9)) +{ return x;} +else +{ switch(x) +{ case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";} +} +} +function pixelFraction(x, y, r) +{ var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;} +switch (whatsides) +{ case "LeftRight": +pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight": +pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom": +pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom": +pixelfraction = (yvalues[0]*xvalues[1])/2; break; default: +pixelfraction = 1;} +return pixelfraction;} +function rgb2Hex(rgbColour) +{ try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);} +catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");} +return hexColour;} +function rgb2Array(rgbColour) +{ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;} +function setOpacity(obj, opacity) +{ opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME") +{ var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";} +else if(typeof(obj.style.opacity) != "undefined") +{ obj.style.opacity = opacity/100;} +else if(typeof(obj.style.MozOpacity) != "undefined") +{ obj.style.MozOpacity = opacity/100;} +else if(typeof(obj.style.filter) != "undefined") +{ obj.style.filter = "alpha(opacity:" + opacity + ")";} +else if(typeof(obj.style.KHTMLOpacity) != "undefined") +{ obj.style.KHTMLOpacity = opacity/100;} +} +function inArray(array, value) +{ for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;} +return false;} +function inArrayKey(array, value) +{ for(key in array){ if(key === value) return true;} +return false;} +function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;} +else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;} +else { elm['on' + evType] = fn;} +} +function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");} +} +function format_colour(colour) +{ var returnColour = "#ffffff"; if(colour != "" && colour != "transparent") +{ if(colour.substr(0, 3) == "rgb") +{ returnColour = rgb2Hex(colour);} +else if(colour.length == 4) +{ returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);} +else +{ returnColour = colour;} +} +return returnColour;} +function get_style(obj, property, propertyNS) +{ try +{ if(obj.currentStyle) +{ var returnVal = eval("obj.currentStyle." + property);} +else +{ if(isSafari && obj.style.display == "none") +{ obj.style.display = ""; var wasHidden = true;} +var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden) +{ obj.style.display = "none";} +} +} +catch(e) +{ } +return returnVal;} +function getElementsByClass(searchClass, node, tag) +{ var classElements = new Array(); if(node == null) +node = document; if(tag == null) +tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++) +{ if(pattern.test(els[i].className)) +{ classElements[j] = els[i]; j++;} +} +return classElements;} +function newCurvyError(errorMessage) +{ return new Error("curvyCorners Error:\n" + errorMessage) +} diff --git a/pkg/annotate_models-0.0.1/website/stylesheets/screen.css b/pkg/annotate_models-0.0.1/website/stylesheets/screen.css new file mode 100644 index 000000000..2c84cd00c --- /dev/null +++ b/pkg/annotate_models-0.0.1/website/stylesheets/screen.css @@ -0,0 +1,138 @@ +body { + background-color: #E1D1F1; + font-family: "Georgia", sans-serif; + font-size: 16px; + line-height: 1.6em; + padding: 1.6em 0 0 0; + color: #333; +} +h1, h2, h3, h4, h5, h6 { + color: #444; +} +h1 { + font-family: sans-serif; + font-weight: normal; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin: 5px; +} +li { + padding: 0; + margin: 0; + list-style-type: square; +} +a { + color: #5E5AFF; + background-color: #DAC; + font-weight: normal; + text-decoration: underline; +} +blockquote { + font-size: 90%; + font-style: italic; + border-left: 1px solid #111; + padding-left: 1em; +} +.caps { + font-size: 80%; +} + +#main { + width: 45em; + padding: 0; + margin: 0 auto; +} +.coda { + text-align: right; + color: #77f; + font-size: smaller; +} + +table { + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; + border-style: dashed; +} + +th { + color: #fff; +} + +td { + padding: 2px 10px 2px 10px; +} + +.success { + color: #0CC52B; +} + +.failed { + color: #E90A1B; +} + +.unknown { + color: #995000; +} +pre, code { + font-family: monospace; + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; +} +.comment { color: #aaa; font-style: italic; } +.keyword { color: #eff; font-weight: bold; } +.punct { color: #eee; font-weight: bold; } +.symbol { color: #0bb; } +.string { color: #6b4; } +.ident { color: #ff8; } +.constant { color: #66f; } +.regex { color: #ec6; } +.number { color: #F99; } +.expr { color: #227; } + +#version { + float: right; + text-align: right; + font-family: sans-serif; + font-weight: normal; + background-color: #B3ABFF; + color: #141331; + padding: 15px 20px 10px 20px; + margin: 0 auto; + margin-top: 15px; + border: 3px solid #141331; +} + +#version .numbers { + display: block; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin-bottom: 15px; +} + +#version p { + text-decoration: none; + color: #141331; + background-color: #B3ABFF; + margin: 0; + padding: 0; +} + +#version a { + text-decoration: none; + color: #141331; + background-color: #B3ABFF; +} + +.clickable { + cursor: pointer; + cursor: hand; +} + diff --git a/pkg/annotate_models-0.0.1/website/template.rhtml b/pkg/annotate_models-0.0.1/website/template.rhtml new file mode 100644 index 000000000..e8517350d --- /dev/null +++ b/pkg/annotate_models-0.0.1/website/template.rhtml @@ -0,0 +1,48 @@ + + + + + + + <%= title %> + + + + + + +
+ +

<%= title %>

+
+

Get Version

+ <%= version %> +
+ <%= body %> +

+ FIXME full name, <%= modified.pretty %>
+ Theme extended from Paul Battley +

+
+ + + + + diff --git a/script/destroy b/script/destroy new file mode 100755 index 000000000..e48464df5 --- /dev/null +++ b/script/destroy @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] +RubiGen::Scripts::Destroy.new.run(ARGV) diff --git a/script/generate b/script/generate new file mode 100755 index 000000000..c27f65593 --- /dev/null +++ b/script/generate @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] +RubiGen::Scripts::Generate.new.run(ARGV) diff --git a/script/txt2html b/script/txt2html new file mode 100755 index 000000000..b9200f8a0 --- /dev/null +++ b/script/txt2html @@ -0,0 +1,74 @@ +#!/usr/bin/env ruby + +require 'rubygems' +begin + require 'newgem' +rescue LoadError + puts "\n\nGenerating the website requires the newgem RubyGem" + puts "Install: gem install newgem\n\n" + exit(1) +end +require 'redcloth' +require 'syntax/convertors/html' +require 'erb' +require File.dirname(__FILE__) + '/../lib/annotate_models/version.rb' + +version = AnnotateModels::VERSION::STRING +download = 'http://rubyforge.org/projects/annotate_models' + +class Fixnum + def ordinal + # teens + return 'th' if (10..19).include?(self % 100) + # others + case self % 10 + when 1: return 'st' + when 2: return 'nd' + when 3: return 'rd' + else return 'th' + end + end +end + +class Time + def pretty + return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}" + end +end + +def convert_syntax(syntax, source) + return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^
|
$!,'') +end + +if ARGV.length >= 1 + src, template = ARGV + template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml') + +else + puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html") + exit! +end + +template = ERB.new(File.open(template).read) + +title = nil +body = nil +File.open(src) do |fsrc| + title_text = fsrc.readline + body_text = fsrc.read + syntax_items = [] + body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)!m){ + ident = syntax_items.length + element, syntax, source = $1, $2, $3 + syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}" + "syntax-temp-#{ident}" + } + title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip + body = RedCloth.new(body_text).to_html + body.gsub!(%r!(?:
)?syntax-temp-(\d+)(?:
)?!){ syntax_items[$1.to_i] } +end +stat = File.stat(src) +created = stat.ctime +modified = stat.mtime + +$stdout << template.result(binding) diff --git a/setup.rb b/setup.rb new file mode 100644 index 000000000..424a5f37c --- /dev/null +++ b/setup.rb @@ -0,0 +1,1585 @@ +# +# setup.rb +# +# Copyright (c) 2000-2005 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# + +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 + module Enumerable + alias map collect + end +end + +unless File.respond_to?(:read) # Ruby 1.6 + def File.read(fname) + open(fname) {|f| + return f.read + } + end +end + +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end + +def File.binread(fname) + open(fname, 'rb') {|f| + return f.read + } +end + +# for corrupted Windows' stat(2) +def File.dir?(path) + File.directory?((path[-1,1] == '/') ? path : path + '/') +end + + +class ConfigTable + + include Enumerable + + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end + + attr_accessor :install_prefix + attr_accessor :config_opt + + attr_writer :verbose + + def verbose? + @verbose + end + + attr_writer :no_harm + + def no_harm? + @no_harm + end + + def [](key) + lookup(key).resolve(self) + end + + def []=(key, val) + lookup(key).set val + end + + def names + @items.map {|i| i.name } + end + + def each(&block) + @items.each(&block) + end + + def key?(name) + @table.key?(name) + end + + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" + end + + def add(item) + @items.push item + @table[item.name] = item + end + + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item + end + + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end + end + + def savefile + '.config' + end + + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end + end + + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } + end + + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end + end + + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + major = c['MAJOR'].to_i + minor = c['MINOR'].to_i + teeny = c['TEENY'].to_i + version = "#{major}.#{minor}" + + # ruby ver. >= 1.4.4? + newpath_p = ((major >= 2) or + ((major == 1) and + ((minor >= 5) or + ((minor == 4) and (teeny >= 4))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" + end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } + + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' + end + + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end + end + + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + @items.freeze + @table.freeze + @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ + end + + def parse_opt(opt) + m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] + end + + def dllext + @rbconfig['DLEXT'] + end + + def value_config?(name) + lookup(name).value? + end + + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + + attr_reader :name + attr_reader :description + + attr_accessor :default + alias help_default default + + def help_opt + "--#{@name}=#{@template}" + end + + def value? + true + end + + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end + + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end + end + + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end + end + + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end + end + + class ProgramItem < Item + def config_type + 'program' + end + end + + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end + end + + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end + end + + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val + end + end + + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end + end + +end # class ConfigTable + + +# This module requires: #verbose?, #no_harm? +module FileOperations + + def mkdir_p(dirname, prefix = nil) + dirname = prefix + File.expand_path(dirname) if prefix + $stderr.puts "mkdir -p #{dirname}" if verbose? + return if no_harm? + + # Does not check '/', it's too abnormal. + dirs = File.expand_path(dirname).split(%r<(?=/)>) + if /\A[a-z]:\z/i =~ dirs[0] + disk = dirs.shift + dirs[0] = disk + dirs[0] + end + dirs.each_index do |idx| + path = dirs[0..idx].join('') + Dir.mkdir path unless File.dir?(path) + end + end + + def rm_f(path) + $stderr.puts "rm -f #{path}" if verbose? + return if no_harm? + force_remove_file path + end + + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? + return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end + + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath + else + force_remove_file entpath + end + end + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end + end + + def move_file(src, dest) + force_remove_file dest + begin + File.rename src, dest + rescue + File.open(dest, 'wb') {|f| + f.write File.binread(src) + } + File.chmod File.stat(src).mode, dest + File.unlink src + end + end + + def force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + + def install(from, dest, mode, prefix = nil) + $stderr.puts "install #{from} #{dest}" if verbose? + return if no_harm? + + realdest = prefix ? prefix + File.expand_path(dest) : dest + realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) + str = File.binread(from) + if diff?(str, realdest) + verbose_off { + rm_f realdest if File.exist?(realdest) + } + File.open(realdest, 'wb') {|f| + f.write str + } + File.chmod mode, realdest + + File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| + if prefix + f.puts realdest.sub(prefix, '') + else + f.puts realdest + end + } + end + end + + def diff?(new_content, path) + return true unless File.exist?(path) + new_content != File.binread(path) + end + + def command(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" + end + + def ruby(*args) + command config('rubyprog'), *args + end + + def make(task = nil) + command(*[config('makeprog'), task].compact) + end + + def extdir?(dir) + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") + end + + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } + } + end + + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) + + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT + } + end + +end + + +# This module requires: #srcdir_root, #objdir_root, #relpath +module HookScriptAPI + + def get_config(key) + @config[key] + end + + alias config get_config + + # obsolete: use metaconfig to change configuration + def set_config(key, val) + @config[key] = val + end + + # + # srcdir/objdir (works only in the package directory) + # + + def curr_srcdir + "#{srcdir_root()}/#{relpath()}" + end + + def curr_objdir + "#{objdir_root()}/#{relpath()}" + end + + def srcfile(path) + "#{curr_srcdir()}/#{path}" + end + + def srcexist?(path) + File.exist?(srcfile(path)) + end + + def srcdirectory?(path) + File.dir?(srcfile(path)) + end + + def srcfile?(path) + File.file?(srcfile(path)) + end + + def srcentries(path = '.') + Dir.open("#{curr_srcdir()}/#{path}") {|d| + return d.to_a - %w(. ..) + } + end + + def srcfiles(path = '.') + srcentries(path).select {|fname| + File.file?(File.join(curr_srcdir(), path, fname)) + } + end + + def srcdirectories(path = '.') + srcentries(path).select {|fname| + File.dir?(File.join(curr_srcdir(), path, fname)) + } + end + +end + + +class ToplevelInstaller + + Version = '3.4.1' + Copyright = 'Copyright (c) 2000-2005 Minero Aoki' + + TASKS = [ + [ 'all', 'do config, setup, then install' ], + [ 'config', 'saves your configurations' ], + [ 'show', 'shows current configuration' ], + [ 'setup', 'compiles ruby extentions and others' ], + [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], + [ 'clean', "does `make clean' for each extention" ], + [ 'distclean',"does `make distclean' for each extention" ] + ] + + def ToplevelInstaller.invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke + end + + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') + end + + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::Config::CONFIG + end + + def initialize(ardir_root, config) + @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] + end + + def inspect + "#<#{self.class} #{__id__()}>" + end + + def invoke + run_metaconfigs + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install + else + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" + end + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end + + def init_installers + @installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + # + # Hook Script API bases + # + + def srcdir_root + @ardir + end + + def objdir_root + '.' + end + + def relpath + '.' + end + + # + # Option Parsing + # + + def parsearg_global + while arg = ARGV.shift + case arg + when /\A\w+\z/ + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) + return arg + when '-q', '--quiet' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' + print_usage $stdout + exit 0 + when '--version' + puts "#{File.basename($0)} version #{Version}" + exit 0 + when '--copyright' + puts Copyright + exit 0 + else + setup_rb_error "unknown global option '#{arg}'" + end + end + nil + end + + def valid_task?(t) + valid_task_re() =~ t + end + + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end + + def parsearg_no_options + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end + end + + alias parsearg_show parsearg_no_options + alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options + alias parsearg_clean parsearg_no_options + alias parsearg_distclean parsearg_no_options + + def parsearg_config + evalopt = [] + set = [] + @config.config_opt = [] + while i = ARGV.shift + if /\A--?\z/ =~ i + @config.config_opt = ARGV.dup + break + end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value + else + evalopt.push [name, value] + end + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) + end + end + + def parsearg_install + @config.no_harm = false + @config.install_prefix = '' + while a = ARGV.shift + case a + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] + path = File.expand_path(path) unless path[0,1] == '/' + @config.install_prefix = path + else + setup_rb_error "install: unknown option #{a}" + end + end + end + + def print_usage(out) + out.puts 'Typical Installation Procedure:' + out.puts " $ ruby #{File.basename $0} config" + out.puts " $ ruby #{File.basename $0} setup" + out.puts " # ruby #{File.basename $0} install (may require root privilege)" + out.puts + out.puts 'Detailed Usage:' + out.puts " ruby #{File.basename $0} " + out.puts " ruby #{File.basename $0} [] []" + + fmt = " %-24s %s\n" + out.puts + out.puts 'Global options:' + out.printf fmt, '-q,--quiet', 'suppress message outputs' + out.printf fmt, ' --verbose', 'output messages verbosely' + out.printf fmt, ' --help', 'print this message' + out.printf fmt, ' --version', 'print version and quit' + out.printf fmt, ' --copyright', 'print copyright and quit' + out.puts + out.puts 'Tasks:' + TASKS.each do |name, desc| + out.printf fmt, name, desc + end + + fmt = " %-24s %s [%s]\n" + out.puts + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default + end + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" + out.puts + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path prefix', '' + out.puts + end + + # + # Task Handlers + # + + def exec_config + @installer.exec_config + @config.save # must be final + end + + def exec_setup + @installer.exec_setup + end + + def exec_install + @installer.exec_install + end + + def exec_test + @installer.exec_test + end + + def exec_show + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? + end + end + + def exec_clean + @installer.exec_clean + end + + def exec_distclean + @installer.exec_distclean + end + +end # class ToplevelInstaller + + +class ToplevelInstallerMulti < ToplevelInstaller + + include FileOperations + + def initialize(ardir_root, config) + super + @packages = directories_of("#{@ardir}/packages") + raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig", self + @packages.each do |name| + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" + end + end + + attr_reader :packages + + def packages=(list) + raise 'package list is empty' if list.empty? + list.each do |name| + raise "directory packages/#{name} does not exist"\ + unless File.dir?("#{@ardir}/packages/#{name}") + end + @packages = list + end + + def init_installers + @installers = {} + @packages.each do |pack| + @installers[pack] = Installer.new(@config, + "#{@ardir}/packages/#{pack}", + "packages/#{pack}") + end + with = extract_selection(config('with')) + without = extract_selection(config('without')) + @selected = @installers.keys.select {|name| + (with.empty? or with.include?(name)) \ + and not without.include?(name) + } + end + + def extract_selection(list) + a = list.split(/,/) + a.each do |name| + setup_rb_error "no such package: #{name}" unless @installers.key?(name) + end + a + end + + def print_usage(f) + super + f.puts 'Inluded packages:' + f.puts ' ' + @packages.sort.join(' ') + f.puts + end + + # + # Task Handlers + # + + def exec_config + run_hook 'pre-config' + each_selected_installers {|inst| inst.exec_config } + run_hook 'post-config' + @config.save # must be final + end + + def exec_setup + run_hook 'pre-setup' + each_selected_installers {|inst| inst.exec_setup } + run_hook 'post-setup' + end + + def exec_install + run_hook 'pre-install' + each_selected_installers {|inst| inst.exec_install } + run_hook 'post-install' + end + + def exec_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + + def exec_clean + rm_f @config.savefile + run_hook 'pre-clean' + each_selected_installers {|inst| inst.exec_clean } + run_hook 'post-clean' + end + + def exec_distclean + rm_f @config.savefile + run_hook 'pre-distclean' + each_selected_installers {|inst| inst.exec_distclean } + run_hook 'post-distclean' + end + + # + # lib + # + + def each_selected_installers + Dir.mkdir 'packages' unless File.dir?('packages') + @selected.each do |pack| + $stderr.puts "Processing the package `#{pack}' ..." if verbose? + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") + Dir.chdir "packages/#{pack}" + yield @installers[pack] + Dir.chdir '../..' + end + end + + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + +end # class ToplevelInstallerMulti + + +class Installer + + FILETYPES = %w( bin lib ext data conf man ) + + include FileOperations + include HookScriptAPI + + def initialize(config, srcroot, objroot) + @config = config + @srcdir = File.expand_path(srcroot) + @objdir = File.expand_path(objroot) + @currdir = '.' + end + + def inspect + "#<#{self.class} #{File.basename(@srcdir)}>" + end + + def noop(rel) + end + + # + # Hook Script API base methods + # + + def srcdir_root + @srcdir + end + + def objdir_root + @objdir + end + + def relpath + @currdir + end + + # + # Config Access + # + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + + def verbose_off + begin + save, @config.verbose = @config.verbose?, false + yield + ensure + @config.verbose = save + end + end + + # + # TASK config + # + + def exec_config + exec_task_traverse 'config' + end + + alias config_dir_bin noop + alias config_dir_lib noop + + def config_dir_ext(rel) + extconf if extdir?(curr_srcdir()) + end + + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop + + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt + end + + # + # TASK setup + # + + def exec_setup + exec_task_traverse 'setup' + end + + def setup_dir_bin(rel) + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" + end + end + + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop + + def update_shebang_line(path) + return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + + def open_atomic_writer(path, &block) + tmpfile = File.basename(path) + '.tmp' + begin + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) + ensure + File.unlink tmpfile if File.exist?(tmpfile) + end + end + + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end + + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args + + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end + end + + # + # TASK install + # + + def exec_install + rm_f 'InstalledFiles' + exec_task_traverse 'install' + end + + def install_dir_bin(rel) + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 + end + + def install_dir_lib(rel) + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 + end + + def install_dir_ext(rel) + return unless extdir?(curr_srcdir()) + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", + 0555 + end + + def install_dir_data(rel) + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 + end + + def install_files(list, dest, mode) + mkdir_p dest, @config.install_prefix + list.each do |fname| + install fname, dest, mode, @config.install_prefix + end + end + + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) + end + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + + # picked up many entries from cvs-1.11.1/src/ignore.c + JUNK_FILES = %w( + core RCSLOG tags TAGS .make.state + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb + *~ *.old *.bak *.BAK *.orig *.rej _$* *$ + + *.org *.in .* + ) + + def existfiles + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) + end + + def hookfiles + %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| + %w( config setup install clean ).map {|t| sprintf(fmt, t) } + }.flatten + end + + def glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } + end + + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } + end + + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } + + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run + end + + # + # TASK clean + # + + def exec_clean + exec_task_traverse 'clean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop + + def clean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'clean' if File.file?('Makefile') + end + + # + # TASK distclean + # + + def exec_distclean + exec_task_traverse 'distclean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias distclean_dir_bin noop + alias distclean_dir_lib noop + + def distclean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'distclean' if File.file?('Makefile') + end + + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + + # + # Traversing + # + + def exec_task_traverse(task) + run_hook "pre-#{task}" + FILETYPES.each do |type| + if type == 'ext' and config('without-ext') == 'yes' + $stderr.puts 'skipping ext/* by user option' if verbose? + next + end + traverse task, type, "#{task}_dir_#{type}" + end + run_hook "post-#{task}" + end + + def traverse(task, rel, mid) + dive_into(rel) { + run_hook "pre-#{task}" + __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') + directories_of(curr_srcdir()).each do |d| + traverse task, "#{rel}/#{d}", mid + end + run_hook "post-#{task}" + } + end + + def dive_into(rel) + return unless File.dir?("#{@srcdir}/#{rel}") + + dir = File.basename(rel) + Dir.mkdir dir unless File.dir?(dir) + prevdir = Dir.pwd + Dir.chdir dir + $stderr.puts '---> ' + rel if verbose? + @currdir = rel + yield + Dir.chdir prevdir + $stderr.puts '<--- ' + rel if verbose? + @currdir = File.dirname(rel) + end + + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer + + +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + +if $0 == __FILE__ + begin + ToplevelInstaller.invoke + rescue SetupError + raise if $DEBUG + $stderr.puts $!.message + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." + exit 1 + end +end diff --git a/tasks/deployment.rake b/tasks/deployment.rake new file mode 100644 index 000000000..2f437425b --- /dev/null +++ b/tasks/deployment.rake @@ -0,0 +1,34 @@ +desc 'Release the website and new gem version' +task :deploy => [:check_version, :website, :release] do + puts "Remember to create SVN tag:" + puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " + + "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} " + puts "Suggested comment:" + puts "Tagging release #{CHANGES}" +end + +desc 'Runs tasks website_generate and install_gem as a local deployment of the gem' +task :local_deploy => [:website_generate, :install_gem] + +task :check_version do + unless ENV['VERSION'] + puts 'Must pass a VERSION=x.y.z release version' + exit + end + unless ENV['VERSION'] == VERS + puts "Please update your version.rb to match the release version, currently #{VERS}" + exit + end +end + +desc 'Install the package as a gem, without generating documentation(ri/rdoc)' +task :install_gem_no_doc => [:clean, :package] do + sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri" +end + +namespace :manifest do + desc 'Recreate Manifest.txt to include ALL files' + task :refresh do + `rake check_manifest | patch -p0 > Manifest.txt` + end +end \ No newline at end of file diff --git a/tasks/environment.rake b/tasks/environment.rake new file mode 100644 index 000000000..691ed3b65 --- /dev/null +++ b/tasks/environment.rake @@ -0,0 +1,7 @@ +task :ruby_env do + RUBY_APP = if RUBY_PLATFORM =~ /java/ + "jruby" + else + "ruby" + end unless defined? RUBY_APP +end diff --git a/tasks/website.rake b/tasks/website.rake new file mode 100644 index 000000000..93e03faa6 --- /dev/null +++ b/tasks/website.rake @@ -0,0 +1,17 @@ +desc 'Generate website files' +task :website_generate => :ruby_env do + (Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt| + sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} } + end +end + +desc 'Upload website files to rubyforge' +task :website_upload do + host = "#{rubyforge_username}@rubyforge.org" + remote_dir = "/var/www/gforge-projects/#{PATH}/" + local_dir = 'website' + sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}} +end + +desc 'Generate and upload website files' +task :website => [:website_generate, :website_upload, :publish_docs] diff --git a/test/test_annotate_models.rb b/test/test_annotate_models.rb new file mode 100644 index 000000000..628b94b65 --- /dev/null +++ b/test/test_annotate_models.rb @@ -0,0 +1,11 @@ +require File.dirname(__FILE__) + '/test_helper.rb' + +class TestAnnotateModels < Test::Unit::TestCase + + def setup + end + + def test_truth + assert true + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 000000000..30f83d987 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,2 @@ +require 'test/unit' +require File.dirname(__FILE__) + '/../lib/annotate_models' diff --git a/website/index.html b/website/index.html new file mode 100644 index 000000000..1a5da3bc3 --- /dev/null +++ b/website/index.html @@ -0,0 +1,93 @@ + + + + + + + annotate_models + + + + + + +
+ +

annotate_models

+
+

Get Version

+ 0.0.1 +
+

→ ‘annotate_models’

+ + +

What

+ + +

Installing

+ + +

sudo gem install annotate_models

+ + +

The basics

+ + +

Demonstration of usage

+ + +

Forum

+ + +

http://groups.google.com/group/annotate_models

+ + +

TODO – create Google Group – annotate_models

+ + +

How to submit patches

+ + +

Read the 8 steps for fixing other people’s code and for section 8b: Submit patch to Google Groups, use the Google Group above.

+ + +

The trunk repository is svn://rubyforge.org/var/svn/annotate_models/trunk for anonymous access.

+ + +

License

+ + +

This code is free to use under the terms of the MIT license.

+ + +

Contact

+ + +

Comments are welcome. Send an email to FIXME full name email via the forum

+

+ FIXME full name, 27th February 2008
+ Theme extended from Paul Battley +

+
+ + + + + diff --git a/website/index.txt b/website/index.txt new file mode 100644 index 000000000..2c699468b --- /dev/null +++ b/website/index.txt @@ -0,0 +1,39 @@ +h1. annotate_models + +h1. → 'annotate_models' + + +h2. What + + +h2. Installing + +
sudo gem install annotate_models
+ +h2. The basics + + +h2. Demonstration of usage + + + +h2. Forum + +"http://groups.google.com/group/annotate_models":http://groups.google.com/group/annotate_models + +TODO - create Google Group - annotate_models + +h2. How to submit patches + +Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above. + +The trunk repository is svn://rubyforge.org/var/svn/annotate_models/trunk for anonymous access. + +h2. License + +This code is free to use under the terms of the MIT license. + +h2. Contact + +Comments are welcome. Send an email to "FIXME full name":mailto:FIXME email via the "forum":http://groups.google.com/group/annotate_models + diff --git a/website/javascripts/rounded_corners_lite.inc.js b/website/javascripts/rounded_corners_lite.inc.js new file mode 100644 index 000000000..afc3ea327 --- /dev/null +++ b/website/javascripts/rounded_corners_lite.inc.js @@ -0,0 +1,285 @@ + + /**************************************************************** + * * + * curvyCorners * + * ------------ * + * * + * This script generates rounded corners for your divs. * + * * + * Version 1.2.9 * + * Copyright (c) 2006 Cameron Cooke * + * By: Cameron Cooke and Tim Hutchison. * + * * + * * + * Website: http://www.curvycorners.net * + * Email: info@totalinfinity.com * + * Forum: http://www.curvycorners.net/forum/ * + * * + * * + * This library is free software; you can redistribute * + * it and/or modify it under the terms of the GNU * + * Lesser General Public License as published by the * + * Free Software Foundation; either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the * + * implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU Lesser General Public * + * License for more details. * + * * + * You should have received a copy of the GNU Lesser * + * General Public License along with this library; * + * Inc., 59 Temple Place, Suite 330, Boston, * + * MA 02111-1307 USA * + * * + ****************************************************************/ + +var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners() +{ if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string") +{ var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);} +else +{ var startIndex = 1; var boxCol = arguments;} +var curvyCornersCol = new Array(); if(arguments[0].validTags) +var validElements = arguments[0].validTags; else +var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++) +{ var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false) +{ curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);} +} +this.objects = curvyCornersCol; this.applyCornersToAll = function() +{ for(var x = 0, k = this.objects.length; x < k; x++) +{ this.objects[x].applyCorners();} +} +} +function curvyObject() +{ this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderTopWidth", "border-top-width"); var borderColour = get_style(this.box, "borderTopColor", "border-top-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingTop", "padding-top"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0) +this.box.innerHTML = ""; this.applyCorners = function() +{ for(var t = 0; t < 2; t++) +{ switch(t) +{ case 0: +if(this.settings.tl || this.settings.tr) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);} +break; case 1: +if(this.settings.bl || this.settings.br) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);} +break;} +} +if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners) +{ if(i > -1 < 4) +{ var cc = corners[i]; if(!this.settings[cc]) +{ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) +{ var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "") +newCorner.style.backgroundColor = this.boxColour; else +newCorner.style.backgroundImage = this.backgroundImage; switch(cc) +{ case "tl": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px" +newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;} +} +} +else +{ if(this.masterCorners[this.settings[cc].radius]) +{ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);} +else +{ var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) +{ if((intx +1) >= borderRadius) +var y1 = -1; else +var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j) +{ if((intx) >= borderRadius) +var y2 = -1; else +var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j) +var y3 = -1; else +var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);} +if((intx) >= j) +var y4 = -1; else +var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j) +{ for(var inty = (y1 + 1); inty < y2; inty++) +{ if(this.settings.antiAlias) +{ if(this.backgroundImage != "") +{ var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30) +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);} +else +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);} +} +else +{ var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);} +} +} +if(this.settings.antiAlias) +{ if(y3 >= y2) +{ if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);} +} +else +{ if(y3 >= y1) +{ this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);} +} +var outsideColour = this.borderColour;} +else +{ var outsideColour = this.boxColour; var y3 = y1;} +if(this.settings.antiAlias) +{ for(var inty = (y3 + 1); inty < y4; inty++) +{ this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);} +} +} +this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);} +if(cc != "br") +{ for(var t = 0, k = newCorner.childNodes.length; t < k; t++) +{ var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";} +if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";} +switch(cc) +{ case "tr": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;} +} +} +} +if(newCorner) +{ switch(cc) +{ case "tl": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;} +} +} +} +var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius) +radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff) +{ if(z == "t" || z == "b") +{ if(radiusDiff[z]) +{ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px" +newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType) +{ case "tl": +newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr": +newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl": +newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br": +newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;} +} +var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z) +{ case "t": +if(this.topContainer) +{ if(this.settings.tl.radius && this.settings.tr.radius) +{ newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);} +this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";} +break; case "b": +if(this.bottomContainer) +{ if(this.settings.bl.radius && this.settings.br.radius) +{ newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);} +} +break;} +} +} +if(this.settings.autoPad == true && this.boxPadding > 0) +{ var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding) +contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding) +contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);} +} +this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) +{ var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "") +{ pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";} +else +{ pixel.style.backgroundColor = colour;} +if (transAmount != 100) +setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);} +} +function insertAfter(parent, node, referenceNode) +{ parent.insertBefore(node, referenceNode.nextSibling);} +function BlendColour(Col1, Col2, Col1Fraction) +{ var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);} +function IntToHex(strNum) +{ base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;} +function MakeHex(x) +{ if((x >= 0) && (x <= 9)) +{ return x;} +else +{ switch(x) +{ case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";} +} +} +function pixelFraction(x, y, r) +{ var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;} +switch (whatsides) +{ case "LeftRight": +pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight": +pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom": +pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom": +pixelfraction = (yvalues[0]*xvalues[1])/2; break; default: +pixelfraction = 1;} +return pixelfraction;} +function rgb2Hex(rgbColour) +{ try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);} +catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");} +return hexColour;} +function rgb2Array(rgbColour) +{ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;} +function setOpacity(obj, opacity) +{ opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME") +{ var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";} +else if(typeof(obj.style.opacity) != "undefined") +{ obj.style.opacity = opacity/100;} +else if(typeof(obj.style.MozOpacity) != "undefined") +{ obj.style.MozOpacity = opacity/100;} +else if(typeof(obj.style.filter) != "undefined") +{ obj.style.filter = "alpha(opacity:" + opacity + ")";} +else if(typeof(obj.style.KHTMLOpacity) != "undefined") +{ obj.style.KHTMLOpacity = opacity/100;} +} +function inArray(array, value) +{ for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;} +return false;} +function inArrayKey(array, value) +{ for(key in array){ if(key === value) return true;} +return false;} +function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;} +else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;} +else { elm['on' + evType] = fn;} +} +function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");} +} +function format_colour(colour) +{ var returnColour = "#ffffff"; if(colour != "" && colour != "transparent") +{ if(colour.substr(0, 3) == "rgb") +{ returnColour = rgb2Hex(colour);} +else if(colour.length == 4) +{ returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);} +else +{ returnColour = colour;} +} +return returnColour;} +function get_style(obj, property, propertyNS) +{ try +{ if(obj.currentStyle) +{ var returnVal = eval("obj.currentStyle." + property);} +else +{ if(isSafari && obj.style.display == "none") +{ obj.style.display = ""; var wasHidden = true;} +var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden) +{ obj.style.display = "none";} +} +} +catch(e) +{ } +return returnVal;} +function getElementsByClass(searchClass, node, tag) +{ var classElements = new Array(); if(node == null) +node = document; if(tag == null) +tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++) +{ if(pattern.test(els[i].className)) +{ classElements[j] = els[i]; j++;} +} +return classElements;} +function newCurvyError(errorMessage) +{ return new Error("curvyCorners Error:\n" + errorMessage) +} diff --git a/website/stylesheets/screen.css b/website/stylesheets/screen.css new file mode 100644 index 000000000..2c84cd00c --- /dev/null +++ b/website/stylesheets/screen.css @@ -0,0 +1,138 @@ +body { + background-color: #E1D1F1; + font-family: "Georgia", sans-serif; + font-size: 16px; + line-height: 1.6em; + padding: 1.6em 0 0 0; + color: #333; +} +h1, h2, h3, h4, h5, h6 { + color: #444; +} +h1 { + font-family: sans-serif; + font-weight: normal; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin: 5px; +} +li { + padding: 0; + margin: 0; + list-style-type: square; +} +a { + color: #5E5AFF; + background-color: #DAC; + font-weight: normal; + text-decoration: underline; +} +blockquote { + font-size: 90%; + font-style: italic; + border-left: 1px solid #111; + padding-left: 1em; +} +.caps { + font-size: 80%; +} + +#main { + width: 45em; + padding: 0; + margin: 0 auto; +} +.coda { + text-align: right; + color: #77f; + font-size: smaller; +} + +table { + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; + border-style: dashed; +} + +th { + color: #fff; +} + +td { + padding: 2px 10px 2px 10px; +} + +.success { + color: #0CC52B; +} + +.failed { + color: #E90A1B; +} + +.unknown { + color: #995000; +} +pre, code { + font-family: monospace; + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; +} +.comment { color: #aaa; font-style: italic; } +.keyword { color: #eff; font-weight: bold; } +.punct { color: #eee; font-weight: bold; } +.symbol { color: #0bb; } +.string { color: #6b4; } +.ident { color: #ff8; } +.constant { color: #66f; } +.regex { color: #ec6; } +.number { color: #F99; } +.expr { color: #227; } + +#version { + float: right; + text-align: right; + font-family: sans-serif; + font-weight: normal; + background-color: #B3ABFF; + color: #141331; + padding: 15px 20px 10px 20px; + margin: 0 auto; + margin-top: 15px; + border: 3px solid #141331; +} + +#version .numbers { + display: block; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin-bottom: 15px; +} + +#version p { + text-decoration: none; + color: #141331; + background-color: #B3ABFF; + margin: 0; + padding: 0; +} + +#version a { + text-decoration: none; + color: #141331; + background-color: #B3ABFF; +} + +.clickable { + cursor: pointer; + cursor: hand; +} + diff --git a/website/template.rhtml b/website/template.rhtml new file mode 100644 index 000000000..e8517350d --- /dev/null +++ b/website/template.rhtml @@ -0,0 +1,48 @@ + + + + + + + <%= title %> + + + + + + +
+ +

<%= title %>

+
+

Get Version

+ <%= version %> +
+ <%= body %> +

+ FIXME full name, <%= modified.pretty %>
+ Theme extended from Paul Battley +

+
+ + + + +