├── var ├── title ├── version ├── created ├── organizations ├── summary ├── authors ├── copyrights ├── description ├── repositories ├── requirements └── resources ├── lib ├── gemdo.yml ├── gemdo │ ├── project │ │ ├── files.rb │ │ ├── manifest.rb │ │ ├── news.rb │ │ └── paths.rb │ ├── core_ext │ │ ├── empty.rb │ │ ├── to_list.rb │ │ ├── try_dup.rb │ │ └── pathname.rb │ ├── error.rb │ ├── core_ext.rb │ ├── cli │ │ ├── about.rb │ │ ├── show.rb │ │ ├── news.rb │ │ ├── spec.rb │ │ ├── base.rb │ │ ├── gemspec.rb │ │ ├── resolve.rb │ │ ├── bump.rb │ │ └── init.rb │ └── cli.rb └── gemdo.rb ├── demo ├── fixtures │ ├── complete │ │ ├── .ruby │ │ ├── myapp.gemspec │ │ ├── README │ │ ├── HISTORY │ │ └── INDEX.yml │ └── empty │ │ └── .ruby ├── applique │ ├── gemdo.rb │ ├── ae.rb │ └── fixtures.rb └── 1project │ ├── 1overview.md │ ├── 2readme.md │ ├── 3history.md │ ├── 5manifest.md │ └── 4news.md ├── work ├── benchmarks │ ├── example │ │ ├── name │ │ ├── version │ │ └── loadpath │ ├── example.yaml │ └── bm_yaml_vs_files.rb ├── deprecated │ ├── test │ │ ├── news.rb │ │ └── version.rb │ ├── gemfile-refit │ │ ├── rubygems_plugin.rb │ │ ├── Gemfile │ │ └── bundler.rb │ ├── rubyforge │ │ └── rubyforge.rb │ ├── spun-off │ │ ├── resolver │ │ │ ├── source.rb │ │ │ ├── gemcutter.rb │ │ │ └── rubygems.rb │ │ ├── readme.rdoc │ │ ├── history.rdoc │ │ ├── history.rb │ │ ├── readme.rb │ │ └── resolver.rb │ ├── profile │ │ ├── template.erb │ │ ├── profile.rdoc │ │ ├── save.rb │ │ ├── bundlerable.rb │ │ └── inference.rb │ ├── resources.rdoc │ ├── property.rb │ ├── alt_version │ │ ├── version_helper.rb │ │ └── version_file.rb │ ├── requires.rdoc │ ├── rubyfile.rb │ ├── gemspec.rb │ └── metastore.rb ├── ideas │ ├── per-field-metadata │ │ └── metadata │ │ │ ├── contact.rb │ │ │ ├── license.rb │ │ │ ├── copyright.rb │ │ │ ├── description.rb │ │ │ ├── title.rb │ │ │ ├── authors.rb │ │ │ ├── suite.rb │ │ │ ├── name.rb │ │ │ ├── custom.rb │ │ │ ├── summary.rb │ │ │ ├── version.rb │ │ │ ├── loadpath.rb │ │ │ ├── created.rb │ │ │ ├── abstract.rb │ │ │ ├── email.rb │ │ │ └── released.rb │ └── version-styles │ │ ├── jeweler_style.rb │ │ ├── pom_style.rb │ │ └── jpom_style.rb ├── reference │ ├── REQUIRE.jruby │ └── ProjectInfo ├── resolver.rb ├── trash │ ├── blog │ │ ├── 2010-05-05-resources-subdirectory.rdoc │ │ ├── 2010-01-17-wikis_make_the_best_user_manuals.rdoc │ │ ├── 2010-01-16-build-metadata.rdoc │ │ ├── 2010-07-23-individual-requirement-files.md │ │ ├── 2010-05-12-directory-store.rdoc │ │ ├── 2010-06-11-version-and-release-files.rdoc │ │ ├── 2010-07-22-final-designs.md │ │ ├── 2010-07-27-no-version-necessary.md │ │ ├── 2010-07-24-seeking-perfection.md │ │ └── 2010-07-21-finding-root.md │ ├── root.rb │ └── needs.rb ├── .ruby.old └── consider │ ├── build.rb │ ├── 02_cli │ ├── bump.md │ └── init.md │ ├── profile.rdoc │ └── version.rb ├── bin ├── gemdo ├── gemdo-init ├── gemdo-show └── gemdo-about ├── site ├── assets │ └── images │ │ ├── icon.jpg │ │ ├── logo.jpg │ │ ├── meta.jpg │ │ ├── intro.jpg │ │ └── important.png ├── .rsync-filter ├── blog │ └── 2010-05-24-requirements │ │ ├── gemfile.page │ │ └── index.post └── index.html ├── .gitignore ├── Rakefile ├── man └── pom.ronn ├── LICENSE.txt ├── MANIFEST ├── Assembly ├── .index ├── README.md └── HISTORY.md /var/title: -------------------------------------------------------------------------------- 1 | POM 2 | -------------------------------------------------------------------------------- /lib/gemdo.yml: -------------------------------------------------------------------------------- 1 | ../.index -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 2.2.0 2 | -------------------------------------------------------------------------------- /demo/fixtures/complete/.ruby: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/fixtures/empty/.ruby: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gemdo/project/files.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2009-07-22 2 | -------------------------------------------------------------------------------- /work/benchmarks/example/name: -------------------------------------------------------------------------------- 1 | Fred 2 | -------------------------------------------------------------------------------- /work/deprecated/test/news.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /demo/applique/gemdo.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | -------------------------------------------------------------------------------- /var/organizations: -------------------------------------------------------------------------------- 1 | --- 2 | - Rubyworks 3 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Ruby Project Object Model 2 | -------------------------------------------------------------------------------- /work/benchmarks/example/version: -------------------------------------------------------------------------------- 1 | 1.0.0 2 | -------------------------------------------------------------------------------- /work/benchmarks/example/loadpath: -------------------------------------------------------------------------------- 1 | lib 2 | ext 3 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Trans 3 | 4 | -------------------------------------------------------------------------------- /demo/applique/ae.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | require 'ae/should' 3 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - 2009 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | POM provides an API for the typical Ruby project. 2 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/pom.git 3 | -------------------------------------------------------------------------------- /work/deprecated/gemfile-refit/rubygems_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo/bundler' 2 | -------------------------------------------------------------------------------- /bin/gemdo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gemdo/cli' 3 | GemDo::CLI.run 4 | 5 | -------------------------------------------------------------------------------- /bin/gemdo-init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gemdo/cli/init' 3 | GemDo::CLI::Init.run 4 | -------------------------------------------------------------------------------- /bin/gemdo-show: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gemdo/cli/show' 3 | GemDo::CLI::Show.run 4 | -------------------------------------------------------------------------------- /bin/gemdo-about: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'gemdo/cli/about' 3 | GemDo::CLI::About.run 4 | -------------------------------------------------------------------------------- /site/assets/images/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/gemdo/master/site/assets/images/icon.jpg -------------------------------------------------------------------------------- /site/assets/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/gemdo/master/site/assets/images/logo.jpg -------------------------------------------------------------------------------- /site/assets/images/meta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/gemdo/master/site/assets/images/meta.jpg -------------------------------------------------------------------------------- /work/benchmarks/example.yaml: -------------------------------------------------------------------------------- 1 | name: fred 2 | version: 1.0.0 3 | loadpath: [lib, ext] 4 | date: 2010-01-01 5 | -------------------------------------------------------------------------------- /site/assets/images/intro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/gemdo/master/site/assets/images/intro.jpg -------------------------------------------------------------------------------- /site/assets/images/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/gemdo/master/site/assets/images/important.png -------------------------------------------------------------------------------- /site/.rsync-filter: -------------------------------------------------------------------------------- 1 | - .svn 2 | - scrap 3 | P wiki 4 | P robot.txt 5 | P robots.txt 6 | P statcvs 7 | P statsvn 8 | P usage 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ergo 2 | .rdoc 3 | .yardoc 4 | /log 5 | /pkg 6 | /tmp 7 | /web 8 | site/docs 9 | work/sandbox 10 | *.lock 11 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/contact.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Contact < String 4 | include AbstractField 5 | end 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/license.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class License < String 4 | include AbstractField 5 | end 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | desc 'run tests' 4 | task :test do 5 | # TODO: replace with `ko/rake` when ready 6 | system "ko -Ilib test/*.rb" 7 | end 8 | 9 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/copyright.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Copyright < String 4 | include AbstractField 5 | end 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/description.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Description < String 4 | include AbstractField 5 | end 6 | 7 | end 8 | 9 | -------------------------------------------------------------------------------- /lib/gemdo/core_ext/empty.rb: -------------------------------------------------------------------------------- 1 | class NilClass 2 | # Returns true. 3 | #-- 4 | # TODO: Is this okay? 5 | #++ 6 | def empty? 7 | true 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - indexer 3 | - versus 4 | - readme 5 | - history 6 | - mast 7 | - facets 2.8+ 8 | - detroit (build) 9 | - citron (test) 10 | - qed 2.2+ (test) 11 | 12 | 13 | -------------------------------------------------------------------------------- /demo/1project/1overview.md: -------------------------------------------------------------------------------- 1 | # Project Class 2 | 3 | Almost all use of POM will be via the Project class. This class encapsulates 4 | all the other aspects of the system from metdata to standard project paths. 5 | 6 | -------------------------------------------------------------------------------- /lib/gemdo/error.rb: -------------------------------------------------------------------------------- 1 | module GemDo 2 | 3 | # Error tag module. 4 | module Error 5 | end 6 | 7 | # 8 | class ProjectNotFound < RuntimeError 9 | include Error 10 | end 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /lib/gemdo/core_ext.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo/core_ext/pathname' 2 | require 'gemdo/core_ext/to_list' 3 | require 'gemdo/core_ext/empty' 4 | 5 | require 'facets/string/unfold' 6 | require 'facets/hash/rekey' 7 | require 'facets/string/tabto' # TODO: has this been ranamed? 8 | 9 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/title.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Title < String 4 | 5 | def self.default(metadata) 6 | metadata.name.capitalize 7 | end 8 | 9 | include AbstractField 10 | 11 | end 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/authors.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Authors < Array 4 | 5 | include AbstractField 6 | 7 | # 8 | def initialize(list) 9 | replace([list].flatten) 10 | end 11 | 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/suite.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | # 4 | class Suite < String 5 | 6 | def self.aliases 7 | ['suite', 'collection', 'organization'] 8 | end 9 | 10 | include AbstractField 11 | 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /demo/fixtures/complete/myapp.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = %q{myapp} 3 | s.version = "1.0.0" 4 | s.authors = ["Tom Sawyer"] 5 | s.date = %q{2010-10-10} 6 | s.description = %q{This is a description of a fake project.} 7 | s.email = %q{transfire@gmail.com} 8 | end -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/name.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Name < String 4 | 5 | def self.store 6 | "package.yml" 7 | end 8 | 9 | include AbstractField 10 | 11 | def required? 12 | true 13 | end 14 | 15 | end 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /demo/1project/2readme.md: -------------------------------------------------------------------------------- 1 | ## Project Readme 2 | 3 | Given a complete project example, a Project class insteance should be able to 4 | access the readme file. 5 | 6 | project = POM::Project.new('example') 7 | 8 | readme = project.readme 9 | 10 | readme.assert.is_a?(::Readme) 11 | 12 | -------------------------------------------------------------------------------- /lib/gemdo/core_ext/to_list.rb: -------------------------------------------------------------------------------- 1 | class NilClass 2 | def to_list 3 | [] 4 | end 5 | end 6 | 7 | class String 8 | def to_list 9 | split(/[:;,\n]/).map(&:strip) 10 | end 11 | end 12 | 13 | module Enumerable 14 | def to_list 15 | [to_a].flatten.compact 16 | end 17 | end 18 | 19 | -------------------------------------------------------------------------------- /demo/1project/3history.md: -------------------------------------------------------------------------------- 1 | ## Project History 2 | 3 | Given a complete project example, a Project class insteance should be able to 4 | access the history file. 5 | 6 | project = POM::Project.new('example') 7 | 8 | history = project.history 9 | 10 | history.assert.is_a?(::History) 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/fixtures/complete/README: -------------------------------------------------------------------------------- 1 | # MyApp 2 | 3 | * http://some.org 4 | 5 | ## DESCRIPTION 6 | 7 | This is a description of the 8 | fake project. 9 | 10 | # INSTALL 11 | 12 | To install use RubyGems 13 | 14 | $ gem install myapp 15 | 16 | # LICENSE 17 | 18 | (GPL) 19 | 20 | Copyright 2009 Thomas Sawyer 21 | -------------------------------------------------------------------------------- /demo/1project/5manifest.md: -------------------------------------------------------------------------------- 1 | ## Project Manifest 2 | 3 | Given a complete project example, a Project class insteance should be able to 4 | access the manifest file. 5 | 6 | project = POM::Project.new('example') 7 | 8 | manifest = project.manifest 9 | 10 | manifest.assert.is_a?(POM::Project::Manifest) 11 | 12 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home : http://rubyworks.github.com/pom 3 | work : http://github.com/rubyworks/pom 4 | docs : http://wiki.github.com/rubyworks/pom 5 | wiki : http://wiki.github.com/rubyworks/pom 6 | api : http://rubdoc.info/gems/pom/frames 7 | mail : http://groups.google.com/group/rubyworks-mailinglist 8 | forum : http://groups.google.com/group/rubyworks-mailinglist 9 | 10 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/custom.rb: -------------------------------------------------------------------------------- 1 | require 'delegate' 2 | 3 | class Rock::Metadata 4 | 5 | # 6 | class Custom < SimpleDelegator 7 | 8 | #extend AbstractField 9 | 10 | #def self.name 11 | # 'summary' 12 | #end 13 | 14 | #def self.store 15 | # 'yaml:profile.yml' 16 | #end 17 | 18 | def to_data 19 | to_s 20 | end 21 | 22 | end 23 | 24 | end 25 | 26 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/summary.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | # 4 | class Summary < String 5 | 6 | # 7 | def self.default(metadata) 8 | p metadata.description.to_s.strip 9 | d = metadata.description.to_s.strip 10 | i = d.index(/(\.|$)/) 11 | i = 69 if i > 69 12 | d[0..i] 13 | end 14 | 15 | # 16 | include AbstractField 17 | end 18 | 19 | end 20 | 21 | -------------------------------------------------------------------------------- /work/reference/REQUIRE.jruby: -------------------------------------------------------------------------------- 1 | requires: 2 | - arel 0.3.3+ vendor/arel 3 | - rake 0.8.7+ 4 | - mocha 0.9.8+ 5 | - ruby-debug 6 | - jruby-openssl 7 | - nokogiri 1.4.0+ 8 | # AP 9 | - rack-test 0.5.3 10 | - RedCloth 4.2.2+ 11 | # AR 12 | - activerecord-jdbcsqlite3-adapter 13 | - activerecord-jdbcmysql-adapter 14 | - activerecord-jdbcpostgresql-adapter 15 | 16 | documentation: 17 | - rdoc 2.1 18 | 19 | testing: 20 | - rspec 21 | 22 | -------------------------------------------------------------------------------- /lib/gemdo/core_ext/try_dup.rb: -------------------------------------------------------------------------------- 1 | class Object 2 | alias_method :try_dup, :dup 3 | end 4 | 5 | class NilClass 6 | def try_dup 7 | self 8 | end 9 | end 10 | 11 | class Symbol 12 | def try_dup 13 | self 14 | end 15 | end 16 | 17 | class TrueClass 18 | def try_dup 19 | self 20 | end 21 | end 22 | 23 | class FalseClass 24 | def try_dup 25 | self 26 | end 27 | end 28 | 29 | class Numeric 30 | def try_dup 31 | self 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/version.rb: -------------------------------------------------------------------------------- 1 | require 'rock/version' 2 | 3 | class Rock::Metadata 4 | 5 | class Version < Rock::VersionNumber 6 | 7 | def self.aliases 8 | ['vers'] 9 | end 10 | 11 | def self.store 12 | "package.yml" 13 | end 14 | 15 | include AbstractField 16 | 17 | #def validate 18 | # /^\d/ =~ value 19 | #end 20 | 21 | def to_data 22 | to_s 23 | end 24 | 25 | end 26 | 27 | end 28 | 29 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/loadpath.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | class Loadpath < Array 4 | 5 | def self.default(metadata) 6 | ['lib'] 7 | end 8 | 9 | include AbstractField 10 | 11 | # 12 | def initialize(path) 13 | case paths 14 | when NilClass 15 | replace ['lib'] 16 | when String 17 | replace paths.split(/[,:;\ ]/) 18 | else 19 | replace [paths].flatten 20 | end 21 | end 22 | 23 | end 24 | 25 | end 26 | 27 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/created.rb: -------------------------------------------------------------------------------- 1 | require 'date' 2 | 3 | class Rock::Metadata 4 | 5 | # 6 | class Created < Date 7 | 8 | # TODO: More efficeint convervion of Date value? 9 | def self.new(value) 10 | case value 11 | when Date, Time, DateTime 12 | parse(value.to_s) 13 | else 14 | parse(value) 15 | end 16 | end 17 | 18 | include AbstractField 19 | 20 | def to_data 21 | self #strftime('%Y-%m-%d') 22 | end 23 | 24 | end 25 | 26 | end 27 | 28 | -------------------------------------------------------------------------------- /lib/gemdo.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo/project' 2 | 3 | module GemDo 4 | # Where in project to store backups. 5 | # TODO: Move to Config. 6 | BACKUP_DIRECTORY = '.cache/gemdo' 7 | 8 | # 9 | def self.metadata 10 | @metadata ||= ( 11 | require 'yaml' 12 | file = File.dirname(__FILE__) + "/gemdo.yml" 13 | YAML.load(File.new(file)) 14 | ) 15 | end 16 | 17 | # Access to project metadata, e.g. VERSION. 18 | def self.const_missing(name) 19 | metadata[name.to_s.downcase] || super(name) 20 | end 21 | end 22 | 23 | 24 | -------------------------------------------------------------------------------- /work/deprecated/rubyforge/rubyforge.rb: -------------------------------------------------------------------------------- 1 | class POM::Metadata 2 | 3 | def rubyforge 4 | @data['rubyforge'] ||= Rubyforge.new(self, 'rubyforge') 5 | end 6 | 7 | # 8 | class Rubyforge < POM::FileStore 9 | 10 | # 11 | attr_accessor :unixname, :default => lambda{ parent.suite } 12 | 13 | # 14 | attr_accessor :groupid 15 | 16 | # S P E C I A L S E T T E R S 17 | 18 | # 19 | def groupid=(id) 20 | raise ValidationError unless /\d+/ =~ id 21 | self['groupid'] = id 22 | end 23 | 24 | end 25 | 26 | end 27 | 28 | -------------------------------------------------------------------------------- /demo/fixtures/complete/HISTORY: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 1.2.1 / 2010-10-18 4 | 5 | 1. This is change 1. 6 | 2. This is change 2. 7 | 3. This is change 3. 8 | 9 | Some Dandy description of the 1.2.1 release. 10 | Notice this time that the changes are listed 11 | first and are numerically enumerated. 12 | 13 | ## 1.1.0 / 2010-01-01 14 | 15 | 1. This is change 1. 16 | 2. This is change 2. 17 | 3. This is change 3. 18 | 19 | Some Dandy description of the 1.1.0 release. 20 | Notice this time that the changes are listed 21 | first and are numerically enumerated. 22 | -------------------------------------------------------------------------------- /work/resolver.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'open-uri' 3 | #require 'json' 4 | require 'yaml' 5 | 6 | #gems = Marshal.load(Gem.inflate(open("http://rubygems.org/Marshal.4.8.Z").read)) 7 | require 'gemdo' 8 | 9 | project = Gemdo::Project.new(Dir.pwd) 10 | 11 | list = [] 12 | 13 | p project.requires 14 | 15 | project.requires.each do |req| 16 | dir = File.expand_path('~/.gem/specs/rubygems.org%80/quick/Marshal.4.8/') 17 | libs = Dir[File.join(dir, "#{req.name}-*")] 18 | 19 | p libs 20 | end 21 | 22 | p list 23 | 24 | #puts gems.size 25 | 26 | #File.open('GEMS.yml', 'w'){ |f| f << gems.to_yaml } 27 | 28 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/abstract.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | # 4 | module AbstractField 5 | 6 | def self.included(base) 7 | base.extend Meta 8 | Rock::Metadata.register(base) 9 | end 10 | 11 | module Meta 12 | def name 13 | super.split('::').last.downcase 14 | end 15 | 16 | def aliases 17 | [] 18 | end 19 | 20 | def names 21 | [name, *aliases] 22 | end 23 | 24 | def store 25 | 'profile.yml' 26 | end 27 | end 28 | 29 | def to_data 30 | to_s 31 | end 32 | 33 | end 34 | 35 | end 36 | 37 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/email.rb: -------------------------------------------------------------------------------- 1 | class Rock::Metadata 2 | 3 | # Contact's email address. 4 | class EMail < String 5 | 6 | # Regular expression for matching valid email addresses. 7 | RE_EMAIL = /\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i #/<.*?>/ 8 | 9 | # 10 | def self.store 11 | "yaml:profile.yml" 12 | end 13 | 14 | # 15 | def self.default(metadata) 16 | contact = metadata.contact 17 | if md = RE_EMAIL.match(contact.to_s) 18 | md[0] 19 | else 20 | nil 21 | end 22 | end 23 | 24 | # 25 | include AbstractField 26 | 27 | end 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /work/trash/blog/2010-05-05-resources-subdirectory.rdoc: -------------------------------------------------------------------------------- 1 | = 2010-05-05 | Resourcess Subdirectory 2 | 3 | To subdirectory or not to subdirectory? That is the question. Whether it is better 4 | to separate relavent sets of information into their own little area of concern or 5 | to leave them adrift within all the other metadata, it is a difficult choice. 6 | On the one hand, the simplicity of a single depth store is very attractive, on the 7 | other the sepration of concerns leads to a cleaner layout. And then I must ask, 8 | how far does it go? Does separating "requirements", like +requires+, +provides+, 9 | and so on, into a separate subdirectory, make more sense as well? 10 | 11 | -------------------------------------------------------------------------------- /work/trash/blog/2010-01-17-wikis_make_the_best_user_manuals.rdoc: -------------------------------------------------------------------------------- 1 | == 2010-01-17 | Wiki's Make the Best User Manuals 2 | 3 | Today I have finally decided that a Wiki is the best place 4 | to keep user documentation. There is of course the obvious 5 | advantage to this: online access, editable by any user, anywhere 6 | at anytime. There are some subtle advantages as well. For instance, 7 | I get automatic spell-checking via my browser. 8 | 9 | Of course the big disadvantage is that should the Wiki Host 10 | go kaputz, then no more documentation. I trust GitHub is not 11 | going anywhere anytime soon, but I have had such a thing happen 12 | to me before so I will be sure to look into making backups just in case. 13 | 14 | -------------------------------------------------------------------------------- /work/ideas/per-field-metadata/metadata/released.rb: -------------------------------------------------------------------------------- 1 | require 'date' 2 | 3 | class Rock::Metadata 4 | 5 | # 6 | class Released < Date 7 | 8 | # TODO: More efficeint convervion of Date value? 9 | def self.new(value) 10 | case value 11 | when Date, Time, DateTime 12 | parse(value.to_s) 13 | else 14 | parse(value) 15 | end 16 | end 17 | 18 | def self.name 19 | 'date' 20 | end 21 | 22 | def self.aliases 23 | ['released'] 24 | end 25 | 26 | def self.store 27 | "package.yml" 28 | end 29 | 30 | include AbstractField 31 | 32 | def to_data 33 | self #strftime('%Y-%m-%d') 34 | end 35 | 36 | end 37 | 38 | end 39 | 40 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/resolver/source.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | class Resolver 4 | 5 | # Resolver source base class. 6 | class Source 7 | 8 | # List all possible dependencies for a given gem +name+, or 9 | # all gems if no +name+ is specified. 10 | def dependencies(name=nil) 11 | deps = [] 12 | if name 13 | @cache[name].each do |gem| 14 | deps.concat(gem.dependencies) 15 | end 16 | else 17 | @cache.each do |name, gems| 18 | gems.each do |gem| 19 | deps.concat(gem.dependencies) 20 | end 21 | end 22 | end 23 | deps.uniq 24 | end 25 | 26 | end 27 | 28 | end 29 | 30 | end 31 | 32 | -------------------------------------------------------------------------------- /lib/gemdo/cli/about.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | 6 | # The +about+ command produces a simply console 7 | # printout of general information about a project. 8 | class About < Base 9 | 10 | # 11 | def parse 12 | parser = OptionParser.new do |opt| 13 | opt.banner = "pom about" 14 | 15 | opt.on("--debug", "run in debug mode") do 16 | $DEBUG = true 17 | $VERBOSE = true 18 | end 19 | 20 | opt.on_tail("--help", "-h", "display this help message") do 21 | puts opt 22 | exit 23 | end 24 | end 25 | 26 | parser.parse! 27 | end 28 | 29 | # 30 | def execute 31 | puts project.about 32 | end 33 | 34 | end 35 | 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /work/benchmarks/bm_yaml_vs_files.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | 3 | def load_yaml 4 | YAML.load(File.new("example.yaml")) 5 | end 6 | 7 | def load_files 8 | data = {} 9 | data['name'] = File.read('example/name') 10 | data['version'] = File.read('example/version') 11 | data['loadpath'] = File.read('example/loadpath').strip.split(/\n/) 12 | data 13 | end 14 | 15 | count = 10000 16 | 17 | puts 18 | puts "YAML:" 19 | 20 | Benchmark.bm(25) do |x| 21 | x.report(" Require YAML: "){ require 'yaml' } 22 | end 23 | 24 | Benchmark.bm(25) do |x| 25 | x.report(" YAML: "){ count.times{ load_yaml } } 26 | end 27 | 28 | puts 29 | puts "Files:" 30 | 31 | Benchmark.bm(25) do |x| 32 | x.report(" Files: "){ count.times{ load_files } } 33 | end 34 | 35 | -------------------------------------------------------------------------------- /demo/fixtures/complete/INDEX.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: demoapp 3 | version: 1.0.0 4 | 5 | title: DemoApp 6 | summary: Demo summary 7 | 8 | description: 9 | This is the description for the example. 10 | 11 | requirements: 12 | - rake 0.8.7+ 13 | - mocha 0.9.8+ 14 | - nokogiri 1.4.0+ 15 | - system_timer 16 | - ruby-debug 0.10.3+ 17 | - json 18 | - yajl-ruby 19 | - rack-test 0.5.3 (ap) 20 | - RedCloth 4.2.2+ (ap) 21 | - sqlite3-ruby 1.3.0.beta.2 (ar) 22 | - fcgi 0.8.7+ (optional) # does not compile on mri 1.9+ 23 | - rake (build development) 24 | - rdoc 2.1 (doc development) 25 | - rspec (test development) 26 | 27 | authors: 28 | - trans 29 | 30 | copyrights: 31 | - 2012 Copyright (c) 2010 Thomas Sawyer (MIT) 32 | 33 | created: 2010-10-10 34 | -------------------------------------------------------------------------------- /work/deprecated/profile/template.erb: -------------------------------------------------------------------------------- 1 | --- 2 | title: <%= title %> 3 | version: <%= version %> 4 | 5 | requirements: 6 | - <%= requirements.join("\n- ") %> 7 | 8 | summary: <%= summary %> 9 | contact: <%= contact %> 10 | created: <%= Time.now.strftime('%Y-%d-%m') %> 11 | 12 | description: 13 | <%= description.tabto(2) %> 14 | 15 | authors: 16 | - <%= authors.join("\n- ") %> 17 | 18 | <% unless resources.empty? -%> 19 | resources: 20 | - <%= resources.map{|*a| "%8s: %s" % a}.join("\n- ") %> 21 | <% end -%> 22 | 23 | <% unless repositories.empty? -%> 24 | repositories: 25 | - <%= repositories.map{|*a| "%8s: %s" % a}.join("\n- ") %> 26 | <% end -%> 27 | 28 | organization : <%= organization %> 29 | copyright : Copyright (c) <%= Time.now.year %> Thomas Sawyer 30 | 31 | <% unless licenses.empty? %> 32 | licenses: 33 | - <%= licenses.join("\n- ") %> 34 | <% end %> 35 | -------------------------------------------------------------------------------- /work/ideas/version-styles/jeweler_style.rb: -------------------------------------------------------------------------------- 1 | class POM::Package 2 | 3 | # Jeweler style VERSION file, e.g. 4 | # 5 | # --- 6 | # :major: 1 7 | # :minor: 0 8 | # :patch: 0 9 | # :build: pre.1 10 | # 11 | module JewelerStyle 12 | 13 | # 14 | def self.match?(data) 15 | return false unless Hash === data 16 | data = data.inject({}){|h,(k,v)| h[k.to_sym]=v; h} 17 | keys = data.keys - [:major, :minor, :patch, :build] 18 | keys.empty? 19 | end 20 | 21 | # 22 | def render 23 | ":major: #{@segments[0]}\n" + 24 | ":minor: #{@segments[1]}\n" + 25 | ":patch: #{@segments[2]}\n" + 26 | ":build: #{@segments[3..-1].join('.')}\n" 27 | end 28 | 29 | # 30 | def parse(data) 31 | data = data.inject({}){|h,(k,v)| h[k.to_sym]=v; h} 32 | self.version = data.values_at(:major,:minor,:patch,:build).compact 33 | end 34 | 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /lib/gemdo/cli/show.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | ## 6 | # Show a given setting of the project's metadata. 7 | # 8 | class Show < Base 9 | # 10 | attr :entry 11 | 12 | # 13 | def parse 14 | parser = OptionParser.new do |opt| 15 | opt.banner = "gemdo show [ENTRY]" 16 | 17 | opt.on("--debug", "run in debug mode") do 18 | $DEBUG = true 19 | $VERBOSE = true 20 | end 21 | 22 | opt.on_tail("--help", "-h", "display this help message") do 23 | puts opt 24 | exit 25 | end 26 | end 27 | 28 | parser.parse! 29 | 30 | @entry = ARGV.last 31 | end 32 | 33 | # 34 | def execute 35 | if entry 36 | puts project.profile[entry] 37 | else 38 | vars = project.profile.attributes 39 | puts vars.sort.join(' ') 40 | end 41 | end 42 | 43 | end 44 | end 45 | end 46 | 47 | -------------------------------------------------------------------------------- /work/trash/blog/2010-01-16-build-metadata.rdoc: -------------------------------------------------------------------------------- 1 | = 2010-01-16 | Build Metadata 2 | 3 | Debated a long time about what to do about "build metadata". 4 | At issue is the fact that it is almost entirely information that 5 | the end-user never needs to see, so it does not need to be 6 | shipped with the package(s), as regular metadata generally 7 | does. So this build data doesn't belong in the same location 8 | as the regular metadata --with one exception. Build requirements 9 | to actually compile and install the pacakge are needed. 10 | 11 | After much consideration I have decided that such build requirements 12 | should be included in the regular requirements field when 13 | they are needed to compile and/or install. 14 | 15 | As for the rest, I have for now at least declared a YAGNI. 16 | The build fields would only act as defaults for other tools in 17 | anycase. One might as well provide the configuration settings 18 | for the tools themselves (e.g. setting them in a Rakefile). 19 | 20 | -------------------------------------------------------------------------------- /man/pom.ronn: -------------------------------------------------------------------------------- 1 | ruby-pom(1) - ruby project model tool 2 | ===================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `ruby-pom [] [...]` 7 | 8 | 9 | ## DESCRIPTION 10 | 11 | Ruby Project Model tool provides a set of utility methods for Ruby 12 | projects based on the Ruby Project Object Model. 13 | 14 | 15 | ## COMMANDS 16 | 17 | * `about` 18 | Output an overview of the project. 19 | 20 | * `show [name]`: 21 | Show specific information about project. 22 | 23 | * `news`: 24 | How the news or latest release history entry. 25 | 26 | * `bump`: 27 | Bump version number and output. 28 | 29 | * `init`: 30 | Create a simply starter Ruby project. 31 | 32 | * `help` 33 | Display this help message. 34 | 35 | 36 | ## OPTIONS 37 | 38 | * `-h`, `--help`: 39 | Same as using `help` command. 40 | 41 | * `--debug`: 42 | Run command with Ruby's $DEBUG flag set to `true`. 43 | 44 | 45 | ## SEE ALSO 46 | 47 | ruby-pom-about(1), ruby-pom-show(1), ruby-pom-news(1), ruby-pom-bump(1), ruby-pom-init(1) 48 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/readme.rdoc: -------------------------------------------------------------------------------- 1 | = Parse README file 2 | 3 | Given a README project file containing ... 4 | 5 | = MyApp 6 | 7 | * http://some.org 8 | 9 | == DESCRIPTION 10 | 11 | This is a description of the 12 | fake project. 13 | 14 | = INSTALL 15 | 16 | To install use RubyGems 17 | 18 | $ gem install myapp 19 | 20 | = LICENSE 21 | 22 | (GPL) 23 | 24 | Copyright 2009 Thomas Sawyer 25 | 26 | Load the POM::Readme library. 27 | 28 | require 'pom/readme' 29 | 30 | Create a new Readme object from file. 31 | 32 | rm = POM::Readme.load("tmp/example/README") 33 | 34 | It should be able to parse out a title and project name. 35 | 36 | rm.title.assert == "MyApp" 37 | 38 | rm.name.assert == "myapp" 39 | rm.project.assert == "myapp" 40 | 41 | It should be able to parse out a description. 42 | 43 | rm.description.assert == "This is a description of the\nfake project." 44 | 45 | It should parse out the lincese. 46 | 47 | rm.license.assert == "GPL" 48 | 49 | And so forth. 50 | 51 | -------------------------------------------------------------------------------- /lib/gemdo/cli/news.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | ## 6 | # New command displays the current release notes. 7 | # 8 | class News < Base 9 | 10 | # 11 | def parser 12 | super do |opt| 13 | opt.banner = "pom news" 14 | 15 | opt.on("--text", "-t", "show verbatim text") do 16 | options.text = true 17 | end 18 | 19 | opt.on("--header", "-h", "include header") do 20 | options.header = true 21 | end 22 | 23 | opt.on("--changes", "-c", "include list of changes") do 24 | options.changes = true 25 | end 26 | end 27 | end 28 | 29 | # 30 | def execute 31 | if options.text 32 | puts project.news.text 33 | else 34 | if options.header 35 | puts project.news.header 36 | puts 37 | end 38 | puts project.news.notes.rstrip 39 | if options.changes 40 | puts 41 | puts project.news.changes 42 | end 43 | end 44 | end 45 | 46 | end 47 | 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /work/deprecated/gemfile-refit/Gemfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | title 'Gemdo' 4 | version '1.0.0' 5 | 6 | gem 'facets', '2.8+' 7 | 8 | group :build do 9 | gem 'syckle' 10 | end 11 | 12 | group :test do 13 | gem 'ko' 14 | gem 'qed', '2.2+' 15 | end 16 | 17 | summary 'Ruby Project Object Model' 18 | license 'Apache 2.0' 19 | contact 'Thomas Sawyer ' 20 | created '2009-07-22' 21 | 22 | description %{ 23 | Gemdo provides a complete project layout standard 24 | and metadata system for Ruby developers. 25 | } 26 | 27 | authors ['Thomas Sawyer'] 28 | 29 | resources( 30 | 'home' => http://rubyworks.github.com/gemdo 31 | 'code' => http://github.com/rubyworks/gemdo 32 | 'repo' => git://github.com/rubyworks/gemdo.git 33 | 'docs' => http://wiki.github.com/rubyworks/gemdo 34 | 'wiki' => http://wiki.github.com/rubyworks/gemdo 35 | 'api' => http://rubyworks.github.com/gemdo/rdoc 36 | 'mail' => http://groups.google.com/group/rubyworks 37 | ) 38 | 39 | organization 'RubyWorks' 40 | copyright 'Copyright (c) 2009 Thomas Sawyer' 41 | 42 | manifest 'MANIFEST' 43 | 44 | -------------------------------------------------------------------------------- /demo/applique/fixtures.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | FIXTURE_DIR = File.join(File.dirname(__FILE__), '..', 'fixtures') 4 | PROJECT_DIR = 'example' 5 | 6 | # Remove the example project if it exists. 7 | Before :demo do 8 | FileUtils.rm_r(PROJECT_DIR) if File.exist?(PROJECT_DIR) 9 | FileUtils.cp_r(File.join(FIXTURE_DIR, 'empty'), PROJECT_DIR) 10 | end 11 | 12 | When 'iven a new project' do 13 | FileUtils.rm_r(PROJECT_DIR) if File.exist?(PROJECT_DIR) 14 | FileUtils.cp_r(File.join(FIXTURE_DIR, 'empty'), PROJECT_DIR) 15 | @_current = :empty 16 | end 17 | 18 | When 'iven a complete project example' do |name| 19 | if @_current != :complete 20 | FileUtils.rm_r(PROJECT_DIR) if File.exist?(PROJECT_DIR) 21 | FileUtils.cp_r(File.join(FIXTURE_DIR, 'complete'), PROJECT_DIR) 22 | @_current = :complete 23 | end 24 | end 25 | 26 | When 'iven a ((([\.\w]+))) project file' do |name, text| 27 | File.open(File.join(PROJECT_DIR, name), 'w') do |f| 28 | f << text 29 | end 30 | end 31 | 32 | When 'no ((([\.\w]+))) file in a project' do |name| 33 | FileUtils.rm(File.join(PROJECT_DIR, name)) 34 | end 35 | 36 | -------------------------------------------------------------------------------- /work/deprecated/test/version.rb: -------------------------------------------------------------------------------- 1 | require 'pom/version' #covers 'pom/version' 2 | 3 | KO.case "POM::VersionNumber" do 4 | 5 | #unit Rock::VersionNumber, :bump 6 | 7 | test "bump major number" do 8 | v = POM::VersionNumber[1,0,0] 9 | v.bump(:major).to_s == '2.0.0' 10 | end 11 | 12 | test "bump minor number" do 13 | v = POM::VersionNumber[1,0,0] 14 | v.bump(:minor).to_s == '1.1.0' 15 | end 16 | 17 | test "bump patch number" do 18 | v = POM::VersionNumber[1,0,0] 19 | v.bump(:patch).to_s == '1.0.1' 20 | end 21 | 22 | test "bump build number" do 23 | v = POM::VersionNumber[1,0,0,0] 24 | v.bump(:build).to_s == '1.0.0.1' 25 | end 26 | 27 | test "bump build number with state" do 28 | v = POM::VersionNumber[1,0,0,'pre',1] 29 | v.bump(:build).to_s == '1.0.0.pre.2' 30 | end 31 | 32 | test "bump state segment" do 33 | v = POM::VersionNumber[1,0,0,'pre',2] 34 | v.bump(:state).to_s == '1.0.0.rc.1' 35 | end 36 | 37 | #unit Rock::VersionNumber, :restate 38 | 39 | test "reset state" do 40 | v = POM::VersionNumber[1,0,0,'pre',2] 41 | v.restate(:beta).to_s == '1.0.0.beta.1' 42 | end 43 | 44 | end 45 | 46 | -------------------------------------------------------------------------------- /lib/gemdo/cli/spec.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | ## 6 | # 7 | class Spec < Base 8 | 9 | def initialize 10 | super 11 | @project = POM::Project.find 12 | @update = false 13 | end 14 | 15 | attr :project 16 | 17 | # TODO: reference constant for .ruby file 18 | def parse 19 | parser = OptionParser.new do |opt| 20 | opt.banner = "pom spec" 21 | 22 | opt.on("--update", "-u", "update .ruby file") do 23 | @update = true 24 | end 25 | 26 | opt.on("--debug", "run in debug mode") do 27 | $DEBUG = true 28 | end 29 | 30 | opt.on_tail("--help", "-h", "display this help message") do 31 | puts opt 32 | exit 33 | end 34 | end 35 | 36 | parser.parse! 37 | end 38 | 39 | # TODO: Maybe make file name printout relative to current directory. 40 | def execute 41 | if @update 42 | file = project.profile.save! 43 | $stdout.puts "#{File.basename(file)} updated." 44 | else 45 | $stdout.puts project.metadata.to_data.to_yaml 46 | end 47 | end 48 | 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /work/.ruby.old: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: 1.0.0 3 | name: pom 4 | repositories: 5 | public: git://github.com/rubyworks/pom.git 6 | title: POM 7 | license: Apache 2.0 8 | contact: Thomas Sawyer 9 | resources: 10 | forum: http://groups.google.com/group/rubyworks-mailinglist 11 | api: http://rubyworks.github.com/pom/rdoc 12 | mail: http://groups.google.com/group/rubyworks-mailinglist 13 | docs: http://wiki.github.com/rubyworks/pom 14 | wiki: http://wiki.github.com/rubyworks/pom 15 | home: http://rubyworks.github.com/pom 16 | work: http://github.com/rubyworks/pom 17 | requires: 18 | - group: [] 19 | 20 | name: facets 21 | version: 2.8+ 22 | - group: 23 | - build 24 | name: syckle 25 | version: 0+ 26 | - group: 27 | - test 28 | name: ko 29 | version: 0+ 30 | - group: 31 | - test 32 | name: qed 33 | version: 2.2+ 34 | manifest: Manifest.txt 35 | version: 1.0.0 36 | copyright: Copyright (c) 2009 Thomas Sawyer 37 | organization: RubyWorks 38 | authors: 39 | - Thomas Sawyer 40 | description: POM provides a complete project layout standard and metadata system for Ruby developers. 41 | summary: Ruby Project Object Model 42 | created: 2009-07-22 43 | -------------------------------------------------------------------------------- /work/trash/root.rb: -------------------------------------------------------------------------------- 1 | require 'pom/core_ext/pathname' 2 | 3 | module POM 4 | 5 | # Root directory is indicated by the presence of either a 6 | # .ruby file or as a fallback a lib/ directory. 7 | ROOT_INDICATORS = ['.rubyspec', '.ruby', '.git', '.hg', '_darcs', 'lib/'] 8 | 9 | # Locate the project's root directory. This is determined 10 | # by ascending up the directory tree from the current position 11 | # until a root indicator is matched. Returns +nil+ if not 12 | # found. 13 | 14 | def self.root(dir=Dir.pwd) 15 | dir = File.expand_path(dir) 16 | home = File.expand_path('~') # Better way? 17 | ROOT_INDICATORS.find do |marker| 18 | while dir != home && dir != '/' 19 | if File.exist?(File.join(dir, marker)) 20 | break dir 21 | else 22 | dir = File.dirname(dir) 23 | end 24 | end 25 | end 26 | dir ? Pathname.new(dir) : nil 27 | end 28 | 29 | =begin 30 | def self.locate_root_at(indicator) 31 | root = nil 32 | dir = Dir.pwd 33 | while !root && dir != '/' 34 | find = File.join(dir, indicator) 35 | mark = Dir.glob(find, File::FNM_CASEFOLD).first 36 | root = dir if mark 37 | dir = File.dirname(dir) 38 | end 39 | root ? Pathname.new(root) : nil 40 | end 41 | =end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /work/ideas/version-styles/pom_style.rb: -------------------------------------------------------------------------------- 1 | class POM::Package 2 | 3 | # Standard POM style PACKAGE file, e.g. 4 | # 5 | # --- 6 | # name: pom 7 | # vers: 1.0.0.pre.1 8 | # date: 2010-10-10 9 | # code: POM 10 | # 11 | module POMStyle 12 | 13 | # 14 | def self.match?(data) 15 | return false unless Hash === data 16 | data = data.inject({}){|h,(k,v)| h[k.to_s]=v; h} 17 | !data.keys.include?('major') 18 | end 19 | 20 | # TODO: Add time to date? 21 | def render 22 | out = [] 23 | out << "name: #{name}" 24 | out << "vers: #{version}" 25 | out << "date: #{date.strftime('%Y-%m-%d')}" 26 | out << "code: #{code}" if code 27 | out << "nick: #{nick}" if nick 28 | out << "path: #{path.inspect}" if path && path != ['lib'] 29 | out.join("\n") 30 | end 31 | 32 | # 33 | def parse(data) 34 | data = data.inject({}){|h,(k,v)| h[k.to_s]=v; h} 35 | self.name = data['name'] 36 | self.vers = data['vers'] || data['version'] 37 | self.date = data['date'] 38 | self.code = data['code'] || data['codename'] || data['module'] 39 | self.nick = data['nick'] || data['nickname'] 40 | self.path = data['path'] || data['loadpath'] || ['lib'] 41 | end 42 | 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (BSD-2-Clause License) 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 14 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 16 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /work/consider/build.rb: -------------------------------------------------------------------------------- 1 | require 'pom/filestore' 2 | 3 | module POM 4 | 5 | # 6 | class Build < FileStore # :nodoc: 7 | 8 | ## 9 | # What packages this project requires for development, 10 | # sometimes called 'build requirements'. 11 | # :attr_accessor: requires 12 | attr_accessor :requires, :default => [] 13 | 14 | ## 15 | # External requirements, outside of the normal packaging system. 16 | # :attr_accessor: externals 17 | attr_accessor :externals, :default => [] 18 | 19 | ## 20 | # File pattern list of files to distribute in package. 21 | # This could, for instance, be used to assist with MANIFEST 22 | # generation. 23 | # :attr_accessor: distribute 24 | attr_accessor :distribute, :default => ['**/*'] 25 | 26 | # Map project directories and files to publish locations on 27 | # the webserver. This entry might be used to publish a project 28 | # website. 29 | attr_accessor :sitemap, :default => lambda{ {webdir => '.'} } 30 | 31 | # S P E C I A L S E T T E R S 32 | 33 | # 34 | def sitemap=(x) 35 | return self['sitemap'] = nil unless x 36 | self['sitemap'] = YAML.load(x) #.to_list 37 | end 38 | 39 | private 40 | 41 | def webdir 42 | root.glob('site,website,web').first || 'site' 43 | end 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /lib/gemdo/core_ext/pathname.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | class Pathname 4 | 5 | # 6 | def glob(match, *opts) 7 | flags = 0 8 | opts.each do |opt| 9 | case opt when Symbol, String 10 | flags += ::File.const_get("FNM_#{opt}".upcase) 11 | else 12 | flags += opt 13 | end 14 | end 15 | Dir.glob(::File.join(self.to_s, match), flags).collect{ |m| self.class.new(m) } 16 | end 17 | 18 | # 19 | def glob_relative(match, *opts) 20 | flags = 0 21 | opts.each do |opt| 22 | case opt when Symbol, String 23 | flags += ::File.const_get("FNM_#{opt}".upcase) 24 | else 25 | flags += opt 26 | end 27 | end 28 | files = Dir.glob(::File.join(self.to_s, match), flags) 29 | files = files.map{ |f| f.sub(self.to_s.chomp('/') + '/', '') } 30 | files.collect{ |m| self.class.new(m) } 31 | end 32 | 33 | # TODO: rename glob_first or deprecate 34 | def first(match, *opts) 35 | flags = 0 36 | opts.each do |opt| 37 | case opt when Symbol, String 38 | flags += ::File.const_get("FNM_#{opt}".upcase) 39 | else 40 | flags += opt 41 | end 42 | end 43 | file = ::Dir.glob(::File.join(self.to_s, match), flags).first 44 | file ? self.class.new(file) : nil 45 | end 46 | 47 | # 48 | alias_method :/, :+ 49 | 50 | end 51 | 52 | -------------------------------------------------------------------------------- /lib/gemdo/cli/base.rb: -------------------------------------------------------------------------------- 1 | module GemDo 2 | module CLI 3 | 4 | # Base class for GemDo commands. 5 | class Base 6 | 7 | # 8 | def self.inherited(subclass) 9 | Commands << subclass 10 | end 11 | 12 | # 13 | def self.run 14 | new.run 15 | end 16 | 17 | # 18 | def initialize 19 | $TRIAL = nil 20 | $FORCE = nil 21 | 22 | @options = OpenStruct.new 23 | @arguments = [] 24 | end 25 | 26 | # Access to Project instance. 27 | def project 28 | @project ||= Project.find 29 | end 30 | 31 | # 32 | attr :options 33 | 34 | # 35 | attr :arguments 36 | 37 | # 38 | def run 39 | parse 40 | execute 41 | end 42 | 43 | # 44 | def parse 45 | parser.parse! 46 | @arguments = ARGV 47 | end 48 | 49 | # 50 | def parser(&block) 51 | @parser ||= ( 52 | opt = OptionParser.new(&block) 53 | 54 | opt.on("--debug", "run in debug mode") do 55 | $DEBUG = true 56 | $VERBOSE = true 57 | end 58 | 59 | opt.on_tail("--help", "-h", "display this help message") do 60 | puts opt 61 | exit 62 | end 63 | 64 | opt 65 | ) 66 | end 67 | 68 | def self.run 69 | new.run 70 | end 71 | 72 | end#class Base 73 | 74 | end#module Commands 75 | 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /work/consider/02_cli/bump.md: -------------------------------------------------------------------------------- 1 | = Version Bumping 2 | 3 | In the demonstration to follow we will uses this macro to reload 4 | the PACKAGE file. 5 | 6 | def package 7 | YAML.load(File.new('tmp/example/PACKAGE')) 8 | end 9 | 10 | Given a PACKAGE project file containing ... 11 | 12 | name: foo 13 | vers: 1.0.0 14 | 15 | First, the `pom bump` command, when given no other arguments will 16 | display the current version. 17 | 18 | `cd tmp/example; pom bump`.assert == "1.0.0\n" 19 | 20 | This is the same as using `pom show version`. 21 | 22 | `cd tmp/example; pom show version`.assert == "1.0.0\n" 23 | 24 | We can use the `pom bump` command to bump the `patch` version 25 | using the `--patch` flag. 26 | 27 | `cd tmp/example; pom bump --patch` 28 | 29 | We can see that the `patch` number has been incremented. 30 | 31 | package['vers'].assert = '1.0.1' 32 | 33 | Again, we can use the `pom bump` command to bump the `minor` version 34 | with the `--minor` flag. 35 | 36 | `cd tmp/example; pom bump --minor` 37 | 38 | And we can see that the `minor` number has been incremented. 39 | 40 | package['vers'].assert = '1.1.0' 41 | 42 | We can use the `pom bump` command to bump the `major` version by 43 | using the `--major` flag. 44 | 45 | `cd tmp/example; pom bump --major` 46 | 47 | We can see that the `major` number has been incremented. 48 | 49 | package['vers'].assert = '2.0.0' 50 | 51 | -------------------------------------------------------------------------------- /work/deprecated/resources.rdoc: -------------------------------------------------------------------------------- 1 | = Resources 2 | 3 | The Resource class keeps track of al lthe various URIs associated with 4 | a project. Each entry has a name and then the URI corresponding to it. 5 | For the most part entries are freeform. You can name them what ever you 6 | like. However common names have beem aliased to one another, such as 'mail' 7 | and 'mailinglist'. And two entries are typically expected to avaialble if 8 | applicable, namely 'homepage', which is the project main website, and 9 | 'repository', which is the project's public SCM repository address. 10 | 11 | Resources are a subentry in the project's PROFILE, so the initializer simply 12 | take a hash of initial entires. 13 | 14 | p POM::Resources 15 | 16 | resources = POM::Resources.new( 17 | :home=>'http://rubyworks.github.com/pom', 18 | :repo=>'git://github.com/rubyworks/pom.git' 19 | ) 20 | 21 | The main thing to note about the Resources object is the ability to access 22 | information by aliased names. 23 | 24 | resources.homepage.assert == 'http://rubyworks.github.com/pom' 25 | resources.repository.assert == 'git://github.com/rubyworks/pom.git' 26 | 27 | If we look at the underlying hash we will see howeever all the entries use only 28 | the original given name. 29 | 30 | resources.assert.to_h = { 31 | :home=>'http://rubyworks.github.com/pom', 32 | :repo=>'git://github.com/rubyworks/pom.git' 33 | } 34 | 35 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast -x Profile .prospec bin demo lib qed test [A-Z]* 2 | bin/pom 3 | lib/pom/command.rb 4 | lib/pom/commands/about.rb 5 | lib/pom/commands/bump.rb 6 | lib/pom/commands/gemspec.rb 7 | lib/pom/commands/init.rb 8 | lib/pom/commands/news.rb 9 | lib/pom/commands/resolve.rb 10 | lib/pom/commands/show.rb 11 | lib/pom/commands/spec.rb 12 | lib/pom/core_ext/pathname.rb 13 | lib/pom/core_ext/to_list.rb 14 | lib/pom/core_ext/try_dup.rb 15 | lib/pom/core_ext.rb 16 | lib/pom/errors.rb 17 | lib/pom/gemspec.rb 18 | lib/pom/history.rb 19 | lib/pom/manifest.rb 20 | lib/pom/news.rb 21 | lib/pom/profile/inference.rb 22 | lib/pom/profile/properties.rb 23 | lib/pom/profile/template.erb 24 | lib/pom/profile.rb 25 | lib/pom/project/files.rb 26 | lib/pom/project/paths.rb 27 | lib/pom/project/utils.rb 28 | lib/pom/project.rb 29 | lib/pom/property.rb 30 | lib/pom/readme.rb 31 | lib/pom/requires.rb 32 | lib/pom/resolver/gemcutter.rb 33 | lib/pom/resolver/rubygems.rb 34 | lib/pom/resolver/source.rb 35 | lib/pom/resolver.rb 36 | lib/pom/resources.rb 37 | lib/pom/version.rb 38 | lib/pom.rb 39 | lib/pom.yml 40 | qed/01_api/applique/pom.rb 41 | qed/01_api/history.rdoc 42 | qed/01_api/news.rdoc 43 | qed/01_api/profile.rdoc 44 | qed/01_api/project.rdoc 45 | qed/01_api/readme.rdoc 46 | qed/01_api/resources.rdoc 47 | qed/02_cli/bump.rdoc 48 | qed/02_cli/init.rdoc 49 | qed/applique/ae.rb 50 | qed/applique/fixtures.rb 51 | qed/profile.rdoc 52 | test/news.rb 53 | test/version.rb 54 | Rakefile 55 | README.rdoc 56 | Notes.rdoc 57 | History.rdoc 58 | License.txt 59 | -------------------------------------------------------------------------------- /work/deprecated/profile/profile.rdoc: -------------------------------------------------------------------------------- 1 | = POM::Profile 2 | 3 | Given a PROFILE project file containing ... 4 | 5 | --- 6 | title : DemoApp 7 | version: 1.0.0 8 | summary: Demo summary 9 | license: MIT 10 | contact: trans 11 | copyright: Copyright (c) 2010 Thomas Sawyer 12 | created: 2010-10-10 13 | 14 | authors: 15 | - Thomas Sawyer 16 | 17 | description: 18 | This is the description for the example. 19 | 20 | We can access this file by passing the project directory to the POM::Profile 21 | constructor method. 22 | 23 | profile = POM::Profile.new('tmp/example') 24 | 25 | Now we can verify profile is being read from the PROFILE. 26 | 27 | profile.name.assert == "demoapp" 28 | profile.title.assert == "DemoApp" 29 | profile.summary.assert == "Demo summary" 30 | profile.licenses.assert == ["MIT"] 31 | profile.contact.assert == "trans " 32 | profile.authors.assert == ["Thomas Sawyer"] 33 | profile.copyright.assert == "Copyright (c) 2010 Thomas Sawyer" 34 | profile.created.assert == Date.parse("2010-10-10") 35 | profile.description.assert == "This is the description for the example." 36 | 37 | And verify version profile is being read from the VERSION file. 38 | 39 | profile.version.to_s.assert == "1.0.0" 40 | 41 | We can also verify that certain settings are picking up their defaults. 42 | 43 | profile.loadpath.assert == ['lib'] 44 | 45 | And so forth. 46 | 47 | -------------------------------------------------------------------------------- /site/blog/2010-05-24-requirements/gemfile.page: -------------------------------------------------------------------------------- 1 | title : Rails GemFile (May 2010) 2 | layout : page 3 | 4 | --- coderay.ruby 5 | 6 | source 'http://rubygems.org' 7 | 8 | gem "arel", :git => "git://github.com/rails/arel.git" 9 | gem "rails", :path => File.dirname(__FILE__) 10 | 11 | gem "rake", ">= 0.8.7" 12 | gem "mocha", ">= 0.9.8" 13 | 14 | mri = !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" 15 | if mri && RUBY_VERSION < '1.9' 16 | gem "system_timer" 17 | gem "ruby-debug", ">= 0.10.3" 18 | end 19 | 20 | if mri || RUBY_ENGINE == "rbx" 21 | gem 'json' 22 | gem 'yajl-ruby' 23 | gem "nokogiri", ">= 1.4.0" 24 | elsif RUBY_ENGINE == "jruby" 25 | gem "ruby-debug" 26 | gem "jruby-openssl" 27 | end 28 | 29 | # AR 30 | if mri || RUBY_ENGINE == "rbx" 31 | gem "sqlite3-ruby", "= 1.3.0.beta.2", :require => 'sqlite3' 32 | 33 | group :db do 34 | gem "pg", ">= 0.9.0" 35 | gem "mysql", ">= 2.8.1" 36 | end 37 | elsif RUBY_ENGINE == "jruby" 38 | gem "activerecord-jdbcsqlite3-adapter" 39 | 40 | group :db do 41 | gem "activerecord-jdbcmysql-adapter" 42 | gem "activerecord-jdbcpostgresql-adapter" 43 | end 44 | end 45 | 46 | # AP 47 | gem "rack-test", "0.5.3", :require => 'rack/test' 48 | gem "RedCloth", ">= 4.2.2" 49 | 50 | group :documentation do 51 | gem 'rdoc', '2.1' 52 | end 53 | 54 | if ENV['CI'] 55 | gem "nokogiri", ">= 1.4.0" 56 | 57 | # fcgi gem doesn't compile on 1.9 58 | gem "fcgi", ">= 0.8.7" if RUBY_VERSION < '1.9.0' 59 | end 60 | 61 | -------------------------------------------------------------------------------- /lib/gemdo/project/manifest.rb: -------------------------------------------------------------------------------- 1 | module GemDo 2 | 3 | class Project 4 | 5 | # Manifest file. 6 | # 7 | # TODO: Replace with Mast's Manifest. 8 | class Manifest 9 | include Enumerable 10 | 11 | # File glob use for locating the MANIFEST file. 12 | DEFAULT_FILE = 'manifest{,.txt}' 13 | 14 | # Stores the path of the amnifest file. 15 | attr :file 16 | 17 | # Instantiate a new Manifest object, provided 18 | # the root directory of the project. 19 | def initialize(root) 20 | @root = root 21 | @file = root.glob(DEFAULT_FILE, :casefold).first 22 | end 23 | 24 | # Parses the MANIFEST file and returns it as an array of 25 | # file names. Blank lines and commented lines (using '#') 26 | # are ignored. 27 | def list 28 | @list ||= ( 29 | if exist? 30 | files = File.readlines(file).map{ |line| line.strip } 31 | files.reject{|line| line == '' or line =~ /^[#]/ } 32 | else 33 | [] 34 | end 35 | ) 36 | end 37 | 38 | # Alternate reference to the manifest list. 39 | alias_method :files, :list 40 | 41 | # Iterate over each file in the manifest. 42 | def each(&block) 43 | list.each(&block) 44 | end 45 | 46 | def exist? 47 | file 48 | end 49 | 50 | # 51 | def empty? 52 | list.empty? 53 | end 54 | 55 | # Size of the manifest. 56 | def size ; list.size ; end 57 | end 58 | 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | service : Email 4 | file : ~ 5 | subject : ~ 6 | mailto : 7 | - ruby-talk@ruby-lang.org 8 | - proutils@googlegroups.com 9 | from : <%= ENV['EMAIL_ACCOUNT'] %> 10 | server : <%= ENV['EMAIL_SERVER'] %> 11 | port : <%= ENV['EMAIL_PORT'] %> 12 | account : <%= ENV['EMAIL_ACCOUNT'] %> 13 | domain : <%= ENV['EMAIL_DOMAIN'] %> 14 | login : <%= ENV['EMAIL_LOGIN'] %> 15 | secure : <%= ENV['EMAIL_SECURE'] %> 16 | active : true 17 | 18 | gemcutter: 19 | active: true 20 | 21 | grancher: 22 | active: true 23 | 24 | box: 25 | service: Box 26 | types : [ gem ] 27 | active : true 28 | 29 | rdoc: 30 | service : RDoc 31 | format : newfish 32 | output : site/docs/api 33 | include : ~ 34 | exclude : ~ 35 | main : ~ 36 | extra : ~ 37 | active : true 38 | 39 | ridoc: 40 | service: RIDoc 41 | include: ~ 42 | exclude: ~ 43 | output : .ri 44 | active : true 45 | 46 | testrb: 47 | service : TestUnit 48 | tests : ~ 49 | exclude : ~ 50 | loadpath : ~ 51 | requires : ~ 52 | live : false 53 | active : false 54 | 55 | syntax: 56 | service : Syntax 57 | loadpath : ~ 58 | exclude : ~ 59 | active : false 60 | 61 | dnote: 62 | service : DNote 63 | loadpath : ~ 64 | labels : ~ 65 | output : ~ 66 | active : true 67 | 68 | vclog: 69 | service : VClog 70 | format : html 71 | type : history 72 | output : ~ 73 | active : true 74 | 75 | stats: 76 | service : Stats 77 | title : ~ 78 | loadpath : ~ 79 | exclude : ~ 80 | output : ~ 81 | active : true 82 | 83 | -------------------------------------------------------------------------------- /work/ideas/version-styles/jpom_style.rb: -------------------------------------------------------------------------------- 1 | class POM::Package 2 | 3 | # Jeweler-esque POM style PACKAGE file, e.g. 4 | # 5 | # --- 6 | # name : pom 7 | # major: 1 8 | # minor: 0 9 | # patch: 0 10 | # build: pre.1 11 | # date : 2010-10-10 12 | # code : POM 13 | # 14 | module JPOMStyle 15 | 16 | # 17 | def self.match?(data) 18 | return false unless Hash === data 19 | data = data.inject({}){|h,(k,v)| h[k.to_s]=v; h} 20 | return false unless data.keys.include?('major') 21 | keys = data.keys - %w{major minor patch build} 22 | return false if keys.empty? # jeweler style 23 | return true 24 | end 25 | 26 | # TODO: Add time to date? 27 | def render 28 | out = [] 29 | out << "name : #{name}" 30 | out << "major: #{major}" 31 | out << "minor: #{minor}" 32 | out << "patch: #{patch}" 33 | out << "build: #{build}" if build && !build.empty? 34 | out << "date : #{date.strftime('%Y-%m-%d')}" 35 | out << "code : #{code}" if code 36 | out << "nick : #{nick}" if nick 37 | out << "path : #{path.inspect}" if path && path != ['lib'] 38 | out.join("\n") 39 | end 40 | 41 | # 42 | def parse(data) 43 | data = data.inject({}){|h,(k,v)| h[k.to_s]=v; h} 44 | self.name = data['name'] 45 | self.vers = data.values_at('major','minor','patch','build').compact 46 | self.date = data['date'] 47 | self.code = data['code'] || data['codename'] || data['module'] 48 | self.nick = data['nick'] || data['nickname'] 49 | self.path = data['path'] || data['loadpath'] || ['lib'] 50 | end 51 | 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /work/deprecated/property.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | # 4 | def self.property(name, &block) 5 | Property.new(name, &block) 6 | end 7 | 8 | # Property class defines a metadata field. 9 | class Property 10 | 11 | # Dummy object. 12 | NA = Object.new 13 | 14 | # 15 | def self.list 16 | @list ||= [] 17 | end 18 | 19 | # 20 | def self.find(name) 21 | list.find{ |property| property.names.include?(name.to_sym) } 22 | end 23 | 24 | def initialize(name, &block) 25 | @name = name.to_sym 26 | 27 | Property.list << self 28 | 29 | @aliases = [] 30 | @parser = nil 31 | 32 | instance_eval(&block) if block 33 | end 34 | 35 | # 36 | def name 37 | @name 38 | end 39 | 40 | # 41 | def aliases(*list) 42 | if list.empty? 43 | @aliases 44 | else 45 | @aliases = list.map{ |a| a.to_sym } 46 | end 47 | end 48 | 49 | # 50 | def names 51 | [name, *aliases] 52 | end 53 | 54 | # 55 | def parse(value=NA, &block) 56 | if value == NA 57 | @parser = block 58 | else 59 | @parser.call(value) 60 | end 61 | end 62 | 63 | # 64 | def parser 65 | @parser 66 | end 67 | 68 | # 69 | def default(value=NA, &block) 70 | if block 71 | @default = block 72 | else 73 | @default = value if value != NA 74 | end 75 | @default 76 | end 77 | 78 | # 79 | def validate(value=NA, &block) 80 | if value == NA 81 | @validate = block 82 | else 83 | @validate.call(value) 84 | end 85 | end 86 | 87 | end 88 | 89 | end 90 | 91 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - var 6 | authors: 7 | - name: Trans 8 | email: transfire@gmail.com 9 | organizations: 10 | - name: Rubyworks 11 | requirements: 12 | - name: indexer 13 | - name: versus 14 | - name: readme 15 | - name: history 16 | - name: mast 17 | - version: 2.8+ 18 | name: facets 19 | - groups: 20 | - build 21 | development: true 22 | name: detroit 23 | - groups: 24 | - test 25 | development: true 26 | name: citron 27 | - groups: 28 | - test 29 | version: 2.2+ 30 | development: true 31 | name: qed 32 | conflicts: [] 33 | alternatives: [] 34 | resources: 35 | - type: home 36 | uri: http://rubyworks.github.com/pom 37 | label: Homepage 38 | - type: work 39 | uri: http://github.com/rubyworks/pom 40 | label: Development 41 | - type: docs 42 | uri: http://wiki.github.com/rubyworks/pom 43 | label: Documentation 44 | - type: wiki 45 | uri: http://wiki.github.com/rubyworks/pom 46 | label: User Guide 47 | - type: api 48 | uri: http://rubdoc.info/gems/pom/frames 49 | label: API Guide 50 | - type: mail 51 | uri: http://groups.google.com/group/rubyworks-mailinglist 52 | label: Mailing List 53 | - type: forum 54 | uri: http://groups.google.com/group/rubyworks-mailinglist 55 | label: Support Forum 56 | repositories: 57 | - name: upstream 58 | scm: git 59 | uri: git://github.com/rubyworks/pom.git 60 | categories: [] 61 | copyrights: 62 | - holder: Rubyworks 63 | year: '2009' 64 | license: BSD-2-Clause 65 | customs: [] 66 | paths: 67 | lib: 68 | - lib 69 | created: '2009-07-22' 70 | summary: Ruby Project Object Model 71 | title: POM 72 | version: 2.2.0 73 | description: POM provides an API for the typical Ruby project. 74 | date: '2013-03-05' 75 | -------------------------------------------------------------------------------- /work/reference/ProjectInfo: -------------------------------------------------------------------------------- 1 | --- 2 | project : projectinfo 3 | version : '0.6' 4 | status : beta 5 | 6 | title : ProjectInfo 7 | subtitle : The Project Metadata Class 8 | description : > 9 | ProjectInfo is a deluxe project metadata class. ProjectInfo is highly versitile 10 | for managing project metadata. It can be converted to a Gem::Specification 11 | in a single command (to_gemspec) for building out gems packages, as well as other 12 | package management control specifications (deb, rpm, pkgbuild, etc.). ProjectInfo 13 | is designed to be the best solution for complementing build tools --it is extensible 14 | and can automatically load from a YAML configuration file. 15 | 16 | author : Thomas Sawyer 17 | created : "2006-08-09" 18 | homepage : "http://ratchets.rubyforge.org" 19 | download : "https://rubyforge.org/frs/?group_id=2144" 20 | repository : "http://ratchets.rubyforge.org/src" 21 | slogan : Be Aware. Be Very Aware! 22 | #signiture : "../_privkey.pem" 23 | 24 | pack : [gem, tgz] 25 | distribute : [ -dev, -doc ] 26 | dependency : 27 | - [ facets, >= 2.0 ] 28 | executable : [] 29 | 30 | digest : sha1 31 | manifest : MANIFEST 32 | 33 | 34 | 35 | 36 | #scm: 37 | # system : svn 38 | # ignore : [ doc/api, test/lib ] 39 | # changelog : CHANGELOG 40 | # stamp : VERSION 41 | 42 | #rdoc: 43 | # template : jamis 44 | 45 | #mail: 46 | # server : smtp.gmail.com 47 | # account : transfire@gmail.com 48 | # mail_to : transfire@gmail.com 49 | 50 | #rubyforge: 51 | # project : ratchets 52 | # username : transami 53 | # groupid : 2144 54 | # package : projectinfo 55 | 56 | #host: 57 | # domain : rubyforge.org 58 | # user : transami 59 | # root : '/var/www/gforge-projects/ratchets' 60 | 61 | -------------------------------------------------------------------------------- /work/trash/needs.rb: -------------------------------------------------------------------------------- 1 | class POM::Metadata 2 | 3 | def needs 4 | @data['needs'] ||= Needs.new(self, 'needs') 5 | end 6 | 7 | # Needs holds a project's collection of dependencies. 8 | # 9 | class Needs < POM::FileStore 10 | 11 | ## 12 | # What other packages *must* this package have in order to function. 13 | # This includes any requirements neccessary for installation. 14 | # :attr_accessor: requries 15 | attr_accessor :requires, :default=>[] 16 | 17 | ## 18 | # External requirements, outside of the normal packaging system. 19 | # :attr_accessor: externals 20 | attr_accessor :externals, :default=>[] 21 | 22 | ## 23 | # What other packages *should* be used with this package. 24 | # :attr_accessor: recommend 25 | attr_accessor :recommend, :default=>[] 26 | 27 | ## 28 | # What other packages *could* be useful with this package. 29 | # :attr_accessor: suggest 30 | attr_accessor :suggest, :default=>[] 31 | 32 | ## 33 | # With what other packages does this package conflict. 34 | # :attr_accessor: conflicts 35 | attr_accessor :conflicts, :default=>[] 36 | 37 | ## 38 | # What other packages does this package replace. This is very much like #provides 39 | # but expresses a closser relation. For instance "libXML" has been replaced by "libXML2". 40 | # :attr_accessor: replaces 41 | attr_accessor :replaces, :default=>[] 42 | 43 | ## 44 | # What other package(s) does this package provide the same dependency fulfilment. 45 | # For example, a package 'bar-plus' might fulfill the same dependency criteria 46 | # as package 'bar', so 'bar-plus' is said to provide 'bar'. 47 | # :attr_accessor: provides 48 | attr_accessor :provides, :default=>[] 49 | 50 | end 51 | 52 | end 53 | 54 | -------------------------------------------------------------------------------- /lib/gemdo/cli/gemspec.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | ## 6 | # Create a gemspec for a project. 7 | # 8 | class Gemspec < Base 9 | 10 | # 11 | def update? 12 | @update 13 | end 14 | 15 | # 16 | def parse 17 | parser = OptionParser.new do |opt| 18 | opt.banner = "gemdo gemspec" 19 | 20 | opt.on("--update", "-u", "update gemspec file") do 21 | @update = true 22 | end 23 | 24 | opt.on("--force", "-f", "override any safe-guarded operations") do 25 | $FORCE = true 26 | end 27 | 28 | opt.on("--debug", "run in debug mode and raise exceptions") do 29 | $DEBUG = true 30 | $VERBOSE = true 31 | end 32 | 33 | opt.on_tail("--help", "-h", "display this help message") do 34 | puts opt 35 | exit 36 | end 37 | end 38 | 39 | parser.parse! 40 | 41 | @file = ARGV.first 42 | end 43 | 44 | # 45 | def execute 46 | yaml = project.to_gemspec.to_yaml 47 | if update? 48 | #if File.exist?(file) and not $FORCE 49 | # $stderr << "Gemspec already exists. Use --force/-f to overwrite.\n" 50 | #else 51 | File.open(file, 'w') do |f| 52 | f << yaml 53 | end 54 | $stderr.puts "#{File.basename(file)} updated." 55 | #end 56 | else 57 | $stdout.puts yaml 58 | end 59 | end 60 | 61 | # 62 | def file 63 | @file ||= ( 64 | dot_gemspec = (project.root + '.gemspec').to_s 65 | if File.exist?(dot_gemspec) 66 | dot_gemspec.to_s 67 | else 68 | project.metadata.name + '.gemspec' 69 | end 70 | ) 71 | end 72 | 73 | end 74 | 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /work/deprecated/alt_version/version_helper.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | module VersionHelper 4 | 5 | # 6 | def parse_release_stamp(text) 7 | release = {} 8 | # version 9 | if md = /\b(\d+\.\d.*?)\s/.match(text) 10 | release[:version] = md[1] 11 | end 12 | # date 13 | if md = /\b(\d+\-\d.*?)\s/.match(text) 14 | release[:date] = md[1] 15 | end 16 | # codename 17 | if md = /\"(.*?)\"/.match(text) 18 | release[:billname] = md[1] 19 | end 20 | release 21 | end 22 | 23 | # 24 | def parse_release_hash(data) 25 | data = data.inject({}){ |h,(k,v)| h[k.to_sym] = v; h } 26 | release = {} 27 | release[:version] = data.values_at(:major,:minor,:patch,:build).compact.join('.') 28 | release[:date] = data[:date] 29 | release[:billname] = data[:bill] || data[:billname] 30 | release 31 | end 32 | 33 | =begin 34 | # 35 | def parse_release_stamp(text) 36 | release = {} 37 | # version 38 | if md = /\b(\d+\.\d.*?)\s/.match(text) 39 | release[:vers] = md[1] 40 | end 41 | # date 42 | if md = /\b(\d+\-\d.*?)\s/.match(text) 43 | release[:date] = md[1] 44 | end 45 | # nickname 46 | if md = /\"(.*?)\"/.match(text) 47 | release[:nick] = md[1] 48 | end 49 | # loadpath 50 | test.scan(/\s(\S+)\/\s/) do |m| 51 | release[:path] ||= [] 52 | release[:path] << m 53 | end 54 | release 55 | end 56 | =end 57 | =begin 58 | # 59 | def parse_version_file 60 | file = root.glob('VERSION{,.txt,.yml,.yaml').first 61 | if file 62 | text = file.read.strip 63 | if file.extname == '.yml' or file.extname == '.yaml' or text[0,3] == '---' 64 | parse_version_yaml(text) 65 | else 66 | parse_version_text(text) 67 | end 68 | end 69 | end 70 | =end 71 | 72 | end 73 | 74 | end 75 | -------------------------------------------------------------------------------- /work/consider/02_cli/init.md: -------------------------------------------------------------------------------- 1 | = Generate POM Metadata with +init+ Command 2 | 3 | == Extracted from a README file 4 | 5 | Given a README project file containing ... 6 | 7 | = MyApp 8 | 9 | * http://some.org 10 | 11 | == DESCRIPTION 12 | 13 | This is a description of the 14 | fake project. 15 | 16 | = INSTALL 17 | 18 | To install use RubyGems 19 | 20 | $ gem install myapp 21 | 22 | = LICENSE 23 | 24 | (GPL) 25 | 26 | Copyright 2009 Thomas Sawyer 27 | 28 | We can generate metadata from the README using the +init+ command. 29 | 30 | `cd tmp/example; pom init README` 31 | 32 | Now lets verify the metadata was extracted as expected. 33 | 34 | require 'pom/project' 35 | 36 | project = POM::Project.new('tmp/example') 37 | 38 | The values should have been picked up from PACKAGE and PROFILE files. 39 | 40 | project.metadata.title.assert == "MyApp" 41 | project.metadata.description.assert == "This is a description of the\nfake project." 42 | project.metadata.license.assert == "GPL" 43 | 44 | == Extracted from a Gemspec 45 | 46 | Given an empty project directory and given a myapp.gemspec 47 | project file containing ... 48 | 49 | Gem::Specification.new do |s| 50 | s.name = %q{myapp} 51 | s.version = "1.0.0" 52 | s.authors = ["Tom Sawyer"] 53 | s.date = %q{2010-10-10} 54 | s.description = %q{This is a description of a fake project.} 55 | s.email = %q{transfire@gmail.com} 56 | end 57 | 58 | We can generate metadata from the gem specification using the pom command. 59 | 60 | `cd tmp/example; pom init myapp.gemspec` 61 | 62 | Now lets verify the metadata was extracted as expected. 63 | 64 | require 'pom/project' 65 | 66 | project = POM::Project.new('tmp/example') 67 | 68 | The values should have been picked up from PACKAGE and PROFILE files. 69 | 70 | project.metadata.title.assert == "Myapp" 71 | project.metadata.version.to_s.assert == "1.0.0" 72 | project.metadata.description.assert == "This is a description of a fake project." 73 | 74 | -------------------------------------------------------------------------------- /lib/gemdo/cli/resolve.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | 6 | # 7 | class Resolve < Base 8 | 9 | # 10 | def parse 11 | parser = OptionParser.new do |opt| 12 | opt.banner = "gemdo resolve" 13 | opt.on("--runtime", "-r", "runtime dependencies only") do 14 | @options[:runtime] = true 15 | end 16 | opt.on("--prerelease", "-p", "include prerleases") do 17 | @options[:prerelease] = true 18 | end 19 | opt.on("--local", "resolve against local sources") do 20 | @options[:local] = true 21 | end 22 | opt.separator("FORMAT OPTIONS: (pick one)") 23 | opt.on("--lock", "output lock code") do 24 | @options[:format] = :lock 25 | end 26 | #opt.on("--requests", "--req", "show all dependencies") do 27 | # @options[:format] = :requests 28 | #end 29 | opt.on("--breakdown", "-b", "show all dependencies") do 30 | @options[:format] = :breakdown 31 | end 32 | opt.separator("COMMON OPTIONS:") 33 | opt.on("--debug", "run in debug mode") do 34 | $DEBUG = true 35 | $VERBOSE = true 36 | end 37 | opt.on_tail("--help", "-h", "display this help message") do 38 | puts opt 39 | exit 40 | end 41 | end 42 | 43 | parser.parse! 44 | end 45 | 46 | # 47 | def execute 48 | resolver = POM::Resolver.new(Dir.pwd, options) 49 | resolver.setup 50 | end 51 | 52 | # 53 | #def project 54 | # @project ||= POM::Project.new(Dir.pwd) 55 | #end 56 | 57 | # OLD SCHOOL 58 | #def execute 59 | # project.requirements.each do |req| 60 | # begin 61 | # # $stdout.print(" " * depth) 62 | # gem(req.name, req.constraint.to_s) 63 | # rescue Exception => error 64 | # $stdout.puts " [FAIL] %-20s %s" % [req.to_s, error.to_s.strip] 65 | # else 66 | # $stdout.puts " [LOAD] #{req}" 67 | # end 68 | # end 69 | #end 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /work/trash/blog/2010-07-23-individual-requirement-files.md: -------------------------------------------------------------------------------- 1 | # 2010-07-23 | Individual Requirement Files 2 | 3 | Today I came up with yet another idea for the layout of project metadata, 4 | one that moves back toward the use of a directory-based configuration, but 5 | still remains well within in the range of middle ground. The idea is to use 6 | a special directory, but break requirements out into individual files. 7 | 8 | RUBY/ 9 | loadpath 10 | package 11 | requires/ 12 | qed-2.3 13 | rdoc-2.5 14 | 15 | The name of each requires file is not actually important. They exist simple 16 | for the benefit of the developer to read. The content of the files define the 17 | actual requirements. Each file being a YAML-formatted hash. For example, 18 | 19 | --- 20 | name: qed 21 | vers: 2.3+ 22 | type: test 23 | 24 | The benefit of this design would be the ease at which requirements could be 25 | swapped about between projects. The `package` file would also be essentialy 26 | identical to ther requirements file, so they too could be used. For example 27 | I could add a a dependency on my current development version of QED to 28 | my development version of POM simply with: 29 | 30 | cp qed/RUBY/package pom/RUBY/requires/qed 31 | 32 | Pretty cool! But, as with most good ideas, it is a bad idea as well. 33 | As with any multiple file configuration, editing them all in one fell swoop 34 | is not as easy as editing a single file, though in this case I do not think 35 | that's a show-stopping issue. A more signifficant downside is the inability 36 | to read in a list of requirements in one stream. Moreover, despite being able 37 | to easily copy requirements between projects, one usually doesn't work 38 | with requirements in this manner. The requirements of one package has no 39 | barring on another beyond depending on that package, in which case there 40 | is certainly no need to "copy" requirements. 41 | 42 | It's an intersting concept, but ultimately it seems to be YAGNI. I went ahead 43 | and blogged about it just as a future reminder of this line of reasoning. 44 | 45 | -------------------------------------------------------------------------------- /lib/gemdo/project/news.rb: -------------------------------------------------------------------------------- 1 | module GemDo 2 | 3 | class Project 4 | 5 | ## 6 | # This class provides access to the latest news / release notes, 7 | # for a project. These notes are either extracted from a +NEWS+ 8 | # file or from the lastest entry in the +HISTORY+ file. 9 | # 10 | # @todo Maybe port this over the History project itself. 11 | class News < History::Release 12 | 13 | # Search glob if any files exist in project from which 14 | # the Release class can gather information. 15 | FILE_PATTERN = '{NEWS,WHATSNEW}{,.*}' 16 | 17 | # 18 | def self.file_pattern 19 | FILE_PATTERN 20 | end 21 | 22 | # 23 | def self.find(root) 24 | root = Pathname.new(root) 25 | root.glob(file_pattern, :casefold).first 26 | end 27 | 28 | # 29 | def self.at(root) 30 | new(root) 31 | end 32 | 33 | # Root directory of project. 34 | attr :root 35 | 36 | # Release file, if any. 37 | attr :file 38 | 39 | # New News ;) 40 | def initialize(root, opts={}) 41 | @root = Pathname.new(root) 42 | @history = opts[:history] 43 | 44 | @file = opts[:file] || self.class.find(root) 45 | 46 | if @file 47 | @text = File.read(@file) 48 | super(@text) 49 | end 50 | 51 | if !file && history 52 | @text = history.release.text 53 | 54 | @header = history.release.header 55 | @notes = history.release.notes 56 | @changes = history.release.changes 57 | 58 | @version = history.release.version 59 | @date = history.release.date 60 | @nickname = history.release.nickname 61 | end 62 | end 63 | 64 | # NEWS file, if it exists. 65 | def file 66 | @file 67 | end 68 | 69 | # Lazy access to HISTORY file. 70 | def history 71 | return @history unless @history.nil? 72 | @history = ( 73 | if History.exist?(root) 74 | History.at(root) 75 | else 76 | false 77 | end 78 | ) 79 | end 80 | 81 | end #class News 82 | 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /work/deprecated/requires.rdoc: -------------------------------------------------------------------------------- 1 | = Require Class 2 | 3 | The requirements configuration file provides information 4 | on what dependencies a project has on other projects. 5 | 6 | Given a REQUIRE project file containing ... 7 | 8 | # Project must haves these to run. 9 | runtime: 10 | - rake 0.8.7+ 11 | - mocha 0.9.8+ 12 | - nokogiri 1.4.0+ 13 | - system_timer 14 | - ruby-debug 0.10.3+ 15 | - json 16 | - yajl-ruby 17 | # AP 18 | - rack-test 0.5.3 19 | - RedCloth 4.2.2+ 20 | # AR 21 | - sqlite3-ruby 1.3.0.beta.2 22 | 23 | # Project does not have to have these, but its a good idea. 24 | runtime/recommend: 25 | - pg 0.9.0+ 26 | - mysql 2.8.1+ 27 | 28 | # Purely optional. 29 | runtime/optional: 30 | - fcgi 0.8.7+ # does not compile on mri 1.9+ 31 | 32 | # Requirements that are vendored and shipped with package. 33 | runtime/vendor: 34 | - arel 35 | 36 | # 37 | development: 38 | - rake 39 | 40 | # To generate documentation. 41 | development/document: 42 | - rdoc 2.1 43 | 44 | # To run tests. 45 | development/test: 46 | - rspec 47 | 48 | # Packages that are vendored just for development. 49 | development/vendor: [] 50 | 51 | alternate/provision: [] 52 | 53 | alternate/replacement: [] 54 | 55 | alternate/conflict: [] 56 | 57 | The PackageList class provides an interface to this information. 58 | The initializer takes the root directory for the project 59 | and looks for a file called +REQUIRE+ or +.require+, 60 | optionally ending in +.yml+ or +.yaml+. 61 | 62 | req = POM::Requires.new(['facets 2.8.3', 'qed 1.0+ (test)']) 63 | 64 | Requires is enumerable over all dependencies list. 65 | 66 | req.size.assert == 2 67 | 68 | But a few of these dependencies are purely optional. 69 | 70 | reqs = req.production 71 | reqs.size.assert == 1 72 | 73 | Two of those are development dependencies. 74 | 75 | devs = req.development 76 | devs.size.assert == 1 77 | 78 | Since Reqfile is Enumerable we can filter dependencies using 79 | fine grain criteria as well. 80 | 81 | test = req.select { |dep| dep.test? } 82 | test.size.assert == 1 83 | 84 | We see that we have a single dependecy for testing. 85 | 86 | -------------------------------------------------------------------------------- /work/deprecated/profile/save.rb: -------------------------------------------------------------------------------- 1 | 2 | # Save package information as Ruby source code. 3 | # 4 | # module Foo 5 | # VERSION = "1.0.0" 6 | # RELEASED = "2010-10-01" 7 | # CODENAME = "Fussy Foo" 8 | # end 9 | # 10 | # NOTE: This is not actually needed, as I exmplain in a recent 11 | # blog post. But we will leave it here for the time being. 12 | # 13 | # TODO: Improve upon this, allow selectable fields. 14 | def save_as_ruby(file) 15 | if File.exist?(file) 16 | text = File.read(file) 17 | save_as_ruby_sub!(text, :version, 'VERSION') 18 | save_as_ruby_sub!(text, :released, 'RELEASED', 'DATE') 19 | save_as_ruby_sub!(text, :codename, 'CODENAME') 20 | else 21 | t = [] 22 | t << %[module #{codename}] 23 | t << %[ VERSION = "#{version}"] 24 | t << %[ RELEASE = "#{release}"] 25 | t << %[ CODENAME = "#{codename}"] 26 | t << %[end] 27 | text = t.join("\n") 28 | end 29 | File.open(file, 'w'){ |f| f << text } 30 | end 31 | 32 | # 33 | def save_as_ruby_sub!(text, field, *patterns) 34 | patterns = patterns.join('|') 35 | text.sub!(/(#{patterns}\s*)=\s*(.*?)(?!\s*\#?|$)/, '\1=' + __send__(field)) 36 | end 37 | 38 | # 39 | def yaml 40 | s = [] 41 | s << "name : #{name}" 42 | s << "date : #{date.strftime('%Y-%m-%d')}" 43 | s << "version : #{version}" 44 | s << "codename: #{codename}" if codename 45 | s << "loadpath: #{loadpath}" if loadpath 46 | s << "" 47 | s << yaml_list('requires' , requires) 48 | s << yaml_list('replaces' , replaces) 49 | s << yaml_list('conflicts', conflicts) 50 | s.compact.join("\n") 51 | end 52 | 53 | # 54 | def yaml_list(name, list) 55 | return nil if list.empty? 56 | s = "\n#{name}:" 57 | list.each do |x| 58 | s << "\n- " + x.yaml.tabto(2) 59 | end 60 | s + "\n" 61 | end 62 | 63 | # This method is not using #to_yaml in order to ensure 64 | # the file is saved neatly. This may require tweaking. 65 | def save!(file=nil) 66 | file = file || @file || self.class.default_filename 67 | file = @root + file if String === file 68 | now! # update date 69 | File.open(file, 'w'){ |f| f << yaml } 70 | end 71 | 72 | 73 | -------------------------------------------------------------------------------- /work/trash/blog/2010-05-12-directory-store.rdoc: -------------------------------------------------------------------------------- 1 | = 2010-05-12 | Directory Store 2 | 3 | One of the more "controversial" design decisions made by POM was to store all 4 | metadata in a directory store --one file per peice of information. But there is 5 | a very specific reason for this. Namely that the version number needs to be 6 | accessible indpendently of the other metadata so that it could be easily 7 | updated. While the version could have been singled out, a few other fields were 8 | related to the version number, for instance, the release date. In addition 9 | other fields, such as the package name, are more commonly useful than 10 | most of the other information and it seems wasteful to load up a large amount 11 | of metadata for the sake of single entry. And so, taking all this into account, 12 | the most consitant result was to put all entires in separate files. 13 | There are plenty of advantages to this approach, as I have discussed else 14 | where, but their are also some unfortunate downsides. 15 | 16 | * Access of metadata via URL is hamperd since it takes multiple http requests to access the data. 17 | * Having to edit multiple files is obviously not as convenient as editing a single file. 18 | * Perhaps, worse of all, it simply strike some developers as "weird". 19 | 20 | I have, for some time tried to work out a potential alternative design, one that 21 | uses a small number of files, separating metadata according the needs mentioned 22 | above. While I am not 100% on the exact names of the files, I have worked out 23 | taht this alternative would have two files: 24 | 25 | * .version for name, version, release date, status, and also internal load path. 26 | * .package for dependencies, with suitable distinction for platforms. 27 | * .profile for all other metadata such as description, summary, authors, etc. 28 | 29 | This system would work just as well as the per file system as far as the 30 | separation of important concerns --the .version file would be updated regularly 31 | while the .profile file would rarely ever change. I am tempted to support this 32 | design, but I am a little hestitant to break backward compatability and concerned 33 | that future needs might prove this division of information more fragile than it 34 | presently seems. That's one of the big advantages of the file store design 35 | actually, adding new stuff to it has essentially no ramifications on the design. 36 | POM could support both designs, but I worry this would only further complicate 37 | matters. 38 | 39 | -------------------------------------------------------------------------------- /demo/1project/4news.md: -------------------------------------------------------------------------------- 1 | # News Class 2 | 3 | The News class encapsulates the current release notes for the project. 4 | It parses a text file by the name of NEWS in the same way that 5 | individual release entries are parsed in the History class. 6 | 7 | Given a NEWS project file containing: 8 | 9 | # Foo 1.2.1 (2010-10-18) 10 | 11 | 1. This is change 1. 12 | 2. This is change 2. 13 | 3. This is change 3. 14 | 15 | Some Dandy description of the 1.2.1 release. 16 | Notice this time that the changes are listed 17 | first and are numerically enumerated. 18 | 19 | The News class provides an interface to this information. 20 | The initializer takes the root directory for the project 21 | and looks for a file called +NEWS+, optionally ending 22 | in an extension such as +.txt+ or +.rdoc+, etc. 23 | 24 | news = POM::Project::News.new('example') 25 | 26 | Now we have access to the latest release notes as given in the 27 | the NEWS file. 28 | 29 | news.header.assert == '# Foo 1.2.1 (2010-10-18)' 30 | news.notes.assert.index('description of the 1.2.1') 31 | news.changes.assert.index('This is change 1') 32 | 33 | The header is further parsed into version, date and nickname if given. 34 | 35 | news.version.assert == '1.2.1' 36 | news.date.assert == '2010-10-18' 37 | 38 | If there is no NEWS file in a project, the News class will fallback 39 | to the HISTORY file's first rentry. 40 | 41 | Given a HISTORY project file containing: 42 | 43 | # RELEASE HISTORY 44 | 45 | ## 1.2.1 / 2010-10-18 46 | 47 | 1. This is change 1. 48 | 2. This is change 2. 49 | 3. This is change 3. 50 | 51 | Some Dandy description of the 1.2.1 release. 52 | Notice this time that the changes are listed 53 | first and are numerically enumerated. 54 | 55 | ## 1.1.0 / 2010-01-01 56 | 57 | 1. This is change 1. 58 | 2. This is change 2. 59 | 3. This is change 3. 60 | 61 | Some Dandy description of the 1.1.0 release. 62 | Notice this time that the changes are listed 63 | first and are numerically enumerated. 64 | 65 | Since the NEWS file is not present, we will get the top entry of the 66 | HISOTRY file above from the News object. 67 | 68 | news = POM::Project::News.new('example') 69 | news.header.assert == '## 1.2.1 / 2010-10-18' 70 | news.version.assert == '1.2.1' 71 | news.date.assert == '2010-10-18' 72 | 73 | Like the history parser, the news parser is farily simplistic, 74 | but again it is designed to handle the most common cases. 75 | 76 | -------------------------------------------------------------------------------- /work/trash/blog/2010-06-11-version-and-release-files.rdoc: -------------------------------------------------------------------------------- 1 | = 2010-06-11 | VERSION and RELEASE files 2 | 3 | I face an issue with supporting the VERSION file as the source for 4 | a project's current version information. Specifically, tools like 5 | Tim Pease's Mr. Bones and and Josh Nichols' Jeweler utilize the VERSION 6 | file as well, but in more limited formats. Bones looks for a simple text 7 | entry, e.g. "1.0.0", while Jeweler can accept this as well, but prefers 8 | the YAML layout: 9 | 10 | -- 11 | :major: 1 12 | :minor: 0 13 | :patch: 0 14 | :build: 15 | 16 | Both are fine as far as they go. But POM recognizes additional version 17 | information including *release date*, *code name*, and *revision number*. 18 | This additional information could be added to either format of VERSION file, 19 | but without modification to Bones and Jeweler, they will choke-on and/or 20 | clobber the additional information. I am somewhat hopeful they will repsond 21 | to my email asking for us to work out a common design, but after 4 days 22 | I have yet to heard from them :( I do not want to step on other's toes. 23 | As much as possible I am trying to conform POM to the way Rubyists already 24 | organize their projects, rather than the other way around. 25 | 26 | If I cannot reach an accord with other developers on a more flexible VERSION 27 | file, the alternative is to fallback to meta/ entries. Yes, those again. 28 | I considered using the RELEASE file instead. I know that a few developer's 29 | already use this file to store a description the the current release, so 30 | it follows logically. I thought it would be possible to utilize a document 31 | header to extract the information, whether it is a line of text or YAML 32 | front matter. But I releazed that the file is inteded for human readability 33 | and not machine, so it won't fly. 34 | 35 | Finally there are three important pieces of information that do not seem 36 | to fit properly among the version data, but must be readily available 37 | to many project tools. These are the project's *unixname*, it's *classname* 38 | and the *loadpath*. Initially I simply placed this information in with 39 | the version data, however, strictly speaking it is not version information. 40 | To avoid this I have utilize certain hueristics that infer the information 41 | from other aspects of the project. For example the unixname can be infered 42 | by the name of the directory in lib/. If for the some reason the project 43 | differs from the usual norms, this information can be specifically specified 44 | in the .meta/ directory. 45 | 46 | -------------------------------------------------------------------------------- /work/consider/profile.rdoc: -------------------------------------------------------------------------------- 1 | = Profile/Gemfile 2 | 3 | The +Profile+/+Gemfile+ is used to define information about a project. 4 | It has specifically defined fields, but also allows for arbitrary 5 | entries to meet custom project metadata usecases. 6 | 7 | The +Profile+/+Gemfile+ file can either be YAML or Ruby-based. 8 | 9 | An example YAML Profile/Gemfile: 10 | 11 | --- 12 | title: POM 13 | version: 1.0.1 14 | 15 | requirements: 16 | - facets 2.8+ 17 | - syckle (build) 18 | - ko (test) 19 | - qed 2.2+ (test) 20 | 21 | summary: Ruby Project Object Model 22 | license: Apache 2.0 23 | contact: Thomas Sawyer 24 | created: 2009-07-22 25 | 26 | description: 27 | POM provides a complete project layout standard 28 | and metadata system for Ruby developers. 29 | 30 | authors: 31 | - Thomas Sawyer 32 | 33 | resources: 34 | home : http://rubyworks.github.com/pom 35 | work : http://github.com/rubyworks/pom 36 | repo : git://github.com/rubyworks/pom.git 37 | docs : http://wiki.github.com/rubyworks/pom 38 | wiki : http://wiki.github.com/rubyworks/pom 39 | api : http://rubyworks.github.com/pom/rdoc 40 | mail : http://groups.google.com/group/rubyworks-mailinglist 41 | 42 | organization: RubyWorks 43 | 44 | copyright: Copyright (c) 2009 Thomas Sawyer 45 | 46 | manifest: 'MANIFEST' 47 | 48 | Here is the same example in Ruby: 49 | 50 | title 'POM' 51 | version '1.0.0' 52 | 53 | gem 'facets', '>=2.8' 54 | 55 | group :test do 56 | gem 'ko' 57 | gem 'qed', '>=2.2' 58 | end 59 | 60 | group :build do 61 | gem 'syckle' 62 | end 63 | 64 | summary 'Ruby Project Object Model' 65 | license 'Apache 2.0' 66 | contact 'Thomas Sawyer ' 67 | created '2009-07-22' 68 | 69 | description %{ 70 | POM provides a complete project layout standard 71 | and metadata system for Ruby developers. 72 | }.strip 73 | 74 | authors ['Thomas Sawyer'] 75 | 76 | resources( 77 | 'home' => 'http://rubyworks.github.com/pom', 78 | 'code' => 'http://github.com/rubyworks/pom', 79 | 'repo' => 'git://github.com/rubyworks/pom.git', 80 | 'docs' => 'http://wiki.github.com/rubyworks/pom', 81 | 'wiki' => 'http://wiki.github.com/rubyworks/pom', 82 | 'api' => 'http://rubyworks.github.com/pom/rdoc', 83 | 'mail' => 'http://groups.google.com/group/rubyworks-mailinglist' 84 | ) 85 | 86 | organization 'RubyWorks' 87 | 88 | copyright 'Copyright (c) 2009 Thomas Sawyer' 89 | 90 | manifest 'MANIFEST' 91 | 92 | The +Profile+/+Gemfile+ is parsed, normalized and saved to another YAML file, 93 | called +.prospect+. This file is then used by all tools to access the projects 94 | metadata. In other words, the +Profile+/+Gemfile+ is a convenience form. 95 | 96 | 97 | -------------------------------------------------------------------------------- /work/deprecated/gemfile-refit/bundler.rb: -------------------------------------------------------------------------------- 1 | # TODO: Bring the Bundler Gemfile and the POM Gemfile in mroe name alignment. 2 | 3 | # TODO: Should the date be specififed? 4 | 5 | # TODO: Do we need quite so many aliases? 6 | 7 | require 'bundler/dsl' 8 | 9 | module POM 10 | GEMFILE_ATTRIBUTES = [ 11 | :name, 12 | :version, 13 | :codename, :code, 14 | :date, :release_date, :released, 15 | :namespace, 16 | :loadpath, :path, 17 | :requires, :requirements, :conflicts, :replaces, :provides, 18 | :manifest 19 | ] 20 | end 21 | 22 | # This Bundler extension allows POM to use the Gemfile for project 23 | # metadata beyond dependency requirements. 24 | module Bundler 25 | class Dsl 26 | # Where as POM can handle some project packaging metadata. 27 | # Bundler can just ignore these (at least for now). 28 | def method_missing(s, *a, &b) 29 | if !POM::GEMFILE_ATTRIBUTES.include?(s) 30 | super(s, *a, &b) 31 | end 32 | end 33 | 34 | def self.evaluate(gemfile, lockfile, unlock) 35 | text = Bundler.read_file(gemfile.to_s) 36 | if /\A---/ =~ text 37 | builder = from_yaml(text) 38 | else 39 | builder = new 40 | builder.instance_eval(text, gemfile.to_s, 1) 41 | end 42 | builder.to_definition(lockfile, unlock) 43 | end 44 | 45 | # 46 | def self.from_yaml(text) 47 | require 'yaml' 48 | data = YAML.load(text) 49 | from_hash(data) 50 | end 51 | 52 | # 53 | def self.from_hash(data) 54 | builder = new 55 | data.each do |key, value| 56 | case key.to_s 57 | when 'requires', 'requirements' 58 | value.each do |entry| 59 | entry = parse_gem(entry) 60 | builder.gem(*entry) 61 | end 62 | #when 'conflicts' 63 | # value.each do |entry| 64 | # builder.gem(entry) 65 | # end 66 | else 67 | builder.__send__(key, value) 68 | end 69 | end 70 | builder 71 | end 72 | 73 | OP_TRANS = {'+'=>'>=', '-'=>'<', '~'=>'~>'} 74 | 75 | # TODO: make the gem method smarter in Bundler itself 76 | def self.parse_gem(entry) 77 | if String === entry 78 | entry.gsub!(/([>=<])\ /, '\1') 79 | parts = entry.split(/\s+/) 80 | name = parts.shift 81 | if parts.last =~ /\((.*?)\)/ 82 | parts.pop 83 | group = $1.split(/\s+/) 84 | end 85 | parts.map! do |part| 86 | if md = /^(.*?)([+-~])$/.match(part) 87 | OP_TRANS[md[2]] + md[1] 88 | else 89 | part 90 | end 91 | end 92 | entry = [name] + parts 93 | entry << {:group=>group} if group 94 | entry 95 | else 96 | entry 97 | end 98 | end 99 | 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/resolver/gemcutter.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | class Resolver 4 | 5 | require 'pom/resolver/source' 6 | 7 | # 8 | class GemCutter 9 | 10 | # 11 | attr :project_gem 12 | 13 | # 14 | attr :missing 15 | 16 | # 17 | #attr :available 18 | 19 | # 20 | #attr :matches 21 | 22 | # 23 | attr :cache 24 | 25 | # 26 | def initialize(project_gem) 27 | require 'open-uri' 28 | 29 | @project_gem = project_gem 30 | 31 | @missing = [] 32 | @cache = {} 33 | end 34 | 35 | # Access to +@cache+ plus current project. 36 | def gems 37 | @gems ||= ( 38 | @cache.merge(project_gem.name=>[project_gem]) 39 | ) 40 | end 41 | 42 | # Featch all potetnial gems for the +project_gem+. 43 | def fetch 44 | fetch_requirements(project_gem.dependencies) 45 | end 46 | 47 | # Fetch all versions of all gems in given requirments and their 48 | # sub-requirements. This builds up the gem +@cache+ with Gem instances. 49 | def fetch_requirements(requirements) 50 | return if requirements.empty? 51 | 52 | names = requirements.map{ |name, constraint| name } 53 | names = names.reject{ |name| already_fetched?(name) } 54 | names = names.uniq 55 | 56 | gems = get(*names) 57 | 58 | requirements.each do |name, constraint| 59 | next if already_fetched?(name) 60 | unless gems.find{ |gem| gem.name == name } 61 | self.missing << [name, constraint] 62 | end 63 | end 64 | 65 | fetch_requirements(unfetched_requirements(gems)) 66 | end 67 | 68 | # 69 | def get(*names) 70 | names = names - @cache.keys # remove names already fetched 71 | return [] if names.empty? 72 | url = "http://rubygems.org/api/v1/dependencies?gems=#{names.join(',')}" 73 | $stderr.puts "fetching #{url}" if ($DEBUG or $VERBOSE) 74 | gems = Marshal.load(open(url)) 75 | gems = gems.map{ |gem| Gem.new(gem) } 76 | gems.each do |gem| 77 | @cache[gem.name] ||= [] 78 | @cache[gem.name] << gem 79 | end 80 | #@cache.merge!(gems.group_by{ |gem| gem[:name] }) 81 | return gems 82 | end 83 | 84 | # 85 | def unfetched_requirements(gems) 86 | reqs = [] 87 | gems.each do |gem| 88 | gem.dependencies.each do |name, constraint| 89 | reqs << [name, constraint] unless already_fetched?(name) 90 | end 91 | end 92 | reqs.uniq 93 | end 94 | 95 | # Has a gem already been fetched? 96 | def already_fetched?(name) 97 | @cache.key?(name) 98 | end 99 | 100 | end 101 | 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /work/deprecated/profile/bundlerable.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | class Profile 4 | 5 | # Add some features for compatibility with Bundler Gemfile syntax. 6 | module Bundlerable 7 | 8 | # 9 | def initialize_mixins 10 | @_source = nil 11 | @_group = [] 12 | @_platform = [] 13 | 14 | super if defined?(super) 15 | end 16 | 17 | # Add a requirement. 18 | def gem(name, *args) 19 | opts = Hash == args.last ? args.pop : {} 20 | 21 | name, *cons = name.split(/\s+/) 22 | 23 | if md = /\((.*?)\)/.match(cons.last) 24 | cons.pop 25 | group = md[1].split(/\s+/) 26 | opts['group'] = group 27 | end 28 | 29 | opts['name'] = name 30 | opts['version'] = cons.join(' ') unless cons.empty? 31 | 32 | opts['source'] ||= @_source if @_source 33 | 34 | unless @_group.empty? 35 | opts['group'] ||= [] 36 | opts['group'] += @_group 37 | end 38 | 39 | unless @_platform.empty? 40 | opts['platform'] ||= [] 41 | opts['plarform'] += @_platform 42 | end 43 | 44 | profile.add_requirement(opts) 45 | end 46 | 47 | # --- Bundler Gemfile Compatibility --- 48 | 49 | def source(source) #:yield: 50 | @_source = source 51 | yield 52 | ensure 53 | @_source = nil 54 | end 55 | 56 | # For use with defining dependencies with the +gem+ method. 57 | # This allows for compatibility with Bundler Gemfile. 58 | def group(*names) #:yield: 59 | @_group.concat names 60 | yield 61 | ensure 62 | names.each{@_group.pop} 63 | end 64 | 65 | # This allows for compatibility with Bundler Gemfile. 66 | def platform(*names) #:yield: 67 | @_platform.concat names 68 | yield 69 | ensure 70 | names.each{@_platform.pop} 71 | end 72 | 73 | # Alias for #platform. 74 | alias_method :platforms, :platform 75 | 76 | # FIXME: Do we need this? 77 | def path(path, options={}, source_options={}, &blk) 78 | end 79 | 80 | # This one sucks. Talk about favoring one SCM over another! 81 | # Handle submodules yourself like a real developer! 82 | def git(*) 83 | msg = "The `git` method is incompatible with POM.\n" / 84 | "Consider using submodules or an alternate tool\n" / 85 | "to manager vendored sources, and use the `path`\n" \ 86 | "option instead." 87 | raise msg 88 | end 89 | 90 | # This one can blow! 91 | def gemspec(*) 92 | msg = "The `gemspec` method is incompatible with POM/.\n" / 93 | "POM will generate a gemspec from the Profile." 94 | raise msg 95 | end 96 | 97 | # --- End Bundler Gemfile Compatibility --- 98 | 99 | end 100 | 101 | end 102 | 103 | end 104 | -------------------------------------------------------------------------------- /work/trash/blog/2010-07-22-final-designs.md: -------------------------------------------------------------------------------- 1 | # 2010-07-22 | Final Designs 2 | 3 | Over the years POM has evolved. Many considerations have contributed to 4 | changes in it's design. The most significant of which was the recent move away 5 | from the directory-based "one piece of data per file" configuration, to the 6 | the YAML-format file-based design. On the whole this has been a positive change. 7 | It's has proven even better than anticipated. Nonetheless the change has opened 8 | up a couple of issues that the previous design addressed. 9 | 10 | One of these is the question of how to reliably determine the location 11 | of a project's root directory. The `.meta/` directory worked quite well 12 | in this regard, whereas the PACKAGE file (being the essential POM file) 13 | is not quite as suited --the name is too generic, the name can also 14 | come in too many flavors (PACAKGE, Package, Package.yml, PACKAGE.ymal, etc.) 15 | which makes it less efficient to detect and access. Worse still, it 16 | effectively presupposes the use of POM to gain the benefit of a reliable 17 | root marker. 18 | 19 | I touched on the first and last of these in my previous blog entry. The second 20 | bares further explination. It is, of course, easy enough for a file system 21 | to do a file name search. Currently POM is littered with code like: 22 | 23 | Dir.glob('Package{,.yml,.yaml}', File::FNM_CASEFOLD) 24 | 25 | Perhaps not as simple as we might like, but perfectly acceptable, and if that 26 | were the only naggle, I wouldn't think twice about it. But also consider a web 27 | agent trying to gather information about projects. In this case 28 | access to the file system is much more limited and would require a silly 29 | brute force attempt on every capital and lowercase combination. The web agent 30 | would be better off pulling down all the files and doing the above search, as 31 | inefficent as that would be. The use of fixed names just makes things easier, 32 | arguably a corollary of the "convention over configuration" meme. 33 | 34 | Taking these factors into consideration, I have narrowed the solutions for 35 | addressing the issue to three: 36 | 37 | 1. Use a dummy .ruby file, which can retain references to the names of the actual metadata files. 38 | 2. Use a `.ruby/` directory, where the metadata files can reside. 39 | 3. Use a `.meta/` or `.project/` directory where a `ruby` file resides and the metadata files can reside. 40 | 41 | The later is probably the best approach, in that it is the most generic 42 | but still allows us to easiy identify a ruby project. The `.meta/ruby` or 43 | `.project/ruby` file could contain a list of Ruby engines that the project 44 | has been tested against. On the other hand the use of .ruby readily identifies 45 | a project as a Ruby project --one does not have to drill down into `.meta` 46 | or `.project` to figure it out. Although both are hidden files anyway, so why 47 | does it really matter? 48 | 49 | For the moment the dummy file approach is being utilized. But the final design 50 | still bares further consideration. 51 | 52 | -------------------------------------------------------------------------------- /lib/gemdo/cli.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'ostruct' 3 | 4 | module GemDo 5 | 6 | # GemDo's command-line interface. 7 | # 8 | # Also is the namespace for all GemDo command-line subclasses. 9 | class CLI 10 | 11 | # Add command class to the list of available commands. 12 | def self.<<(command_class) 13 | command_classes << command_class 14 | end 15 | 16 | # 17 | def self.command_classes 18 | @command_classes 19 | end 20 | 21 | # 22 | def self.run 23 | #require_commands 24 | new.run 25 | end 26 | 27 | # 28 | #def self.require_commands 29 | # cmds = Dir.glob(File.join(File.dirname(__FILE__), 'commands', '*.rb')) 30 | # cmds.each{ |lib| require lib } 31 | #end 32 | 33 | # 34 | def run 35 | begin 36 | run_command 37 | rescue => err 38 | raise(err) if $DEBUG 39 | $stderr.puts err.message 40 | end 41 | end 42 | 43 | private 44 | 45 | # Get a list of command classes. 46 | def commands 47 | self.class.command_classes 48 | end 49 | 50 | # 51 | def run_command 52 | job = parse_command 53 | 54 | begin 55 | exec ["gemdo-#{job}", *ARGV] 56 | #cmd = commands.find{ |c| c.to_s.downcase == job } 57 | rescue NameError 58 | puts "Unrecognized command -- #{job}" 59 | exit 1 60 | end 61 | 62 | cmd.run 63 | end 64 | 65 | # 66 | def parse_command 67 | job = ARGV.shift 68 | 69 | case job 70 | when '--help', '-h', nil 71 | puts(manpage || COMMAND_HELP) 72 | when 'help' 73 | name = ARGV.shift 74 | puts(manpage(name) || COMMAND_HELP) 75 | exit 76 | end 77 | 78 | job 79 | end 80 | 81 | COMMAND_HELP = <<-END.gsub(/^\ {6}/, '') 82 | gemdo [OPTIONS ...] 83 | 84 | BUILT-IN COMMANDS: 85 | about show a summary of project 86 | show [name] show specific metadata entry 87 | news show the last release history entry 88 | bump bump version number 89 | init make a starter Ruby project 90 | help show this help message 91 | 92 | COMMON OPTIONS: 93 | --debug activate debug mode 94 | 95 | Use 'gemdo --help' for command options. 96 | END 97 | 98 | # TODO: 99 | # resolve resolve dependencies 100 | # gemspec make a gemspec file 101 | 102 | def manpage(name=nil) 103 | name = name ? "gemdo-#{name}" : "gemdo" 104 | dir = File.dirname(__FILE__) + '/../../man' 105 | file = File.join(dir, name + '.ronn') 106 | File.exist?(file) ? File.read(file) : nil 107 | end 108 | 109 | # 110 | def require_command(name) 111 | end 112 | 113 | end#class Command 114 | 115 | end 116 | -------------------------------------------------------------------------------- /lib/gemdo/cli/bump.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | 6 | # Command to bump version number. 7 | # 8 | class Bump < Base 9 | 10 | attr :project 11 | 12 | # 13 | def initialize 14 | @project = POM::Project.find 15 | @slots = [] 16 | @state = nil 17 | @force = false 18 | end 19 | 20 | # 21 | def run 22 | parse 23 | execute 24 | end 25 | 26 | # Returns instance of option parser. 27 | def parser 28 | @parser ||= OptionParser.new do |opt| 29 | opt.banner = "pom bump [OPTIONS | ENTRY]" 30 | 31 | opt.on("--major", "-M", "bump major version number") do 32 | @slots << :major 33 | end 34 | 35 | opt.on("--minor", "-m", "bump minor version number") do 36 | @slots << :minor 37 | end 38 | 39 | opt.on("--patch", "-p", "bump patch version number") do 40 | @slots << :patch 41 | end 42 | 43 | opt.on("--build", "-b", "bump build version number") do 44 | @slots << :build 45 | end 46 | 47 | opt.on("--state", "-s", "bump version state") do |term| 48 | @slots << :state 49 | end 50 | 51 | opt.on("--no-write", "-n", "do not write version change") do 52 | $DRYRUN = true 53 | end 54 | 55 | opt.on("--force", "-f", "force otherwise protected action") do 56 | @force = true 57 | end 58 | 59 | opt.on("--debug", "run in debug mode") do 60 | $DEBUG = true 61 | $VERBOSE = true 62 | end 63 | 64 | opt.on_tail("--help", "-h", "display this help message") do 65 | puts opt 66 | exit 67 | end 68 | end 69 | end 70 | 71 | # 72 | def parse 73 | parser.parse! 74 | 75 | @entry = ARGV.last 76 | 77 | if @entry == 'help' 78 | puts parser 79 | exit 80 | end 81 | end 82 | 83 | # 84 | def execute 85 | if POM::VersionNumber::STATES.include?(@entry) 86 | @state = @entry.to_sym 87 | end 88 | 89 | if POM::VersionNumber::SLOTS.include?(@entry) 90 | @slots << @entry.to_sym 91 | @entry = nil 92 | end 93 | 94 | raise "Why bump if you know what you want, silly?" if @entry && !@slots.empty? 95 | 96 | new_version = @entry ? POM::VersionNumber.new(@entry) : project.version 97 | 98 | @slots.each do |slot| 99 | new_version = new_version.bump(slot) 100 | end 101 | 102 | if @state 103 | new_version = new_version.restate(@state) 104 | end 105 | 106 | if new_version > project.version or @force 107 | project.version = new_version 108 | #project.save_version! unless $TRIAL 109 | else 110 | if new_version < project.version 111 | $stderr.puts "pom: Going backwards in time?" 112 | $stderr.outs " New version is older than current version." 113 | #$stderr.outs " Use --force to fly the TARDIS." 114 | end 115 | end 116 | 117 | puts(project.version) 118 | end 119 | 120 | end 121 | 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /work/trash/blog/2010-07-27-no-version-necessary.md: -------------------------------------------------------------------------------- 1 | 62 | 63 | 69 | 70 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/history.rdoc: -------------------------------------------------------------------------------- 1 | = History Class 2 | 3 | The history class encapsulates this list of release 4 | made by a project. It parses a text file by the 5 | name of HISTORY into it indivdual relase entries. 6 | 7 | Given a HISTORY project file containing: 8 | 9 | = RELEASE HISTORY 10 | 11 | == 1.2.0 / 2010-10-18 12 | 13 | Some Dandy description of the 1.2.0 release. 14 | This is multiline description. 15 | 16 | Changes: 17 | 18 | * This is change 1. 19 | * This is change 2. 20 | * This is change 3. 21 | 22 | 23 | == 1.1.0 | 2010-06-06 | "Happy Days" 24 | 25 | Some Dandy description of the 1.1.0 release. 26 | This is multiline description. Notice the 27 | header varies from the first. 28 | 29 | The description can even have multiple paragraphs. 30 | 31 | Changes: 32 | 33 | * This is change 1. 34 | * This is change 2. 35 | * This is change 3. 36 | 37 | 38 | == 1.0.0 / 2010-04-30 39 | 40 | Some Dandy description of the 1.0.0 release. 41 | This is multiline description. Notice that 42 | the "changes:" label isn't strictly needed. 43 | 44 | * This is change 1. 45 | * This is change 2. 46 | * This is change 3. 47 | 48 | 49 | == 0.9.0 / 2010-04-10 50 | 51 | 1. This is change 1. 52 | 2. This is change 2. 53 | 3. This is change 3. 54 | 55 | Some Dandy description of the 0.9.0 release. 56 | Notice this time that the changes are listed 57 | first and are numerically enumerated. 58 | 59 | The History class provides an interface to this information. 60 | The initializer takes the root directory for the project 61 | and looks for a file called +HISTORY+, optionally ending 62 | in an extension such as +.txt+ or +.rdoc+, etc. 63 | 64 | history = POM::History.new('tmp/example') 65 | 66 | Now we should have an enumeration of each release entry in 67 | the HISTORY file. 68 | 69 | history.releases.size.assert == 4 70 | 71 | The non-plurual #release method will give us the first entry. 72 | And we can see that it has been parsed into its component 73 | attributes. 74 | 75 | history.release.header.assert == '== 1.2.0 / 2010-10-18' 76 | history.release.notes.assert.index('description of the 1.2.0') 77 | 78 | The header is further parsed into version, date and nickname if given. 79 | 80 | history.release.version.assert == '1.2.0' 81 | history.release.date.assert == '2010-10-18' 82 | 83 | We should see like results for the other release entries. 84 | 85 | history.releases[2].version.assert == '1.0.0' 86 | history.releases[2].date.assert == '2010-04-30' 87 | 88 | history.releases[2].header.assert == '== 1.0.0 / 2010-04-30' 89 | history.releases[2].notes.assert.index('description of the 1.0.0') 90 | history.releases[2].changes.assert.index('This is change 1') 91 | 92 | Even though there are variations in the formats of each entry they are 93 | still parsed correctly. For example the second release has a nick name. 94 | 95 | history.releases[1].nickname.assert == 'Happy Days' 96 | 97 | And the last entry has it's changes listed before the description. 98 | 99 | history.releases[3].header.assert == '== 0.9.0 / 2010-04-10' 100 | history.releases[3].notes.assert.index('description of the 0.9.0') 101 | history.releases[3].changes.assert.index('This is change 1') 102 | 103 | The history parser is farily simplistic, but it is flexibile enough 104 | to parse the most common HISTORY file formats. 105 | 106 | -------------------------------------------------------------------------------- /work/consider/version.rb: -------------------------------------------------------------------------------- 1 | # = VersionNumber 2 | # 3 | # VersionNumber is a simplified form of a Tuple class 4 | # desgined specifically for dealing with version numbers. 5 | # 6 | # == Authors 7 | # 8 | # * Thomas Sawyer (7rans) 9 | # 10 | # == Todo 11 | # 12 | # * Maybe add Kernel method #version ? 13 | # 14 | # * If Tuple were standard part of Ruby this probably would 15 | # not be needed or at least might be simple sublcass instead. 16 | # 17 | # == Copyright 18 | # 19 | # Copyright (c) 2005 Thomas Sawyer 20 | # 21 | # Ruby License 22 | # 23 | # This module is free software. You may use, modify, and/or redistribute this 24 | # software under the same terms as Ruby. 25 | # 26 | # This program is distributed in the hope that it will be useful, but WITHOUT 27 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 28 | # FOR A PARTICULAR PURPOSE. 29 | 30 | # = VersionNumber 31 | # 32 | # VersionNumber is a simplified form of a Tuple class 33 | # desgined specifically for dealing with version numbers. 34 | # 35 | class VersionNumber #< Tuple 36 | 37 | #include Enumerable 38 | include Comparable 39 | 40 | def initialize( *args ) 41 | args = args.join('.').split(/\W+/) 42 | @self = args.collect { |i| i.to_i } 43 | end 44 | 45 | def to_s ; @self.join('.') ; end 46 | 47 | # This is here only becuase File.join calls it instead of to_s. 48 | 49 | def to_str ; @self.join('.') ; end 50 | 51 | def inspect 52 | @self.to_s 53 | end 54 | 55 | def [](i) 56 | @self.fetch(i,0) 57 | end 58 | 59 | # "Spaceship" comparsion operator. 60 | 61 | def <=>( other ) 62 | #other = other.to_t 63 | [@self.size, other.size].max.times do |i| 64 | c = @self[i] <=> other[i] 65 | return c if c != 0 66 | end 67 | 0 68 | end 69 | 70 | # For pessimistic constraint (like '~>' in gems) 71 | 72 | def =~( other ) 73 | #other = other.to_t 74 | upver = other.dup 75 | upver[0] += 1 76 | @self >= other and @self < upver 77 | end 78 | 79 | # Major is the first number in the version series. 80 | 81 | def major ; @self[0] ; end 82 | 83 | # Minor is the second number in the version series. 84 | 85 | def minor ; @self[1] || 0 ; end 86 | 87 | # Teeny is third number in the version series. 88 | 89 | def teeny ; @self[2] || 0 ; end 90 | 91 | # 92 | def bump(which=:teeny) 93 | case which 94 | when :major 95 | self.class.new(major+1) 96 | when :minor 97 | self.class.new(major, minor+1) 98 | when :teeny 99 | self.class.new(major, minor, teeny+1) 100 | else 101 | # ??? 102 | end 103 | end 104 | 105 | # Delegate to the array. 106 | 107 | def method_missing( sym, *args, &blk ) 108 | @self.send(sym, *args, &blk ) rescue super 109 | end 110 | 111 | # Parses a string constraint returning the operation as a lambda. 112 | 113 | def self.constraint_lambda( constraint ) 114 | op, val = *parse_constraint( constraint ) 115 | lambda { |t| t.send(op, val) } 116 | end 117 | 118 | def self.parse_constraint( constraint ) 119 | constraint = constraint.strip 120 | re = %r{^(=~|~>|<=|>=|==|=|<|>)?\s*(\d+(:?[-.]\d+)*)$} 121 | if md = re.match( constraint ) 122 | if op = md[1] 123 | op = '=~' if op == '~>' 124 | op = '==' if op == '=' 125 | val = new( *md[2].split(/\W+/) ) 126 | else 127 | op = '==' 128 | val = new( *constraint.split(/\W+/) ) 129 | end 130 | else 131 | raise ArgumentError, "invalid constraint" 132 | end 133 | return op, val 134 | end 135 | 136 | end 137 | -------------------------------------------------------------------------------- /work/deprecated/alt_version/version_file.rb: -------------------------------------------------------------------------------- 1 | require 'pom/version_helper' 2 | 3 | module POM 4 | 5 | # Access to VERSION file. This class supports plain-text 6 | # and YAML formatted files. 7 | class VersionFile 8 | 9 | # 10 | include VersionHelper 11 | 12 | # 13 | FILE_PATTERN = 'VERSION{,.txt,.yml,.yaml}' 14 | 15 | # 16 | def self.file_pattern 17 | FILE_PATTERN 18 | end 19 | 20 | # 21 | def self.find(root) 22 | root.glob(file_pattern, File::FNM_CASEFOLD).first 23 | end 24 | 25 | # 26 | def initialize(root, opts={}) 27 | @root = Pathname.new(root) 28 | @file = opts[:file] || self.class.find(root) 29 | read_version_file 30 | end 31 | 32 | # Project root. 33 | attr :root 34 | 35 | # Version file. 36 | attr :file 37 | 38 | # Version number. 39 | attr_reader :version 40 | 41 | # 42 | attr_accessor :name 43 | 44 | # Date this version was released. 45 | attr_reader :date 46 | 47 | # Code name for this release, e.g. "Hardy Haron". 48 | attr_accessor :codename 49 | 50 | # Integer(esque) build number. 51 | attr_accessor :buildno 52 | 53 | # 54 | def version=(raw) 55 | @version = VersionNumber.new(raw) 56 | end 57 | 58 | # 59 | def date=(val) 60 | case val 61 | when Date, Time, DateTime 62 | @date = val 63 | else 64 | @date = Time.parse(val) if val 65 | end 66 | end 67 | 68 | # Current status (beta, alpha, pre, rc, etc.) 69 | def status 70 | if md = /(\w+)/.match(version.build.to_s) 71 | md[1].to_sym 72 | end 73 | end 74 | 75 | # Current version of the project. Will be a dot separated 76 | # string, e.g. "1.0.0". 77 | def to_s 78 | @version.to_s 79 | end 80 | 81 | # 82 | def to_a 83 | @version.to_a 84 | end 85 | 86 | # 87 | def read_version_file 88 | if file 89 | text = File.read(file).strip 90 | if yaml?(file, text) 91 | @type = :yaml 92 | release = parse_release_hash(YAML.load(text)) 93 | else 94 | @type = :text 95 | release = parse_release_stamp(text) 96 | end 97 | self.version = release[:version] 98 | self.date = release[:date] 99 | self.codename = release[:codename] 100 | end 101 | end 102 | 103 | # TODO: handle jeweler and non-jeweler yaml? 104 | def save_version_file 105 | now! 106 | case type 107 | when :text 108 | File.write(file, 'w'){ |f| f << version.to_s } 109 | when :yaml 110 | File.write(file, 'w'){ |f| f << version.to_h.to_yaml } 111 | else 112 | File.write(file, 'w'){ |f| f << version.to_h.to_yaml } 113 | end 114 | end 115 | 116 | ## This method is not using #to_yaml in order to ensure 117 | ## the file is saved neatly. This may require tweaking. 118 | #def save_version_file_in_yaml 119 | # File.open(file, 'w') do |f| 120 | # #f.puts "name : #{name}" 121 | # f.puts "major: #{major}" 122 | # f.puts "minor: #{minor}" if minor 123 | # f.puts "patch: #{patch}" if patch 124 | # f.puts "build: #{build}" if build 125 | # f.puts "date : #{date.strftime('%Y-%m-%d')}" 126 | # end 127 | #end 128 | 129 | private 130 | 131 | # 132 | def yaml?(file, text) 133 | return true if file.extname == '.yml' 134 | return true if file.extname == '.yaml' 135 | return true if text[0,3] == '---' 136 | return true if text.index('major:') 137 | false 138 | end 139 | 140 | end 141 | 142 | end 143 | 144 | -------------------------------------------------------------------------------- /work/deprecated/profile/inference.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | class Profile 4 | 5 | # This module encapulates a few algorithms that attempt to infer 6 | # project metdata from various "common" parts of a project 7 | # For example, the project's name may be able to be infered from 8 | # a ruby file in the lib/ directory. 9 | # 10 | class Inference 11 | 12 | # 13 | def self.inference_methods 14 | @inference_methods ||= [] 15 | end 16 | 17 | # 18 | def self.method_added(name) 19 | if name.to_s =~ /^infer_/ 20 | inference_methods << name 21 | end 22 | end 23 | 24 | # 25 | attr :project 26 | 27 | # 28 | attr :root 29 | 30 | # Table of inferable metadata. 31 | attr :table 32 | 33 | # 34 | def initialize(project) 35 | @project = project 36 | @root = project.root 37 | @table = {} 38 | 39 | infer! 40 | end 41 | 42 | # 43 | def infer! 44 | self.class.inference_methods.each do |meth| 45 | send(meth) 46 | end 47 | end 48 | 49 | # 50 | def apply(profile) 51 | table.each do |key, value| 52 | profile[key] = value unless profile.value?(key) 53 | end 54 | end 55 | 56 | # Extract version from VERSION file. 57 | #-- 58 | # TODO: It might be better to support the VERSION file as a separate class, 59 | # in the same way as we support README and MANFIEST. 60 | # 61 | # TODO: Support for codename? 62 | #++ 63 | def infer_version_from_version_file 64 | vfile = root.glob('VERSION{,.txt,.yml,.yaml}', File::FNM_CASEFOLD).first 65 | if vfile && vfile.exist? 66 | text = vfile.read.strip 67 | case text 68 | when /\A---/ 69 | type = :yaml 70 | when /\A\d+[.]/ 71 | type = :text 72 | when /[:]/ 73 | type = :yaml 74 | else 75 | type = nil 76 | end 77 | 78 | case type 79 | when :yaml 80 | data = YAML.load(text) 81 | data = data.inject({}){|h,(k,v)| h[k.to_sym]=v; h} 82 | text = data.values_at(:major,:minor,:patch,:build).compact.join('.') 83 | set :version, text 84 | when :text 85 | set :version, text 86 | end 87 | end 88 | end 89 | 90 | # 91 | #-- 92 | # TODO: maybe in meta/ too? 93 | #++ 94 | def infer_manifest 95 | if file = root.glob('manifest{,.txt}', :casefold).first 96 | set :manifest, File.basename(file) 97 | end 98 | end 99 | 100 | # 101 | def infer_from_readme 102 | readme = project.readme 103 | set :name, readme.name 104 | set :title, readme.title 105 | set :description, readme.description 106 | set :copyrights, readme.copyright 107 | set :authors, readme.authors 108 | set :resources, readme.resources 109 | end 110 | 111 | # Failing to find a name for the project, the last hope 112 | # is to discern it from the lib files. 113 | def infer_name_from_lib 114 | if file = root.glob('lib/*.rb').first 115 | name = file.basename.to_s.chomp('.rb') 116 | set :name, name 117 | end 118 | end 119 | 120 | private 121 | 122 | # 123 | def set(name, value) 124 | if value && @table[name.to_sym].nil? 125 | @table[name.to_sym] = value 126 | end 127 | end 128 | 129 | end 130 | 131 | end 132 | 133 | end 134 | 135 | -------------------------------------------------------------------------------- /work/trash/blog/2010-07-24-seeking-perfection.md: -------------------------------------------------------------------------------- 1 | # 2010-07-23 | Seeking Perfection 2 | 3 | I realize my biggest problem it working out the various details of design for 4 | a Ruby POM primarily stem from my over baring desire to achieve "perfection", 5 | or at least some close semblance there-of. Though there is likely none to be 6 | found. Which is why I continually toil over the various choices. At some point 7 | a mere choice must be made between varying imperfect solutions. (Not to mention 8 | the fact that I would really like to get this done and move on to other 9 | projects!) Despite this I keep seeking. 10 | 11 | The reoccurring question of POM --indeed the main point of POM is to specifying 12 | a file system based data structure for project information. There are probably 13 | a million ways to sundown to do this, and I assure you I've traveled too many. 14 | From a single unified YAML file to a fully directory-based one-file-per-field 15 | design, I have teased out their various advantages and disadvantages. My 16 | conclusion is that the best solution lies somewhere between the extremes. 17 | It is better to have small files, which allows greater atomicity in access, but 18 | it is better to have files of multiple related information for quick access and 19 | ease of editing. So for that last few months I have been attempting to ring out 20 | the best compromise. This endeavor gave rise to the PACKAGE, PROFILE and 21 | REQUIRE files. I thought I had finally reach the best choice, but, as is often 22 | the case in exploring new avenues, there remains some issues. 23 | 24 | 1. Having deprecated the use of a `.meta/` directory. There is no good way 25 | to reliably ascertain the project's root directory. 26 | 2. There is nothing in particular that clearly indicates that the project 27 | is a Ruby project. 28 | 3. Too many name variations (PACKAGE, Package.yaml, ...). 29 | 30 | The remaining possibilities fall into two groups. Using a sub-directory 31 | vs. using toplevel files. Of these, there three significant designs. 32 | 33 | The simplest solution relative to what POM has now, would be to simply add 34 | a file to identify the project as Ruby, using it as a marker be able 35 | to detect the root directory. To give the file contents some use it could 36 | contain a list of the versions of Ruby the project has been tested against. 37 | 38 | .ruby 39 | PACKAGE 40 | PROFILE 41 | REQUIRE 42 | 43 | One downside to this approach it that it tends to further clutter up 44 | the root directory, where the README is really the most important file. 45 | To remedy we might merge REQUIRE into PACKAGE, leaving us only: 46 | 47 | .ruby 48 | PACKAGE 49 | PROFILE 50 | 51 | We could also rename the package file in such a way as to use it as a Ruby 52 | root marker as well. Something likes `Rubyfile` or `Ruby.yml`, in which would 53 | be the all the package and requirements information. Thus leaving us with: 54 | 55 | Profile 56 | Rubyfile 57 | 58 | I think for most Rubyists that would seem a more appealing result, as it means 59 | only two extra file, fits is well with ones Rakefile, and says clearly 60 | "this is Ruby", without looking for hidden files. 61 | 62 | The last option would move the metadata files into a `.ruby/` subdirectory. 63 | 64 | .ruby/ 65 | package 66 | profile 67 | require 68 | 69 | On the downside this hides important metadata away in hidden directory. 70 | To remedy that, instead of `.ruby/` we could use `ruby/` or `RUBY`/. 71 | But it still places the data a directory down which makes it slightly less 72 | convenient to access. In addition, `ruby/` gets kind of lost 73 | among the other directories, while `RUBY/` -- I don't know, it just sort 74 | of sticks out oddly. 75 | 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GemDo 2 | 3 | (c) 2009 Rubyworks (BSD-2-Clause License) 4 | 5 | [Homepage](http://rubyworks.github.com/gemdo) / 6 | [Source Code](http://github.com/rubyworks/gemdo) 7 | 8 | 9 | GemDo defines a standard Ruby Project Object Model (GemDo), encompassing a 10 | standard project layout, project files and thier formats. The standard is 11 | specified via an implementation in Ruby, suited for use by Ruby projects 12 | tools. GemDo supports the most common practices of the Ruby community at large. 13 | But also defines some new practices to shore up weaker points or altogether 14 | missing features. 15 | 16 | 17 | ## WHY? 18 | 19 | Consider the state of Ruby project standards today. While a number of well 20 | aheared practices have evolved over the years, largely due to specifications 21 | of the orginal _setup.rb_, still other common project needs remain chaotic and 22 | confusing. The storage of the a project's current version number is a 23 | painfully obvious example. The data is required in a project's .gemspec, and 24 | common practice dictates that is should also be in our library code as a 25 | constant; if we use Rakegem[http://rakegem.github.org] that constanct should be 26 | in lib/foo.rb, but if we are using Bundler[http://bundler.org] it is expected 27 | in lib/foo/version.rb. If we use Bones[http://twp.github.com/bones] to help us 28 | manage our project, the verison number is stored in a VERSION file as a string, 29 | but if we use Jeweler[http://] the same file may be a YAML-formated hash. All 30 | of this just for one piece of metadata and a small smatter of available tools! 31 | 32 | 33 | ## SYNOPSIS 34 | 35 | Primarily GemDo defines a standard set of project layout and design patterns. 36 | Most of these derive for setup.rb, the original Ruby project installer, 37 | as well as common patterns widely used among the Ruby community. 38 | 39 | The specification designates particular files and their uses, most fo which 40 | are obvious, such as a `README` or a `MANIFEST`. Others are new introduced 41 | by GemDo such as the `.ruby` and `Profile` files. 42 | 43 | This entire specification is implemeted in Ruby code, known as the "GemDo" 44 | (Project Object Model). Primary usage of this model relies on the 45 | Project class, instantiated by passing the constructor the root directory 46 | of the project. 47 | 48 | project = GemDo::Project.new(root_directory) 49 | 50 | The project object then ties into all the available metadata, e.g. 51 | 52 | project.name 53 | project.version 54 | project.homepage 55 | 56 | etc... 57 | 58 | GemDo also provides a command line tool with some useful project utilities. 59 | For example, it can be used to easily bump a project's version number. 60 | 61 | $ gemdo bump --patch 62 | 63 | See the Wiki[http://wiki.github.com/rubyworks/gemdo] and 64 | API[http://rubyworks.github.com/gemdo/rdoc] documentation for further 65 | details. 66 | 67 | 68 | ## DEVELOPMENT 69 | 70 | ### Source Code Management 71 | 72 | GemDo uses git and hosts it's project repositories with GitHub at 73 | http://github.com/rubyworks/gemdo. 74 | 75 | 76 | ### Mailing List 77 | 78 | You can subscribe to the RubyWorks mailing list by sending a message to 79 | this mailing address[mailto:rubyworks-mailinglist+subscribe@googlegroups.com], 80 | or visit http://groups.google.com/group/rubyworks-mailinglist. 81 | 82 | 83 | ## HOW TO INSTALL 84 | 85 | To install with RubyGems simply open a console and type: 86 | 87 | $ sudo gem install gemdo 88 | 89 | Site installation requires Setup.rb (gem install setup), 90 | then download the tarball package and type: 91 | 92 | $ tar -xvzf gemdo-1.0.0.tar.gz 93 | $ cd gemdo-1.0.0.tar.gz 94 | $ sudo setup.rb all 95 | 96 | Windows users use 'ruby setup.rb all'. 97 | 98 | 99 | ## COPYRIGHT & LICENSE 100 | 101 | GemDo Copyright (c) 2009 Thomas Sawyer 102 | 103 | Made available according to the terms of the BSD 2-Clause License. 104 | 105 | See COPYING.rdoc file for details. 106 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/history.rb: -------------------------------------------------------------------------------- 1 | require 'pom/core_ext' 2 | require 'pom/version' 3 | 4 | module POM 5 | 6 | # = History File 7 | # 8 | # The History class parses a HISTORY file into individual 9 | # release sections. The file is expected to be in RDoc or simple 10 | # Mardkdown format with each section beginning with a secondary 11 | # header (== or ##) giving *version* and *date* of release, 12 | # then a *note* followed by a point by point outline of *changes*. 13 | # 14 | # For example: 15 | # 16 | # == 1.0.0 / 2009-10-07 17 | # 18 | # Say something about this version. 19 | # 20 | # Changes: 21 | # 22 | # * outline oimportant changelog items 23 | # 24 | # +Changes:+ is used as a parsing marker. While optional, it 25 | # helps the parser find the list of changes, rather than looking 26 | # for an asterisk or digit, so that ordered and unordered lists 27 | # can be used in the note section too. 28 | # 29 | # TODO: Deal with ChangeLog like formats? Perhaps just make extendable 30 | # to handle custom formats. 31 | # 32 | class History 33 | 34 | # File glob for finding the HISTORY file. 35 | DEFAULT_FILE = '{History}{,.*}' 36 | 37 | # 38 | def self.find(root) 39 | root = Pathname.new(root) 40 | root.glob(DEFAULT_FILE, :casefold).first 41 | end 42 | 43 | # HISTORY file's pathname. 44 | attr :file 45 | 46 | # List of release entries. 47 | attr :releases 48 | 49 | # New History. 50 | def initialize(root, opts={}) 51 | @root = Pathname.new(root) 52 | @file = opts[:file] || self.class.find(root) 53 | read 54 | end 55 | 56 | # Match against version number string. 57 | HEADER_RE = /^[=#]+\s*\d+\.\S+/ 58 | 59 | # Read and parse the Histoy file. 60 | def read 61 | @releases = [] 62 | entry = nil 63 | if file 64 | file.readlines.each do |line| 65 | if HEADER_RE =~ line 66 | @releases << Release.new(entry) if entry 67 | entry = line 68 | else 69 | next unless entry 70 | entry << line 71 | end 72 | end 73 | @releases << Release.new(entry) 74 | end 75 | end 76 | 77 | # Returns first entry in releases list. 78 | def release 79 | releases.first 80 | end 81 | 82 | # History release entry. 83 | class Release 84 | 85 | include VersionHelper 86 | 87 | # The full text of the release note. 88 | attr :text 89 | 90 | # The header. 91 | attr :header 92 | 93 | # The description. 94 | attr :notes 95 | 96 | # The list of changes. 97 | attr :changes 98 | 99 | # Version number (as a string). 100 | attr :version 101 | 102 | # Release date. 103 | attr :date 104 | 105 | # Nick name of the release, if any. 106 | attr :nickname 107 | 108 | # 109 | def initialize(text) 110 | @text = text.strip 111 | parse 112 | end 113 | 114 | # Returns the complete text. 115 | def to_s 116 | text 117 | end 118 | 119 | ;; private 120 | 121 | # Parse the release text into +header+, +notes+ 122 | # and +changes+ components. 123 | def parse 124 | lines = text.lines.to_a 125 | 126 | @header = lines.shift.strip 127 | 128 | parse_release_stamp(@header) 129 | 130 | # remove blank lines from top 131 | lines.shift until lines.first !~ /^\s+$/ 132 | 133 | # find line that looks like the startt of a list of c hanges. 134 | idx = nil 135 | idx ||= lines.index{ |line| /^changes\:\s*$/i =~ line } 136 | idx ||= lines.index{ |line| /^1.\ / =~ line } 137 | idx ||= lines.index{ |line| /^\*\ / =~ line } 138 | 139 | if idx.nil? 140 | @notes = lines.join 141 | @changes = '' 142 | elsif idx > 0 143 | @notes = lines[0...idx].join 144 | @changes = lines[idx..-1].join 145 | else 146 | gap = lines.index{ |line| /^\s*$/ =~ line } 147 | @changes = lines[0...gap].join 148 | @notes = lines[gap..-1].join 149 | end 150 | end 151 | 152 | # Parse out the different components of the header, such 153 | # as `version`, release `date` and release `nick name`. 154 | def parse_release_stamp(text) 155 | # version 156 | if md = /\b(\d+\.\d.*?)(\s|$)/.match(text) 157 | @version = md[1] 158 | end 159 | # date 160 | if md = /\b(\d+\-\d+\-.*?\d)(\s|\W|$)/.match(text) 161 | @date = md[1] 162 | end 163 | # nickname 164 | if md = /\"(.*?)\"/.match(text) 165 | @nickname = md[1] 166 | end 167 | end 168 | end 169 | 170 | end #class History 171 | 172 | end #module POM 173 | 174 | -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Rock 4 | 5 | 53 | 54 | 55 | 56 |
57 |
58 | 61 |
62 |

Rock

63 |

Object Model
for Ruby Projects

64 |
65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
 73 |   $ rock about
 74 | 
 75 |   Rock v1.3 (2009-07-22)
 76 | 
 77 |   Project metadata system for Ruby projects.
 78 | 
 79 |   contact    : http://googlegroups/group/protuils
 80 |   homepage   : http://proutils.rubyforge.org/rock
 81 |   repository : git://github.com/proutils/rock.git
 82 |   authors    : Thomas Sawyer <transfire@gmail.com>
 83 |   package    : rock-1.3
 84 | 
 85 |   Copyright (c) 2009 Thomas Sawyer
 86 |   
87 | 88 | 107 | 108 | 113 | 114 | 119 |
120 | 121 | 126 | 127 |
128 | API Documentation · 129 | Demonstrandum 130 |
131 | 132 |
133 |
134 | 135 |
136 | 137 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | = RELEASE HISTORY 2 | 3 | == 2.1.2 / 2010-01-20 4 | 5 | Add new 'pom news' command to show current release 6 | notes --useful for tagging! (NOTE: I thought this was 7 | in the last release but it never made it into the MANIFEST.) 8 | 9 | Changes: 10 | 11 | * Add pom news command to show current release notes. 12 | 13 | 14 | == 2.1.1 / 2010-10-19 15 | 16 | Bump command can now take state or slot as an argument. 17 | In other words, 'pom bump major' will work exactly like 18 | using 'pom bump --major'. Als added new 'pom news' command 19 | to show current release notes (usefule for tagging!). And 20 | last but not least History and News class are now much 21 | more robust. 22 | 23 | Changes: 24 | 25 | * More robust History and News classes. 26 | * Bump command is more versitile. 27 | 28 | 29 | == 2.1.0 / 2010-10-18 30 | 31 | The significant change with v2.1 is the use of a PACKAGE 32 | file to provide essential information needed by package and 33 | library managers and other tools, including name, version 34 | and loadpath. The file can still be called VERSION if prefered. 35 | PACKAGE was chosen as a default to prevent potential clashes 36 | with other tools use of VERSION (and becuase it is a fitting 37 | description). Again review the POM wiki to learn more. 38 | 39 | Changes: 40 | 41 | * PACKAGE file is the default name of what was VERSION file. 42 | * Multi-format support for PACKAGE/VERSION file. 43 | * Renamed 'ver' subcommand to 'bump'. 44 | 45 | 46 | == 2.0.0 / 2010-06-06 47 | 48 | Version 2.0 marks a major turning point for POM --it is offically 49 | ready for mass consumption! What separates this version from 50 | previous versions is the adoption of YAML-based metadata files, 51 | over the previous meta directory-based config system. Be sure 52 | to checkout the Wiki to learn more. 53 | 54 | Changes: 55 | 56 | * Swtich to YAML-based metadata files. 57 | * Announcement generator is much imropved. 58 | * Removed Build class (it was a dumb idea). 59 | 60 | 61 | == 1.8.0 / 2010-04-30 62 | 63 | This release refines the API a bit and adds a few additional niceities. 64 | 65 | Changes: 66 | 67 | * Add Project#name and #version as shortcuts to metadata. 68 | * README parser is much improved (could still use more though). 69 | * Filestore can be only be one directory (meta/ or .meta/, not both) 70 | * Add Build class for development metadata stored in .build/. 71 | * Simplifed root lookup code. 72 | 73 | 74 | == 1.7.0 / 2010-02-06 75 | 76 | This release introduced Metadata extensions, which can be defined 77 | to augment the primary metadata via a meta/ subdirectory. Currently 78 | only a RubyForge extension is built-in. Also, the +Metabuild+ class 79 | introduced in the last version has been deprecated --it may be 80 | replaced by a Metadata extension in the furture, but for now it has 81 | been determined that it is not a vital need. In addition a new 82 | +maintianer+ field has been added to complement the +contact+ 83 | field. Finally this release fixes a major bug that prevented certain 84 | metadata fields from being accessed properly. 85 | 86 | Changes: 87 | 88 | * New Metadata extension system. 89 | * Deprecated Metabuild class. 90 | * Add maintainer field to Metadata. 91 | * Fix special reader bug. 92 | 93 | 94 | == 1.6.0 / 2009-12-07 95 | 96 | This release introduce the Metabuild class, which is like the 97 | Metadata class but supports general build configuration 98 | information. 99 | 100 | Changes: 101 | 102 | * Added Metabuild class. 103 | 104 | 105 | == 1.5.0 / 2009-11-14 106 | 107 | Between version 1.1 and the current 1.5, a great deal of polish 108 | has been applied to the project. There are no longer any metadata 109 | aliases, save one (collection/suite). A new Release class provides 110 | encapsualted access to the current release notes, either from 111 | a standard file or from the top entry of the HISTORY file. And a 112 | +pom+ command-line utility now provides a variety of POM releated 113 | functions from printing out a project summary to generating meta 114 | entries from a +README+ and/or +.gemspec+ file. 115 | 116 | Changes: 117 | 118 | * Added +pom+ command-line tool. 119 | * Added new Release class to handle current release notes. 120 | * Removed all metadata aliases (save one). 121 | * Metadata is no longer lazy-loaded. 122 | * Can generate meta entries from +README+ and/or +.gemspec+ file. 123 | * Polished code, removing vestages of old code. 124 | * Separated cli commands into separate classes. 125 | 126 | 127 | == 1.1.0 / 2009-08-14 128 | 129 | This release brings some nice improements to POM, in particular 130 | the addition of the Project class and it's supporting classes 131 | History and Manifest, which were taken and deprecated from Syckle. 132 | 133 | Changes: 134 | 135 | * Metadata is loaded passively as needed. 136 | * Metadata fallback is parsed README. 137 | * Project class has added, taken from Reap. 138 | 139 | 140 | == 1.0.0 / 2009-07-22 141 | 142 | This is the initial release of POM. POM is a "Project Object Model" 143 | designed for Ruby projects. 144 | 145 | Changes: 146 | 147 | * Happy Birthday! 148 | 149 | -------------------------------------------------------------------------------- /work/trash/blog/2010-07-21-finding-root.md: -------------------------------------------------------------------------------- 1 | # 2010-07-21 | Find Root 2 | 3 | How can we reliably identify the root directory of a Ruby project? 4 | This is an important need that as of yet has gone unatteneded. 5 | 6 | In general practice the issue has been largely circumvented by the use of Rake, 7 | since a Rakefile resides in the project's root and it is build tasks that 8 | primarily need to operate out of a project's root. However there are tools that 9 | lie outside of use via Rake, and attempting ot make the Rakefile a more general 10 | requirement also leaves users of alternate build tools in the cold. 11 | 12 | It has been suggested that searching for a `lib/` directory is the 13 | best choice. And it is a good idea in that project maintainers would not 14 | have to do anything to support the specification since the `lib/` directory 15 | is already a standard, having been derived from setup.rb. However, as 16 | remarkable as it may seem there are a few Ruby projects in the wild that do not 17 | use a `lib/` directory. Since the loadpath can be adjusted in a gemspec, it is 18 | certainly not necessary. While the Ruby POM specification could require it to 19 | compensate, allowing a modifiable loadpath and requiring a `lib/` directory 20 | are pretty antithetical. It is also not guarenteed that a developer 21 | will not want to create a lib/ directory somewhere below root. 22 | 23 | A more obvious, and rather generic choice, would be the use of special SCM 24 | directories. Directory like `.git`, `.hg`, `_darcs`, and so on, are dead-giveaways 25 | as to the location of the project's root fodler. But here again we run into 26 | off cases. Some persons may use an uncommon SCM or none at all. Worse still, 27 | Subverison can't be include here becuase it puts `.svn/` in _every_ project 28 | directory. 29 | 30 | Another option is the `.gemspec` file. This is a farily good option in that 31 | it clearly marks the project as a Ruby project, but many tools generate a 32 | Gem::Specification on demand and thus have no need of a perminantly present 33 | .gempec file (despite what some have urged). Moreover, it is not a DRY 34 | solution since the purpose of POM metadata is a to provide a more complete 35 | and resuable design for storing much of the same information. 36 | 37 | Similarly some may suggest the `Gemfile` as a marker, being rapidly 38 | popularized by Bundler. And this I think would be a better notion, however 39 | I have some issues with the Gemfile. Primarily I do not think it is wise 40 | to make a configuration file an executable Ruby script. By doing so, it is no 41 | long declartive in nature. If-conditions are especially unacceptable. 42 | Indeed this is the very thing that led to the creation of POM's alternativee, 43 | `requires`. 44 | 45 | If none of these pre-existing options are fully satisfactory, where does this 46 | leave us? We could define a specific marker just for the purpose. However if 47 | we are going to define a file or a directory to act as marker, clearly the 48 | file of directory should also be of some use beyond being a mere marker. 49 | Having an empty file or directory for the purpose seems rather silly. 50 | 51 | One possibility is the `PACKAGE` file. This is the one essential file the 52 | specification designates. Using this file as the marker will almost assuredly 53 | work in every case. However it has a fatal flaw --using would also essentially 54 | entail that one were using the POM. Now clearly I want people to use POM, 55 | but I also don't necessarily want to discourage others from using a viable 56 | standard for reliably identifying the root directory of a Ruby project even if 57 | they do not wish to do so. In addition the name lacks any sort of "this-is-ruby" 58 | quality, which would also be nice to have (IMO). 59 | 60 | The final alterantive is to use something more akin to the original POM `.meta/` 61 | directory. This was a good indicator of root in itself. Though again it lacks 62 | the "Ruby" quality and assumes the use of POM. Perhaps it can be called `.ruby/` 63 | instead. I very much like this in that is has an appreciable quality, in much 64 | the same way "Gemfile" does. On the other hand, the more generic approach would 65 | be to use somthing like `.meta/` or `.project/` but have a `ruby` file within 66 | it, e.g. `.project/ruby`. Thus achivieving a generic design but also clearly 67 | indicating a Ruby project at the same time. The trouble with the directory 68 | is what else do we put in the directory? Do we move `PACKAGE`, `PROFILE` and 69 | other into it? Do we really want to hide these files away under a hidden 70 | directory when they offer so much useful general purpose information about 71 | a project? The placing of these files into the directory could be optional but 72 | remember that git will not track an empty directory, so something has to go in 73 | it. On the plus side, other tools could use the location as well. Currently 74 | Setup.rb uses a `.setup/` directory to house install hooks and other optional 75 | files. Potentially it could use the `.ruby/` directory instead. In other words 76 | the directory has more room for growth, where as using a file as a marker is 77 | more limited. 78 | 79 | I haven't come to a clear solution yet, but at least the options are narrowing. 80 | 81 | -------------------------------------------------------------------------------- /work/deprecated/rubyfile.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo/spec' 2 | 3 | module Gemdo 4 | 5 | # A Rubyfile is a script that is used to buld a Rubyspec. 6 | class Rubyfile 7 | 8 | # 9 | def self.attr_setter(name) 10 | define_method(name) do |value| 11 | @data[name.to_s] = value 12 | end 13 | end 14 | 15 | # 16 | def self.find(root) 17 | root = Pathname.new(root) 18 | pattern = '{' + filename.join(',') + '}{,.rb}' 19 | root.glob(pattern, :casefold).first 20 | end 21 | 22 | # The default file name to use for saving a new Rubyfile. 23 | def self.default_filename 24 | 'Rubyfile' 25 | end 26 | 27 | # Standard file names for a Rubyfile. Notice that the file 28 | # can be visible or hidden. 29 | def self.filename 30 | ['Rubyfile', '.rubyfile', 'Gemfile'] 31 | end 32 | 33 | # 34 | def initialize(root, data={}) 35 | @root = Pathname.new(root) 36 | 37 | @file = data.delete(:file) || 38 | self.class.find(root) || 39 | root + self.class.default_filename 40 | 41 | @data = data 42 | 43 | # for compatibility with Bundler 44 | @_source = nil 45 | @_group = [] 46 | @_platform = [] 47 | 48 | load! 49 | end 50 | 51 | # 52 | def load! 53 | if files.each do |file| 54 | if file.exist? 55 | script = File.read(file) 56 | if /^A---/ =~ script 57 | import YAML.load(script) 58 | else 59 | instance_eval(script, file) 60 | end 61 | end 62 | end 63 | end 64 | 65 | # 66 | def import(file) 67 | case File.extname(file) 68 | when '.yaml', '.yml' 69 | @data.merge!(YAML.load(File.new(file))) 70 | else 71 | text = File.read(file).strip 72 | if /\A---/ =~ text 73 | @data.merge!(YAML.load(text)) 74 | else 75 | 76 | end 77 | end 78 | end 79 | 80 | # 81 | attr :file 82 | 83 | # 84 | attr_setter :name 85 | 86 | # 87 | attr_setter :version 88 | 89 | # 90 | attr_setter :date 91 | 92 | # 93 | def author(person) 94 | @data['authors'] ||= [] 95 | @data['authors'] << person 96 | end 97 | 98 | # 99 | def manifest(file_or_array) 100 | case file_or_array 101 | when Array 102 | @data['manifest'] = file_or_array 103 | else 104 | list = File.readlines(file_or_array).to_a 105 | list = list.map{ |f| f.strip } 106 | list = list.reject{ |f| /^\#/ =~ f } 107 | list = list.reject{ |f| /^\s*$/ =~ f } 108 | @data['manifest'] = list 109 | end 110 | end 111 | 112 | # 113 | def requires(list) 114 | list.each{ |entry| gem entry } 115 | end 116 | alias_method :dependencies, :requires 117 | 118 | # Designate a requirement. 119 | def gem(name, *args) 120 | opts = Hash == args.last ? args.pop : {} 121 | 122 | name, *cons = name.split(/\s+/) 123 | 124 | if md = /\((.*?)\)/.match(cons.last) 125 | cons.pop 126 | group = md[1].split(/\s+/) 127 | opts['group'] = group 128 | end 129 | 130 | opts['name'] = name 131 | opts['version'] = cons.join(' ') unless cons.empty? 132 | 133 | opts['source'] ||= @_source if @_source 134 | 135 | unless @_group.empty? 136 | opts['group'] ||= [] 137 | opts['group'] += @_group 138 | end 139 | 140 | unless @_platform.empty? 141 | opts['platform'] ||= [] 142 | opts['plarform'] += @_platform 143 | end 144 | 145 | @data['requires'] ||= [] 146 | @data['requires'] << opts 147 | end 148 | 149 | # --- Bundler Compatibility ??? --- 150 | 151 | def source(source) #:yield: 152 | @_source = source 153 | yield 154 | ensure 155 | @_source = nil 156 | end 157 | 158 | # For use with defining dependencies with the +gem+ method. 159 | # This allows for compatibility with Bundler Gemfile. 160 | def group(*names) #:yield: 161 | @_group.concat names 162 | yield 163 | ensure 164 | names.each{@_group.pop} 165 | end 166 | 167 | # This allows for compatibility with Bundler Gemfile. 168 | def platform(*names) #:yield: 169 | @_platform.concat names 170 | yield 171 | ensure 172 | names.each{@_platform.pop} 173 | end 174 | alias_method :platforms, :platform 175 | 176 | # ??? 177 | def path(path, options={}, source_options={}, &blk) 178 | end 179 | 180 | # This one sucks. Talk about favoring one SCM over another! 181 | # Handle submodules yourself like a real developer! 182 | def git(*) 183 | msg = "The `git` method is incompatible with Gemdo.\n" / 184 | "Consider using submodules or an alternate tool\n" / 185 | "to manager vendored sources, and use the `path`\n" \ 186 | "option instead." 187 | raise msg 188 | end 189 | 190 | # This one can blow! 191 | def gemspec(*) 192 | msg = "The `gemspec` method is incompatible with Gemdo/.\n" / 193 | "Gemdo will generate a gemspec from the Gemfile." 194 | raise msg 195 | end 196 | 197 | # --- End Bundler Compatibility --- 198 | 199 | # 200 | def resource(label, url) 201 | @data['resources'] ||= {} 202 | @data['resources'][label.to_s] = url 203 | end 204 | 205 | # 206 | def method_missing(name, *args, &block) 207 | super(name, *args, &block) if block 208 | if args.size > 1 209 | @data[name.to_s] = args 210 | else 211 | @data[name.to_s] = args.first 212 | end 213 | end 214 | 215 | # 216 | def to_rubyspec 217 | Rubyspec.new(@root, @data) 218 | end 219 | 220 | end 221 | 222 | end 223 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/resolver/rubygems.rb: -------------------------------------------------------------------------------- 1 | # TODO: rename to Gempath. 2 | 3 | module POM 4 | 5 | class Resolver 6 | 7 | require 'pom/resolver/source' 8 | 9 | # Local Rubygems Source. 10 | class Rubygems < Source 11 | 12 | # 13 | attr :project_gem 14 | 15 | # 16 | attr :missing 17 | 18 | # 19 | attr :cache 20 | 21 | # 22 | def initialize(project_gem) 23 | unless defined?(::Gem) 24 | raise "No library manager loaded." 25 | end 26 | 27 | @project_gem = project_gem 28 | 29 | @missing = [] 30 | @cache = {} 31 | end 32 | 33 | # Access to +@cache+ plus current project. 34 | def gems 35 | @gems ||= ( 36 | @cache.merge(project_gem.name=>[project_gem]) 37 | ) 38 | end 39 | 40 | # Featch all potetnial gems for the +project_gem+. 41 | def fetch 42 | fetch_requirements(project_gem.dependencies) 43 | end 44 | 45 | # Fetch all versions of all gems in given requirments and their 46 | # sub-requirements. This builds up the gem +@cache+ with Gem instances. 47 | def fetch_requirements(requirements) 48 | return if requirements.empty? 49 | 50 | names = requirements.map{ |name, constraint| name } 51 | names = names.reject{ |name| already_fetched?(name) } 52 | names = names.uniq 53 | 54 | gems = get(*names) 55 | 56 | requirements.each do |name, constraint| 57 | next if already_fetched?(name) 58 | unless gems.find{ |gem| gem.name == name } 59 | self.missing << [name, constraint] 60 | self.missing.uniq! 61 | end 62 | end 63 | 64 | fetch_requirements(unfetched_requirements(gems)) 65 | end 66 | 67 | # 68 | def get(*names) 69 | names = names - @cache.keys # remove names already fetched 70 | return [] if names.empty? 71 | 72 | gems = names.map{ |name| 73 | ::Gem.source_index.find_name(name) 74 | }.flatten 75 | 76 | gems = gems.map do |gem| 77 | Gem.new( 78 | :name=>gem.name, 79 | :number=>gem.version.to_s, 80 | :dependencies=>gem.dependencies.map{ |d| [d.name, d.requirement.to_s] } 81 | ) 82 | end 83 | 84 | gems.each do |gem| 85 | @cache[gem.name] ||= [] 86 | @cache[gem.name] << gem 87 | end 88 | 89 | return gems 90 | end 91 | 92 | # 93 | def unfetched_requirements(gems) 94 | reqs = [] 95 | gems.each do |gem| 96 | gem.dependencies.each do |name, constraint| 97 | reqs << [name, constraint] unless already_fetched?(name) 98 | end 99 | end 100 | reqs.uniq 101 | end 102 | 103 | # Has a gem already been fetched? 104 | def already_fetched?(name) 105 | @cache.key?(name) 106 | end 107 | 108 | end 109 | 110 | end 111 | 112 | end 113 | 114 | 115 | 116 | 117 | =begin 118 | 119 | # 120 | def resolve_dependencies(requirements) 121 | requests = [] 122 | matches = [] 123 | missing = [] 124 | 125 | requirements.each do |name, constraint| 126 | gem_dep = Gem::Dependency.new(name, [constraint]) 127 | rubygems_resolve_gem(gem_dep, requests, matches, missing) 128 | end 129 | 130 | libs = matches.group_by{ |spec| spec.name } 131 | 132 | requests.each do |name, constraint| 133 | next unless libs[name] 134 | libs[name].reject! do |spec| 135 | !POM::VersionNumber.new(spec.version.to_s).match?(constraint.to_s) 136 | end 137 | end 138 | 139 | return libs, missing 140 | end 141 | 142 | # 143 | def rubygems_resolve_gem(gem_dep, requests=[], matches=[], missing=[]) 144 | requests << [gem_dep.name, gem_dep.requirement] 145 | list = Gem.source_index.find_name(gem_dep.name, gem_dep.requirement) 146 | missing << gem_dep if list.empty? 147 | matches.concat(list) 148 | list.each do |spec| 149 | next if matches.include?(spec) 150 | spec.runtime_dependencies.each do |dep_gem| 151 | next if missing.include?(gem_dep) 152 | rubygems_resolve_gem(dep_gem, matches, missing) 153 | end 154 | spec.development_dependencies.each do |dep_gem| 155 | next if missing.include?(gem_dep) 156 | rubygems_resolve_gem(dep_gem, matches, missing) 157 | end unless runtime 158 | end 159 | end 160 | =end 161 | 162 | 163 | =begin 164 | # 165 | def rubygems_resolve(requirements) 166 | requests = [] 167 | matches = [] 168 | missing = [] 169 | requirements.each do |name, constraint| 170 | gem_dep = Gem::Dependency.new(name, [constraint]) 171 | rubygems_resolve_gem(gem_dep, requests, matches, missing) 172 | end 173 | libs = matches.group_by{ |spec| spec.name } 174 | requests.each do |name, constraint| 175 | next unless libs[name] 176 | libs[name].reject! do |spec| 177 | !POM::VersionNumber.new(spec.version.to_s).match?(constraint.to_s) 178 | end 179 | end 180 | return libs, missing 181 | end 182 | 183 | # 184 | def rubygems_resolve_gem(gem_dep, requests=[], matches=[], missing=[]) 185 | requests << [gem_dep.name, gem_dep.requirement] 186 | list = Gem.source_index.find_name(gem_dep.name, gem_dep.requirement) 187 | missing << gem_dep if list.empty? 188 | matches.concat(list) 189 | list.each do |spec| 190 | next if matches.include?(spec) 191 | spec.runtime_dependencies.each do |dep_gem| 192 | next if missing.include?(gem_dep) 193 | rubygems_resolve_gem(dep_gem, matches, missing) 194 | end 195 | spec.development_dependencies.each do |dep_gem| 196 | next if missing.include?(gem_dep) 197 | rubygems_resolve_gem(dep_gem, matches, missing) 198 | end unless runtime 199 | end 200 | end 201 | =end 202 | 203 | -------------------------------------------------------------------------------- /work/deprecated/gemspec.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | class Project 3 | ## 4 | # Mixin adds methods for accessing a project's gemspec and 5 | # other gem releated features. 6 | # 7 | module GemUtils 8 | 9 | 10 | public 11 | 12 | # TODO: Replace everything below here with Indexer code and tie into above. 13 | 14 | # Create a Gem::Specification from a POM::Project. Because POM metadata 15 | # is extensive a fairly complete a Gem::Specification can be created from 16 | # it which is sufficient for almost all needs. 17 | # 18 | # TODO: However there are still a few features that need address, such 19 | # as signatures. 20 | def to_gemspec(options={}) 21 | require_rubygems 22 | 23 | if metadata.resources 24 | homepage = metadata.resources.homepage 25 | else 26 | homepage = nil 27 | end 28 | 29 | if homepage && md = /(\w+).rubyforge.org/.match(homepage) 30 | rubyforge_project = md[1] 31 | else 32 | # b/c it has to be something according to Eric Hodel. 33 | rubyforge_project = metadata.name.to_s 34 | end 35 | 36 | #TODO: may be able to get this from project method 37 | if news = Dir[root + 'NEWS{,.txt}'].first 38 | install_message = File.read(news) 39 | end 40 | 41 | ::Gem::Specification.new do |spec| 42 | spec.name = self.name.to_s 43 | spec.version = self.version.to_s 44 | spec.require_paths = self.loadpath.to_a 45 | 46 | spec.summary = metadata.summary.to_s 47 | spec.description = metadata.description.to_s 48 | spec.authors = metadata.authors.to_a 49 | spec.email = metadata.email.to_s 50 | spec.licenses = metadata.licenses.to_a 51 | 52 | spec.homepage = metadata.homepage.to_s 53 | 54 | # -- platform -- 55 | 56 | # TODO: how to handle multiple platforms? 57 | spec.platform = options[:platform] #|| verfile.platform #'ruby' ??? 58 | #if metadata.platform != 'ruby' 59 | # spec.require_paths.concat(spec.require_paths.collect{ |d| File.join(d, platform) }) 60 | #end 61 | 62 | # -- rubyforge project -- 63 | 64 | spec.rubyforge_project = rubyforge_project 65 | 66 | # -- compiled extensions -- 67 | 68 | spec.extensions = options[:extensions] || self.extensions 69 | 70 | # -- dependencies -- 71 | 72 | case options[:gemfile] 73 | #when String 74 | # gemfile = root.glob(options[:gemfile]).first # TODO: Alternate gemfile 75 | when nil, true 76 | gemfile = root.glob('Gemfile').first 77 | else 78 | gemfile = nil 79 | end 80 | 81 | if gemfile 82 | require 'bundler' 83 | spec.add_bundler_dependencies 84 | else 85 | metadata.requirements.each do |dep| 86 | if dep.development? 87 | spec.add_development_dependency( *[dep.name, dep.constraint].compact ) 88 | else 89 | next if dep.optional? 90 | spec.add_runtime_dependency( *[dep.name, dep.constraint].compact ) 91 | end 92 | end 93 | end 94 | 95 | # TODO: considerations? 96 | #spec.requirements = options[:requirements] || package.consider 97 | 98 | # -- executables -- 99 | 100 | # TODO: bin/ is a POM convention, is there are reason to do otherwise? 101 | spec.bindir = options[:bindir] || "bin" 102 | spec.executables = options[:executables] || self.executables 103 | 104 | # -- rdocs (argh!) -- 105 | 106 | readme = root.glob_relative('README{,.*}', File::FNM_CASEFOLD).first 107 | extra = options[:extra_rdoc_files] || [] 108 | 109 | rdocfiles = [] 110 | rdocfiles << readme.to_s if readme 111 | rdocfiles.concat(extra) 112 | rdocfiles.uniq! 113 | 114 | rdoc_options = [] #['--inline-source'] 115 | rdoc_options.concat ["--title", "#{metadata.title} API"] #if metadata.title 116 | rdoc_options.concat ["--main", readme.to_s] if readme 117 | 118 | spec.extra_rdoc_files = rdocfiles 119 | spec.rdoc_options = rdoc_options 120 | 121 | # -- distributed files -- 122 | 123 | if manifest.exist? 124 | filelist = manifest.select{ |f| File.file?(f) } 125 | spec.files = filelist 126 | else 127 | spec.files = root.glob_relative("**/*").map{ |f| f.to_s } # metadata.distribute ? 128 | end 129 | 130 | # DEPRECATED: -- test files -- 131 | #spec.test_files = manifest.select do |f| 132 | # File.basename(f) =~ /test\// && File.extname(f) == '.rb' 133 | #end 134 | 135 | if install_message 136 | spec.post_install_message = install_message 137 | end 138 | end 139 | 140 | end 141 | 142 | # Import a Gem::Specification into a POM::Project. This is intended to make it 143 | # farily easy to build a set of POM metadata files from pre-existing gemspec. 144 | def import_gemspec(gemspec=nil) 145 | gemspec = gemspec || self.gemspec 146 | 147 | profile.name = gemspec.name 148 | profile.version = gemspec.version.to_s 149 | profile.path = gemspec.require_paths 150 | #metadata.engines = gemspec.platform 151 | 152 | profile.title = gemspec.name.capitalize 153 | profile.summary = gemspec.summary 154 | profile.description = gemspec.description 155 | profile.authors = gemspec.authors 156 | profile.contact = gemspec.email 157 | 158 | profile.resources.homepage = gemspec.homepage 159 | 160 | #metadata.extensions = gemspec.extensions 161 | 162 | gemspec.dependencies.each do |d| 163 | next unless d.type == :runtime 164 | requires << "#{d.name} #{d.version_requirements}" 165 | end 166 | end 167 | 168 | end #module RubyGems 169 | end #class Project 170 | end #module POM 171 | -------------------------------------------------------------------------------- /lib/gemdo/cli/init.rb: -------------------------------------------------------------------------------- 1 | require 'gemdo' 2 | 3 | module GemDo 4 | module CLI 5 | ## 6 | # Create a basic Ruby project layout. 7 | # 8 | class Init < Base 9 | 10 | # 11 | attr :resources 12 | 13 | # 14 | def parse 15 | parser = OptionParser.new do |opt| 16 | opt.banner = "pom init [ ...]" 17 | 18 | opt.on("--replace", "-r", "replace any pre-existing entries") do 19 | options[:replace] = true 20 | end 21 | 22 | opt.on("--force", "-f", "override safe-guarded operations") do 23 | $FORCE = true 24 | end 25 | 26 | opt.on("--trial", "-n", "run in trial mode, skips disk writes") do 27 | $TRIAL = true 28 | end 29 | 30 | opt.on("--debug", "run in debug mode, raises exceptions") do 31 | $DEBUG = true 32 | $VERBOSE = true 33 | end 34 | 35 | opt.on_tail("--help", "-h", "display this help message") do 36 | puts opt 37 | exit 38 | end 39 | end 40 | 41 | parser.parse! 42 | 43 | @resources = ARGV 44 | end 45 | 46 | #-- 47 | # TODO: How to handle options[:replace] ? 48 | #++ 49 | def execute 50 | #require 'erb' 51 | 52 | root = Dir.pwd 53 | 54 | #has_profile = POM::Profile.find(root) 55 | has_profile = Dir.glob('profile{,.yml,.yaml,.rb}', File::FNM_CASEFOLD).first 56 | 57 | if has_profile and not $FORCE #if POM::Profile.find(root) and not $FORCE 58 | $stderr << "PROFILE already exists. Use --force option to allow overwrite.\n" 59 | exit -1 60 | end 61 | 62 | require_rubygems 63 | 64 | require 'readme' 65 | require 'gemdo/gemspec' 66 | 67 | #prime = { 68 | # 'name' => File.basename(root), 69 | # 'version' => '0.0.0', 70 | # 'requires' => [], 71 | # 'summary' => "FIX brief one line description here", 72 | # 'contact' => "FIX name or uri", 73 | # 'authors' => "FIX names of authors here", 74 | # 'repository' => "FIX master public repo uri" 75 | #} 76 | 77 | #current = Dir.glob('**/*', File::FNM_DOTMATCH) 78 | 79 | #if !File.exist?('.ruby') 80 | # File.open('.ruby', 'w'){|f| f << `ruby -v`} 81 | #end 82 | 83 | #has_package = Gemdo::Rubyspec.find(root) 84 | 85 | #if has_profile && !$FORCE 86 | # $stderr.puts "Looks like your project is already built on a gemdo." 87 | # $stderr.puts "To re-create the metadata files use the --force option." 88 | # return 89 | #end 90 | 91 | #name = File.basename(root) 92 | 93 | project = POM::Project.new(root) 94 | 95 | #name = project.name || File.basename(root) 96 | # profile = project.profile 97 | 98 | #if !has_package 99 | # #metadata.new_project 100 | # metadata.name = File.basename(root) 101 | # metadata.version = '0.0.0' 102 | # metadata.codename = 'FIXME A version codename is optional' 103 | #end 104 | 105 | # profile.summary = "FIXME brief one line description here" 106 | # profile.contact = "FIXME name <#{ENV['EMAIL']}>" 107 | # profile.authors << "FIXME list of author's names here" 108 | # profile.homepage = "FIXME: http://your_website.org" 109 | #profile.resources.repository = "FIXME: master public repo uri" 110 | 111 | if resources.empty? 112 | resources << Dir.glob('*.gemspec').first 113 | resources << Dir.glob('README{,.*}').first 114 | end 115 | 116 | resources.compact! 117 | 118 | resources.each do |file| 119 | case file 120 | when /\.gemspec$/ 121 | text = File.read(file) 122 | gemspec = /^---/.match(text) ? YAML.load(text) : Gem::Specification.load(file) 123 | project.import_gemspec(gemspec) 124 | when /^README/i 125 | readme = Readme.load(file) 126 | project.import_readme(readme) 127 | else 128 | text = File.read(file) 129 | obj = /^---/.match(text) ? YAML.load(text) : text 130 | case obj 131 | when ::Gem::Specification 132 | project.import_gemspec(obj) 133 | when String 134 | project.import_readme(obj) 135 | #when Hash 136 | #metadata.mesh(obj) 137 | else 138 | puts "Cannot convert #{file} (skipped)" 139 | end 140 | end 141 | end 142 | 143 | #project.root = root 144 | 145 | # load any meta entries that may already exist 146 | #project.reload unless options[:replace] 147 | 148 | #profile_file = profile.file ? profile.file : File.join(root,'PROFILE') 149 | 150 | #if $TRIAL 151 | #else 152 | # #if $FORCE or !(has_package or has_profile) 153 | # metadata.backup! 154 | # metadata.save! #(package_file) 155 | # #end 156 | #end 157 | 158 | text = project.profile.render 159 | 160 | if $TRIAL 161 | puts text 162 | else 163 | File.open('Profile', 'w'){ |f| f << text } 164 | 165 | puts "'Profile' has been created or updated. Please review" 166 | puts "this file carefully and edit as needed.\n" 167 | end 168 | 169 | 170 | #diff = Dir.glob('**/*', File::FNM_DOTMATCH) - current 171 | #diff = diff.select{ |f| File.file?(f) } 172 | 173 | #if diff.empty? 174 | # puts "Nothing has been done." 175 | #else 176 | # diff.each do |f| 177 | # puts " #{f}" 178 | # end 179 | #end 180 | end 181 | 182 | # 183 | #def render_templates(project) 184 | # profile = project.profile 185 | # ERB.new(template).result(binding) 186 | #end 187 | 188 | # 189 | #def print_fixes 190 | # root = Dir.pwd 191 | # fixes = [] 192 | # pwd = Pathname.new(root) 193 | # files = [Gemdo::Package.find(root), Gemdo::Profile.find(root)] 194 | # files.each do |file| 195 | # File.readlines(file).each{ |l| l.grep(/FIXME/).each{ |r| fixes << file.relative_path_from(pwd) } } 196 | # end 197 | # fixes.uniq! 198 | # unless fixes.empty? 199 | # puts "The following files require editing:\n" 200 | # puts " " + fixes.join("\n ") 201 | # end 202 | #end 203 | 204 | # 205 | def require_rubygems 206 | begin 207 | require 'rubygems' 208 | #::Gem::manage_gems 209 | rescue LoadError 210 | raise LoadError, "RubyGems is not installed." 211 | end 212 | end 213 | 214 | end 215 | end 216 | end 217 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/readme.rb: -------------------------------------------------------------------------------- 1 | require 'pom/core_ext' 2 | 3 | module POM 4 | 5 | # Readme is designed to parse a README file 6 | # applying various hueristics in order to 7 | # descern metadata about a project. 8 | # 9 | class Readme 10 | 11 | # File glob for matching README file. 12 | FILE_PATTERN = "README{,.*}" 13 | 14 | # 15 | attr :root 16 | 17 | # 18 | attr :file 19 | 20 | # 21 | attr :text 22 | 23 | # 24 | def self.load(path) 25 | path = Pathname.new(path) 26 | if path.directory? 27 | path = path.first(FILE_PATTERN, :casefold) 28 | end 29 | if path 30 | new(path.read, path) 31 | else 32 | new("") 33 | end 34 | end 35 | 36 | # 37 | def initialize(text, file=nil) 38 | @text = text 39 | @file = file 40 | @cache = {} 41 | parse 42 | end 43 | 44 | # 45 | def [](name) 46 | return nil unless file 47 | if respond_to?(name) 48 | send(name) 49 | else 50 | nil 51 | end 52 | end 53 | 54 | # 55 | def name ; @cache[:name] ; end 56 | 57 | # DEPRECATE 58 | #alias_method :project, :name 59 | 60 | # 61 | def title 62 | @cache[:title] 63 | end 64 | 65 | # 66 | def description 67 | @cache[:description] 68 | end 69 | 70 | # 71 | def license 72 | @cache[:license] 73 | end 74 | 75 | # 76 | def copyright 77 | @cache[:copyright] 78 | end 79 | 80 | # 81 | def authors 82 | @cache[:authors] 83 | end 84 | 85 | # 86 | def resources 87 | @cache[:resources] ||= {} 88 | end 89 | 90 | # 91 | def homepage 92 | resources[:home] 93 | end 94 | 95 | # 96 | def wiki 97 | resources[:wiki] 98 | end 99 | 100 | # 101 | def issues 102 | resources[:issues] 103 | end 104 | 105 | # Return file extension of README. Even if the file has no extension, 106 | # this method will look at the contents and try to determine it. 107 | #-- 108 | # TODO: improve type heuristics 109 | #++ 110 | def extname 111 | ext = File.extname(file) 112 | if ext.empty? 113 | ext = '.rdoc' if /^\=/ =~ text 114 | ext = '.md' if /^\#/ =~ text 115 | end 116 | return ext 117 | end 118 | 119 | private 120 | 121 | # 122 | def parse 123 | parse_title 124 | parse_description 125 | parse_license 126 | parse_copyright 127 | parse_resources 128 | end 129 | 130 | # 131 | def parse_title 132 | if md = /^[=#]\s*(.*?)$/m.match(text) 133 | title = md[1].strip 134 | @cache[:title] = title 135 | @cache[:name] = title.downcase.gsub(/\s+/, '_') 136 | end 137 | end 138 | 139 | # 140 | def parse_description 141 | if md = /[=#]+\s*(DESCRIPTION|ABSTRACT)[:]*(.*?)[=#]+/m.match(text) 142 | @cache[:description] = md[2].strip #.sub("\n", ' ') # unfold instead of sub? 143 | else 144 | d = [] 145 | o = false 146 | text.split("\n").each do |line| 147 | if o 148 | if /^(\w|\s*$)/ !~ line 149 | break d 150 | else 151 | d << line 152 | end 153 | else 154 | if /^\w/ =~ line 155 | d << line 156 | o = true 157 | end 158 | end 159 | end 160 | @cache[:description] = d.join(' ').strip 161 | end 162 | end 163 | 164 | # 165 | def parse_license 166 | if md = /[=]+\s*(LICENSE)/i.match(text) 167 | section = md.post_match 168 | @cache[:license] = ( 169 | case section 170 | when /LGPL/ 171 | "LGPL" 172 | when /GPL/ 173 | "GPL" 174 | when /MIT/ 175 | "MIT" 176 | when /BSD/ 177 | "BSD" 178 | end 179 | ) 180 | end 181 | end 182 | 183 | # 184 | def parse_copyright 185 | md = /Copyright.*?\d+(.*?)$/.match(text) 186 | if md 187 | @cache[:copyright] = md[0] 188 | @cache[:authors] = md[1].split(/(and|\&|\,)/).map{|a|a.strip} 189 | end 190 | end 191 | 192 | # 193 | def parse_resources 194 | @cache[:resources] = {} 195 | 196 | scan_for_github 197 | 198 | text.scan(/(\w+)\:\s*(http:.*?[\w\/])$/) do |m| 199 | @cache[:resources][$1] = $2 200 | end 201 | end 202 | 203 | # 204 | # TODO: Improve on github matching. 205 | def scan_for_github 206 | text.scan(/http\:.*?github\.com.*?[">\s]/) do |m| 207 | case m 208 | when /wiki/ 209 | @cache[:resources]['wiki'] = m[0...-1] 210 | when /issues/ 211 | @cache[:resources]['issues'] = m[0...-1] 212 | else 213 | if m[0] =~ /:\/\/github/ 214 | @cache[:resources]['code'] = m[0...-1] 215 | else 216 | @cache[:resources]['home'] = m[0...-1] 217 | end 218 | end 219 | end 220 | end 221 | 222 | # TODO 223 | def scan_for_googlegroups 224 | 225 | end 226 | 227 | # TODO: parse readme into sections of [label, text]. 228 | #def sections 229 | # @sections ||= ( 230 | # secs = text.split(/^(==|##)/) 231 | # secs.map do |sec| 232 | # i = sec.index("\n") 233 | # n = sec[0..i].sub(/^[=#]*/, '') 234 | # t = sec[i+1..-1] 235 | # [n, t] 236 | # end 237 | # ) 238 | #end 239 | end 240 | 241 | class Project 242 | 243 | # 244 | def readme 245 | @readme ||= Readme.load(root) 246 | end 247 | 248 | # Get POM metadata from a README. This is intended to make it 249 | # fairly easy to build a set of POM's metadata files if you 250 | # already have a README. 251 | def import_readme(readme=nil) 252 | readme = readme || self.readme 253 | 254 | profile.name = readme.name if readme.name 255 | 256 | profile.title = readme.title if readme.title 257 | profile.description = readme.description if readme.description 258 | profile.license = readme.license if readme.license 259 | profile.copyright = readme.copyright if readme.copyright 260 | profile.authors = readme.authors if readme.authors 261 | 262 | profile.resources.homepage = readme.homepage if readme.homepage 263 | profile.resources.wiki = readme.wiki if readme.wiki 264 | profile.resources.issues = readme.issues if readme.issues 265 | end 266 | 267 | end 268 | 269 | end 270 | 271 | -------------------------------------------------------------------------------- /site/blog/2010-05-24-requirements/index.post: -------------------------------------------------------------------------------- 1 | title : Requirements Specification 2 | author : Trans 3 | date : 2010-05-24 4 | tags : rubygems, bundler, metadata, configuration, pom 5 | layout : post 6 | 7 | --- markdown 8 | 9 | Recently, on my personal programming [blog](http://trans.github.com), I expounded on some concerns I have with [Bundler](http://gembundler.com/). These concerns have their origin in my work on [POM](http://github.com/proutils/pom) and related ProUtils projects such as [Syckle](http://github.com/proutils/syckle) and [Box](http://github.com/proutils/box). These projects utilize requirements information. If Bundler becomes widely used outside of Rails development, and it seems it might, then it is imporatan that I figure out how to best fit Bundler into the POM's design. 10 | 11 | Of course, as with any new technologies, my initial take on Bundler held some false assumptions, and thus some over-blown concerns. However, I still see some issues with the design. Nonetheless I figure most issues will be worked out in time --and if not, and the project is fundamentally flawed in some fahsion, then it will eventually wither, and be replaced by something better. 12 | 13 | --- markdown 14 | 15 | So rather than jumping head first into Gemfile support, which at first I thought would be necessary (and so a source of some of my initial fears), I have simply built in conditional support. If a Gemfile is present, than POM will take advantage of it (unless otherwise instructed). If not, then it will fallback to its own metadate designs. Speaking of which, the process of working through the Gemfile specification, considering all it's nuanced effects and reviewing alternate solutions in the ecosystem, has led me to create a more robust requirements specification for POM itself. 16 | 17 | Here is an example of this sepcification. The file is conventually named `REQUIRE`. It is purely YAML and only includes the MRI requirements. JRuby requirements, for example, would be kept in a separate file `REQUIRE.jruby`. For realism I have translated Rails' Gemfile. Of course, that file keeps changing as Rails evolves, so here is a [copy](gemfile.html) of the file I used. 18 | 19 | --- html 20 | 21 |
22 |   runtime:
23 |     - rake 0.8.7+
24 |     - mocha 0.9.8+
25 |     - nokogiri 1.4.0+
26 |     - system_timer
27 |     - ruby-debug 0.10.3+
28 |     - json
29 |     - yajl-ruby
30 |     # AP
31 |     - rack-test 0.5.3
32 |     - RedCloth 4.2.2+
33 |     # AR
34 |     - sqlite3-ruby 1.3.0.beta.2
35 | 
36 |   runtime/recommend:
37 |     - pg 0.9.0+
38 |     - mysql 2.8.1+
39 | 
40 |   runtime/optional:
41 |     - fcgi 0.8.7+  # does not compile on mri 1.9+
42 | 
43 |   runtime/vendor:
44 |     - arel
45 | 
46 |   development/document:
47 |     - rdoc 2.1
48 | 
49 |   development/test:
50 |     - rspec
51 | 
52 | 53 | --- markdown 54 | 55 | The most important feature of POM's `REQUIRE` file in contrast to Bundler's `GemFile` is that it is 100% declarative. It is not Ruby code. So it cannot include if-then clauses and the like. Rather it provides only the criteria that such a specification must. This is important. Because it is fully declarative, any tool, on any system, can make complete use of the information it provides, irregardless of the state of the system at the time the file is parsed. It also does not support arbitraty groups. I understand how they can be useful, but they add a great deal of complexity to the specification. For POM the simpilicty is sufficiant. You can always use a Gemfile if you need it's capabilities. Of course this example doesn't show all possible section type. One in particular is `production` which might be used by Rails application in place of `runtime`. 56 | 57 | For the `arel` package, notice that it is listed under `runtime/vendor`. I get the impression that a git repo was being referenced because arel is still under heavy development, and therefore the actual intent is to bundle it with the rails gem until it is ready to stand on it's own. The REQUIRE specification doesn't need to know any of this other than the fact that exists as such. The originals `path` option is not needed becuase `vendor/arel` will be added to the `LOAD_PATH` in the `VERSION` file. And we do not need the `git` option becuase it doesn't matter to POM how the code got there (read: "Use git submodules!"). For the same reason, basically, we don't need an entry for `rails` itself either. 58 | 59 | You will also not see in this alternative design, anything about the original's :require => option. This is something `Bundler.require` uses to automate requires. First of all, it is perhaps not the best idea in the world because many libraries can be used in piecemeal fashion --they do not necessarily have a canonical file to be required. But more importantly, we should be concerned with using `Bundler.require` in our code, because it ties us down to RubyGems, making our programs unusable by other installation systems. While we might all be fans of RubyGems, it is still important not to box ourselves in and remain open to innovations in the field (not to mention traditional techniques). Perhaps the best approach to dealing with this with Bundler is simply to isolate it's use via a conditional `if defined?('RubyGems')` to make sure it is *already* loaded. E.g. 60 | 61 | --- coderay.ruby 62 | 63 | if defined?('RubyGems') 64 | require 'bundler' 65 | Bundler.setup 66 | end 67 | 68 | --- markdown 69 | 70 | There are a few special pieces of information that might also be useful but are not shown here. 71 | 72 | * `alternate/provides`: What other packages does this package provide the same dependency fulfillment. For example, a package 'bar-plus' might fulfill the same dependency criteria as package 'bar', so 'bar-plus' is said to provide 'bar'. 73 | * `alternate/replaces`: What other packages does this package replace. This is very much like #provides but expresses a overriding relation. For instance "libXML" has been replaced by "libXML2". 74 | * `alternate/conflicts`: With what other packages does this package conflict. This information would be used to ensure two conflicting gems are not being used together. 75 | 76 | Another potential concern not covered by this design is the question of *external* dependencies. For example, sqlite3-ruby requires that sqlite3 be present on the system. In other words, is there any way to specify, or should we even be concerned about, dependencies on packages in other packaging systems? At the very least I suppose we could provide an `external` section to list them, even if they have no actual effect. 77 | 78 | Of course, the main purpose of this post is not to consider every possible field entry that may be useful, but rather to demonstrate POM alternate format. By using this clean, static format, we are able to get most of the configurablity of a Gemfile while ensuring a fully accessible configuration. In doing so, it opens requirements configuration up to wider use. 79 | 80 | -------------------------------------------------------------------------------- /lib/gemdo/project/paths.rb: -------------------------------------------------------------------------------- 1 | module GemDo 2 | 3 | class Project 4 | 5 | ## 6 | # Locations of typical project directories. 7 | # 8 | # This is handled via delegation in order to provide a 9 | # cleaner interface. 10 | # 11 | class Paths 12 | 13 | # Initialize Paths instance. 14 | def initialize(project) 15 | @project = project 16 | end 17 | 18 | # Location of project source code. Currently, this is always 19 | # the same as the root. 20 | # 21 | # @return [Pathname] 22 | def root 23 | @project.root 24 | end 25 | 26 | # Alias for +root+. 27 | # 28 | # @todo Support alternate source location in the future verison? 29 | alias src root 30 | alias source root 31 | 32 | # The bin directory is the place to keep executables. 33 | # 34 | # Get pathname of given bin `path`. Or without `path` 35 | # returns the pathname for the bin directory. 36 | # 37 | # @return [Pathname] 38 | def bin(path=nil) 39 | if path 40 | doc + path 41 | else 42 | @doc ||= root + 'bin' 43 | end 44 | end 45 | 46 | # The ext directory is the place to keep extensions, typically 47 | # written in C. 48 | # 49 | # Get pathname of given doc `path`. Or without `path` 50 | # returns the pathname for the doc directory. 51 | # 52 | # @return [Pathname] 53 | def ext(path=nil) 54 | if path 55 | doc + path 56 | else 57 | @doc ||= root + 'ext' 58 | end 59 | end 60 | 61 | # The doc directory is the place to keep documentation. The directory 62 | # is intended to be distributed with a package, but this is not often 63 | # done these days since RubyGems generates RDocs on demand, and 64 | # documentation is often found online. 65 | # 66 | # Get pathname of given doc `path`. Or without `path` 67 | # returns the pathname for the doc directory. 68 | # 69 | # @return [Pathname] 70 | def doc(path=nil) 71 | if path 72 | doc + path 73 | else 74 | @doc ||= root + 'doc' 75 | end 76 | end 77 | 78 | # The log/ directory stores log output created by 79 | # build tools. If you want to publish your logs as part 80 | # of your website (which might be a very nice thing to do) 81 | # symlink it into you site location. 82 | # 83 | # Get pathname of given log +path+. Or without +path+ 84 | # returns the pathname for the log directory. 85 | # 86 | # @return [Pathname] 87 | def log(path=nil) 88 | if path 89 | log + path 90 | else 91 | @log ||= root + 'log' 92 | end 93 | end 94 | 95 | # Returns the pathname for the package directory. 96 | # With +path+ returns the pathname within the pacakge path. 97 | # 98 | # @return [Pathname] 99 | def pkg(path=nil) 100 | if path 101 | pkg + path 102 | else 103 | @pkg ||= root.first('{pkg,pack,package}{,s}') || root + 'pkg' 104 | end 105 | end 106 | 107 | # The `tmp/` or `.cache/` directory is used by build tools to 108 | # store temporary files. For instance, the +pom+ command uses 109 | # it to store backups of metadata entries when overwriting 110 | # old entries. .cache/ should be in your SCM's 111 | # ignore list. 112 | # 113 | # Get pathname of given cache +path+. Or without +path+ 114 | # returns the pathname for the cache directory. 115 | # 116 | # @return [Pathname] 117 | def cache(path=nil) 118 | if path 119 | cache + path 120 | else 121 | @cache ||= root.first('tmp,.cache') || root + 'tmp' 122 | end 123 | end 124 | 125 | # Alias for #cache. 126 | alias tmp cache 127 | 128 | # The directory for for build tools to place their configration 129 | # files. It is either `.config/`, `.etc/`, `config/` or `etc/`, 130 | # matched in that order of precednece. 131 | # 132 | # Pathname of given config +path+. Or without `path` 133 | # Returns the path to the config directory (either `.etc` 134 | # or `etc`). 135 | # 136 | # @return [Pathname] 137 | def config(path=nil) 138 | if path 139 | config + path 140 | else 141 | @config ||= root.first('{.config,.etc,config,etc}') || root + 'etc' 142 | end 143 | end 144 | 145 | alias etc config 146 | 147 | =begin 148 | # The plug/ directory serves the same purpose as 149 | # the lib/ directory. It simply provides a place 150 | # to put plugins separate from the main lib/ files. 151 | # 152 | # Get pathname of given plugin +path+. Or without +path+ 153 | # returns the pathname for the plugin directory. 154 | # 155 | # TODO: This assumes lib/ is in the load path, and is used 156 | # to house plugin/. This is of course typical. However it is 157 | # possible to alter the load path. So it may not always be the 158 | # case. In the future, it must be decided if we should standardize 159 | # around the lib/ convention (though you could still add others to 160 | # the load path) or allow it to be complete free form. As I did for 161 | # bin/, I prefer the former, but have not yet firmly decided. 162 | def plugin(path=nil) 163 | if path 164 | plugin + path 165 | else 166 | @plugin ||= root.first('lib/plugins') || root + 'lib/plugins' 167 | end 168 | end 169 | 170 | # 171 | alias_method :plugins, :plugin 172 | =end 173 | 174 | # The `script/` directory is like the task/ 175 | # directory but usually holds executables that are made 176 | # available to the end-installers. 177 | # 178 | # Get pathname of given script +path+. Or without +path+ 179 | # returns the pathname for the script directory. 180 | # 181 | # @return [Pathname] 182 | def script(path=nil) 183 | if path 184 | script + path 185 | else 186 | @script ||= root + 'script' 187 | end 188 | end 189 | 190 | # The task/ directory is where task scripts are 191 | # stored used by build tools, such as Rake and Syckle. 192 | # 193 | # Get pathname of given task +path+. Or without +path+ 194 | # returns the pathname for the task directory. 195 | # 196 | # @deprecated Using tasks/ has become rather passe. 197 | # 198 | # @return [Pathname] 199 | def task(path=nil) 200 | if path 201 | task + path 202 | else 203 | @task ||= root.first('{task,tasks}') || root+'task' 204 | end 205 | end 206 | 207 | # The `site/`, `web/` or `website/` directory is 208 | # where a project's website files are stored. 209 | # 210 | # Get pathname of given site `path`. Or without +path+ 211 | # returns the pathname for the site directory. 212 | # 213 | # @return [Pathname] 214 | def website(path=nil) 215 | if path 216 | site + path 217 | else 218 | @site ||= root.first('{site,web,website}') || root + 'web' 219 | end 220 | end 221 | 222 | # Alias for #website. 223 | alias web website 224 | alias site website 225 | 226 | # This provides a temporary system location outside the 227 | # project directory. 228 | # 229 | # Get pathname of given temporary `path`. Or without `path` 230 | # returns the pathname to the temporary directory. 231 | # 232 | # @todo Add project name to end of path? 233 | # 234 | # @return [Pathname] 235 | def systmp(path=nil) 236 | if path 237 | systmp + path 238 | else 239 | @systmp ||= Pathname.new(Dir.tmpdir) 240 | end 241 | end 242 | 243 | private 244 | 245 | # Does a file exist in the project? 246 | # Returns the first match. 247 | def file?(glob) 248 | Pathname.glob(root + glob).first 249 | end 250 | 251 | # Determines if a directory exists within the project. 252 | # 253 | # @param [String] path 254 | # The path of the directory, relative to the project. 255 | # 256 | # @return [Boolean] 257 | # true if directory exists, otherwise false. 258 | def directory?(path) 259 | root.join(path).directory? 260 | end 261 | 262 | end 263 | 264 | end 265 | 266 | end 267 | -------------------------------------------------------------------------------- /work/deprecated/spun-off/resolver.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | require 'pom/resolver/rubygems' 4 | require 'pom/resolver/gemcutter' 5 | 6 | # The Resolver class takes the project requirements list 7 | # and resolves the dependencies against a gem source. 8 | # 9 | # It can be used to determines if the current load environment 10 | # satisfies the requirements or to produce a lock file based 11 | # on Rubygems.org. 12 | # 13 | #-- 14 | # TODO: Do we need a way to limit to certain dependency groups? 15 | # TODO: Gemcutter API does not separate runtime and development. 16 | # TODO: Platform is not being taken into consideration. 17 | #++ 18 | class Resolver 19 | 20 | # POM::Project instance. 21 | attr_reader :project 22 | 23 | # Include runtime dependencies only. 24 | attr_accessor :runtime 25 | 26 | # Include prereleases. 27 | attr_accessor :prerelease 28 | 29 | # Resolve against local gems. 30 | attr_accessor :local 31 | 32 | # Output format (default, lock or requests) 33 | attr_accessor :format 34 | 35 | # Gem source (e.g. local gems or gemscutter). 36 | attr :source 37 | 38 | # 39 | def initialize(root, options={}) 40 | @project = Project.new(root) 41 | options.each do |k,v| 42 | __send__("#{k}=", v) 43 | end 44 | end 45 | 46 | # 47 | def project_gem 48 | @project_gem ||= Gem.new(:name=>project.name, :number=>project.version, :dependencies=>requirements) 49 | end 50 | 51 | # 52 | def setup 53 | if local 54 | @source = Rubygems.new(project_gem) 55 | else 56 | @source = GemCutter.new(project_gem) 57 | end 58 | 59 | # fetch all gems that could be used 60 | #source.fetch_requirements(requirements) 61 | @source.fetch 62 | 63 | # Exclude prereleases unless +prerelease+ option is active. 64 | if !prerelease 65 | @source.cache.each do |name, gems| 66 | gems.reject!{ |gem| /[A-Za-z]/ =~ gem.number } 67 | end 68 | end 69 | 70 | # create a dependency graph by applying constraints 71 | @source.gems.each do |name, gems| 72 | gems.each do |gem| 73 | gem.apply_constraints(@source.gems) 74 | end 75 | end 76 | 77 | case format 78 | #when :requests 79 | when :breakdown 80 | produce_dependency_breakdown(source) 81 | else 82 | produce_dependency_lockdown(source) 83 | end 84 | end 85 | 86 | # Navigate graph until complete route is found. 87 | def resolve 88 | picks = {} 89 | resolve_gem(project_gem, picks) 90 | picks.values.map{ |pick| pick.gem } 91 | end 92 | 93 | # 94 | def produce_dependency_breakdown(source) 95 | source.gems.each do |name, gems| 96 | puts "#{name}" 97 | gems.each do |gem| 98 | puts " #{gem.number}" 99 | gem.dependencies.each do |(depname, constraint)| 100 | puts " #{depname} #{constraint}" 101 | end 102 | end 103 | end 104 | puts 105 | puts "MISSING" 106 | source.missing.each do |name, constraint| 107 | puts " #{name} #{constraint}" 108 | end 109 | end 110 | 111 | # TODO: traverse resolved dependencies and find missing matches for actual missing. 112 | def produce_dependency_lockdown(source) 113 | matches = resolve 114 | 115 | $stderr.puts "(#{matches.size} gems)" 116 | 117 | matches = matches.group_by{ |g| g.name } 118 | 119 | matches.each do |name, gems| 120 | vers = gems.map{ |gem| gem.number }.sort.reverse 121 | print(name, vers) 122 | end 123 | 124 | #source.missing.each do |name, constraint| 125 | # print(name, [constraint]) 126 | #end 127 | end 128 | 129 | # 130 | def requirements 131 | @requirements ||= ( 132 | if runtime 133 | project.requirements.production.map do |req| 134 | [req.name, req.constraint.to_s] 135 | end 136 | else 137 | project.requirements.map do |req| 138 | [req.name, req.constraint.to_s] 139 | end 140 | end 141 | ) 142 | end 143 | 144 | # 145 | def resolve_gem(main_gem, picks) 146 | main_gem.resolved.each do |name, gem_choices| 147 | next if gem_choices.size == 0 148 | if pick = picks[name] 149 | if gem_choices.find{ |g| g == pick.gem } 150 | # we're good! 151 | else 152 | # damn! reset picks and try another 153 | if bump = pick.bump 154 | picks = pick.reset 155 | picks[name] = bump 156 | resolve_gem(bump.gem, picks) 157 | else 158 | master_pick = picks[pick.master.name] 159 | if bump = master_pick.bump 160 | picks = master_pick.reset 161 | picks[pick.master.name] = bump 162 | resolve_gem(bump.gem, picks) 163 | else 164 | raise "no possible resolution" 165 | end 166 | end 167 | end 168 | else 169 | gem_index = 0 170 | gem_choice = gem_choices[gem_index] 171 | picks[name] = Pick.new(main_gem, gem_index, gem_choice, picks) 172 | resolve_gem(gem_choice, picks) 173 | end 174 | end 175 | end 176 | 177 | # Apply requirements to table, elminating version that do not satisfy 178 | # the version constraints. 179 | #def apply_constraints(requirements) 180 | # matches = [] 181 | # requirements.each do |name, constraint| 182 | # gems = available_versions.select{ |gem| gem[:name] == name } 183 | # gems = gems.select{ |gem| gem[:number].match?(constraint) } 184 | # matches.concat(gems) 185 | # end 186 | # matches 187 | #end 188 | 189 | # Output resolution. Make sure versions are sorted from most to least recent. 190 | def print(name, versions) 191 | if format == :lock 192 | puts "gem '%s', '= %s'" % [name.to_s, versions.first] 193 | else 194 | puts "%s (%s)" % [name.to_s, versions.join(", ")] 195 | end 196 | end 197 | 198 | # Instead of using a Hash as given by the url api, we encapsulate each 199 | # library as a Gem object. 200 | class Gem 201 | attr :name 202 | attr :number 203 | attr :dependencies 204 | attr :platform 205 | 206 | # Hash of resolved dependencies. 207 | attr :resolved 208 | 209 | # 210 | def initialize(settings) 211 | @name = settings[:name] 212 | @number = VersionNumber.new(settings[:number]) 213 | @platform = settings[:platform] 214 | @dependencies = settings[:dependencies] 215 | 216 | @resolved = {} #Hash.new{|h,k|h[k]=[]} 217 | end 218 | 219 | # Apply dependencies, elminating versions that do not satisfy 220 | # the version constraints. 221 | def apply_constraints(available_gems) 222 | dependencies.each do |name, constraint| 223 | resolved[name] = (available_gems[name] || []).dup 224 | end 225 | dependencies.each do |name, constraint| 226 | resolved[name].reject!{ |gem| !gem.number.match?(constraint) } 227 | end 228 | dependencies.each do |name, constraint| 229 | resolved[name].sort!{ |a,b| b.number <=> a.number } 230 | end 231 | end 232 | def inspect 233 | "#{name} #{number}" 234 | end 235 | end 236 | 237 | # When resolving dependencies, a Pick is used to encapsulate a 238 | # dependency choice. In other words, if a requirement can be satisfied 239 | # by multiple gems, the latest version is picked, placed in an instance of 240 | # Pick along with a snapshot of the current state of all picks (resolution) 241 | # at that moment. If later a conflict occurs which traces back to a pick, 242 | # the pick can reset the reslution state and try a different pick. 243 | class Pick 244 | attr :master 245 | attr :index 246 | attr :gem 247 | attr :reset 248 | def initialize(master, index, gem, reset) 249 | @master = master 250 | @index = index 251 | @gem = gem 252 | @reset = reset.dup 253 | end 254 | def name 255 | gem.name 256 | end 257 | def bump 258 | gem = master.resolved[name][index+1] 259 | if gem 260 | Pick.new(master, index+1, gem, reset) 261 | else 262 | nil # none left to try 263 | end 264 | end 265 | def inspect 266 | "(#{master.inspect} -> #{gem.inspect})" 267 | end 268 | end 269 | 270 | end 271 | 272 | end 273 | 274 | -------------------------------------------------------------------------------- /work/deprecated/metastore.rb: -------------------------------------------------------------------------------- 1 | require 'pom/core_ext' 2 | require 'pom/errors' 3 | 4 | module POM 5 | 6 | # MetaStore serves as the base class for the Metadata 7 | # class. It connects the file system to the POM model. 8 | class MetaStore 9 | 10 | # Parent store, or root pathname. The topmost store 11 | # should set this to the root pathname. All substores 12 | # use this to reference their parent store (akin to 13 | # parent directory). 14 | attr :parent 15 | 16 | # Filesystem path for the file store. 17 | attr :pathname 18 | 19 | # New file store. The +parent+ is either the parent 20 | # Pathname or FileStore and +directory+ is the name 21 | # of the subdirectory with in it. If this is a toplevel 22 | # FileStore then set +parent+ to +nil+. 23 | def initialize(parent, directory) 24 | @parent = parent 25 | @data = {} 26 | @pathname = ( 27 | case parent 28 | when Pathname 29 | parent + directory 30 | when FileStore 31 | parent.pathname + directory 32 | else 33 | Pathname.new(directory) 34 | end 35 | ) 36 | end 37 | 38 | # 39 | def method_missing(sym, *args) 40 | name = sym.to_s 41 | case name 42 | when /=$/ 43 | super(sym, *args) unless args.size == 1 44 | self[name] = args.first 45 | else 46 | super(sym, *args) unless args.empty? 47 | self[name] 48 | end 49 | end 50 | 51 | # Get value from store by name. The value will 52 | # be cached so the file system is only hit once. 53 | def [](name) 54 | name = name.to_s 55 | if @data.key?(name) 56 | @data[name] 57 | else 58 | @data[name] = get!(name) 59 | end 60 | end 61 | 62 | # Set value. 63 | def []=(name, value) 64 | @data[name.to_s] = value 65 | end 66 | 67 | # Get a metadata +entry+, where entry is a pathname. 68 | # If it is a directory, will create a new FileStore object. 69 | def get!(name) 70 | case name 71 | when String, Symbol 72 | path = @pathname + name #.to_s 73 | else 74 | path = name 75 | end 76 | 77 | if path.directory? 78 | data = FileStore.new(self, path.basename) 79 | elsif path.file? 80 | text = path.read.strip 81 | data = (/\A^---/ =~ text ? YAML.load(text) : text) 82 | data = data.to_list if self.class.listings[name] 83 | else 84 | data = self.class.defaults[name] 85 | data = instance_eval(&data) if Proc === data 86 | end 87 | data 88 | end 89 | 90 | # List of available entries. 91 | def entries 92 | pathname.glob('*').map{ |path| File.basename(path) } 93 | end 94 | 95 | alias_method :keys, :entries 96 | 97 | # Has a value been loaded from the file system? 98 | def key?(name) 99 | @data.key?(name.to_s) 100 | end 101 | 102 | # 103 | def ignore 104 | [] 105 | end 106 | 107 | # Return +root+ pathname. The root pathname is the topmost 108 | # point of entry of this metadata set, and is (almost 109 | # certainly) the project root directory. 110 | def root 111 | case parent 112 | when Pathname then parent 113 | when FileStore then parent.root 114 | else nil 115 | end 116 | end 117 | 118 | # 119 | def inspect 120 | "#<#{self.class} #{@data.inspect}>" 121 | end 122 | 123 | ## 124 | #def initialize_attributes 125 | # path.glob('*').each do |file| 126 | # name = path_to_name(file, path) 127 | # next unless name 128 | # @_keys << name 129 | # if not respond_to?(name) 130 | # if /\W/ !~ name # only files that are all word letters 131 | # (class << self; self; end).class_eval do 132 | # attr_accessor name 133 | # end 134 | # end 135 | # end 136 | # end 137 | #end 138 | 139 | # Subclasses can override this. 140 | def new_project_defaults 141 | {} 142 | end 143 | 144 | # Load attribute values from file system. 145 | def load!(alt_path=nil) 146 | path = alt_path ? Pathname.new(alt_path) : pathname() 147 | path.glob('*').each do |file| 148 | #next if file.to_s.index(/[.]/) # TODO: rejection filter 149 | name = file.basename #path_to_name(file, path) 150 | self[name] = get!(file) 151 | end 152 | self 153 | end 154 | 155 | # Load attribute values from file system, but only if 156 | # the attribute is not currently set. 157 | def read! 158 | path = pathname 159 | path.glob('*').each do |file| 160 | #next if file.to_s.index(/[.]/) # TODO: rejection filter 161 | name = file.basename #path_to_name(file, path) 162 | self[name] = get!(file) unless key?(name) 163 | end 164 | self 165 | end 166 | 167 | # 168 | def save!(path=nil) 169 | @pathname = Pathname.new(path) if path 170 | @pathname.mkdir unless @pathname.exist? 171 | @data.each do |name, value| 172 | write!(name, value) 173 | end 174 | end 175 | 176 | private 177 | 178 | # Write entry to file system. 179 | #-- 180 | # TODO: escape filename for file system as needed 181 | #++ 182 | 183 | def write!(name, value, overwrite=true) 184 | return if value.nil? 185 | fname = name # TODO 186 | file = @pathname + fname 187 | if file.exist? 188 | if overwrite 189 | text = file.read 190 | yaml = /\A---/ =~ text 191 | value = value.to_yaml if yaml 192 | if text != value 193 | write_raw!(file, value) 194 | end 195 | end 196 | else 197 | case value 198 | when Array, Hash 199 | return if value.empty? 200 | out = value.to_yaml 201 | when String, Numeric 202 | out = value 203 | else 204 | out = value.to_yaml 205 | end 206 | write_raw!(file, out) 207 | end 208 | end 209 | 210 | # Write +output+ to a file referenced by the given +pathname+. 211 | # This method first ensures the pathname's parent directory exists. 212 | 213 | def write_raw!(pathname, output) 214 | FileUtils.mkdir_p(pathname.parent) unless pathname.parent.exist? 215 | File.open(pathname, 'w'){ |f| f << output } 216 | end 217 | 218 | # S U P P O R T M E T H O D S 219 | 220 | # 221 | def path_to_name(path, prefix='') 222 | #return nil if file.to_s.index(/[.]/) # TODO: rejection filter 223 | path = path.to_s 224 | path = path.sub(prefix.to_s.chomp('/') + '/', '') 225 | #path = path.gsub('/', '_') 226 | path 227 | end 228 | 229 | public 230 | 231 | # C O N V E R S I O N 232 | 233 | # Convert to YAML. 234 | #-- 235 | # TODO: This might not be the best way to convert to YAML. 236 | #++ 237 | def to_yaml 238 | to_h.to_yaml 239 | end 240 | 241 | # Return metadata in Hash form. 242 | def to_h 243 | read! 244 | @data.dup 245 | end 246 | 247 | # M O D I F I C A T I O N 248 | 249 | # Update underlying data table. 250 | def merge!(other) 251 | case other 252 | when Hash 253 | data = other 254 | when FileStore 255 | data = other.to_h 256 | end 257 | data.each do |name, value| 258 | @data[name.to_s] = value 259 | end 260 | end 261 | 262 | # Like #merge! but does not update a value if it is *empty*. 263 | def mesh!(other) 264 | case other 265 | when Hash 266 | data = other 267 | when FileStore 268 | data = other.to_h 269 | end 270 | data.each do |name, value| 271 | case value 272 | when String, Array, Hash 273 | @data[name.to_s] = value unless value.empty? 274 | else 275 | @data[name.to_s] = value 276 | end 277 | end 278 | end 279 | 280 | # C L A S S M E T H O D S 281 | 282 | # FileStore uses uniquely defined accessors. 283 | def self.attr_accessor(name, options={}) 284 | defaults[name.to_s] = options[:default] if options[:default] 285 | listings[name.to_s] = true if options[:default] == [] 286 | code = %{ 287 | def #{name} 288 | self["#{name}"] 289 | end 290 | def #{name}=(x) 291 | self["#{name}"] = x 292 | end 293 | } 294 | eval code 295 | end 296 | 297 | # Stores default values for attributes. 298 | def self.defaults 299 | @defaults ||= {} 300 | end 301 | 302 | #What attributes are listings. 303 | def self.listings 304 | @listings ||= {} 305 | end 306 | 307 | ## 308 | #def self.alias_accessor(name, orig) 309 | # alias_method(name, orig) 310 | # alias_method("#{name}=", "#{orig}=") 311 | #end 312 | 313 | end 314 | 315 | end 316 | 317 | --------------------------------------------------------------------------------