├── var ├── name ├── title ├── version ├── created ├── customs ├── categories ├── summary ├── copyrights ├── example ├── requirements ├── description ├── repositories ├── organizations ├── resources └── authors ├── pkg └── .gitignore ├── .document ├── Assembly ├── demo ├── applique │ ├── indexer.rb │ ├── instance_text.rb │ └── ae.rb ├── validator │ ├── applique │ │ ├── indexer.rb │ │ └── field_assignment.rb │ ├── 01_introduction.md │ ├── 10_authors.md │ ├── 08_description.md │ ├── 09_suite.md │ ├── 12_resources.md │ ├── 13_repositories.md │ ├── 05_codename.md │ ├── 06_title.md │ ├── 07_summary.md │ ├── 17_alternatives.md │ ├── 24_engines.md │ ├── 19_conflicts.md │ ├── 22_organizations.md │ ├── 21_created.md │ ├── 11_paths.md │ ├── 25_platform.md │ ├── 14_date.md │ ├── 20_install_message.md │ ├── 15_requirements.md │ ├── 03_name.md │ ├── 04_version.md │ ├── 23_copyrights.md │ ├── 99_extra.md │ └── 02_initialize.md ├── componenets │ ├── applique │ │ └── indexer_revision.rb │ ├── requirement │ │ ├── runtime.md │ │ ├── to_h.md │ │ ├── optional.md │ │ ├── repository.md │ │ ├── name.md │ │ ├── development.md │ │ └── parse.md │ └── author │ │ ├── 09_to_h.md │ │ └── 02_initialize.md ├── metadata │ ├── 01_introduction.md │ ├── applique │ │ ├── indexer.rb │ │ └── field_assignment.rb │ ├── 09_suite.md │ ├── 26_namespace.md │ ├── 08_description.md │ ├── 05_codename.md │ ├── 07_summary.md │ ├── 24_engines.md │ ├── 03_name.md │ ├── 25_platform.md │ ├── 20_install_message.md │ ├── 04_version.md │ ├── 99_extra.md │ ├── 21_created.md │ ├── 06_title.md │ ├── 17_alternatives.md │ ├── 14_date.md │ ├── 19_conflicts.md │ ├── 12_resources.md │ ├── 13_repositories.md │ ├── 11_paths.md │ ├── 10_authors.md │ ├── 22_organizations.md │ ├── 02_initialize.md │ ├── 15_requirements.md │ └── 23_copyrights.md ├── loadable │ ├── applique │ │ └── indexer.rb │ ├── 32_find.md │ ├── 30_read.md │ ├── 31_load.md │ └── 33_save.md ├── version │ ├── number │ │ ├── 19_match.md │ │ ├── 20_to_str.md │ │ ├── 11_release_candidate.md │ │ ├── 02_initialize.md │ │ ├── 10_prerelease.md │ │ ├── 05_build.md │ │ ├── 03_parse.md │ │ ├── 09_stable_release.md │ │ ├── 04_crush.md │ │ ├── 08_bump.md │ │ ├── 07_segments.md │ │ └── 06_cmp.md │ └── constraint │ │ ├── 04_to_proc.md │ │ ├── 09_to_gem_version.md │ │ └── 02_initialize.md ├── importer │ └── ruby.md ├── valid │ ├── 08_version_string.md │ └── 05_word.md └── error.md ├── Gemfile ├── .gitignore ├── .yardopts ├── lib ├── indexer │ ├── core_ext │ │ ├── hash │ │ │ ├── to_h.rb │ │ │ └── rekey.rb │ │ └── kernel │ │ │ └── cli.rb │ ├── core_ext.rb │ ├── version │ │ ├── exceptions.rb │ │ └── constraint.rb │ ├── conversion.rb │ ├── components.rb │ ├── importer │ │ ├── gemfile.rb │ │ ├── yaml.rb │ │ ├── version.rb │ │ ├── ruby.rb │ │ ├── gemspec.rb │ │ └── file.rb │ ├── error.rb │ ├── components │ │ ├── dependency.rb │ │ ├── conflict.rb │ │ ├── engine.rb │ │ ├── copyright.rb │ │ ├── organization.rb │ │ ├── author.rb │ │ └── repository.rb │ ├── revision.rb │ ├── conversion │ │ ├── gemfile.rb │ │ └── gemspec.rb │ ├── gemfile.rb │ ├── importer.rb │ ├── loadable.rb │ └── attributes.rb └── indexer.rb ├── bin └── index ├── work ├── deprecated │ ├── gemfile_generator │ │ ├── index-gemfile │ │ └── generate_gemfile.rb │ ├── resources │ │ ├── spec │ │ │ ├── 12_size.md │ │ │ ├── 09_to_h.md │ │ │ ├── 10_to_hash.md │ │ │ ├── 99_method_missing.md │ │ │ └── 11_each.md │ │ └── lib │ │ │ └── indexer │ │ │ └── r2013 │ │ │ └── resources.rb │ ├── cli │ │ ├── show.rb │ │ ├── source.rb │ │ ├── index.rb │ │ ├── init.rb │ │ └── gemspec.rb │ ├── model.rb │ └── revisions │ │ └── r2013 │ │ ├── conflict.rb │ │ ├── copyright.rb │ │ ├── author.rb │ │ └── repository.rb └── reference │ ├── example.gemfile │ ├── plugin_support │ ├── plugins.rb │ └── rubygems.rb │ ├── gemspec_exporter.rb │ ├── r1.gemspec │ ├── pom_classes │ ├── resources.rb │ └── properties.rb │ └── old.gemspec ├── .travis.yml ├── etc └── qed-simplecov.rb ├── data └── indexer │ ├── yaml.txt │ ├── ruby.txt │ ├── index.yesi │ ├── index.yes │ └── index.kwalify ├── Rakefile ├── .index ├── Manifest.txt └── HISTORY.md /var/name: -------------------------------------------------------------------------------- 1 | indexer 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | Indexer 2 | -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 0.3.1 2 | -------------------------------------------------------------------------------- /pkg/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2011-06-01 2 | -------------------------------------------------------------------------------- /var/customs: -------------------------------------------------------------------------------- 1 | --- 2 | - example 3 | -------------------------------------------------------------------------------- /var/categories: -------------------------------------------------------------------------------- 1 | --- 2 | - metadata 3 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | - 2 | ChangeLog.md 3 | DESIGN.md 4 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | -------------------------------------------------------------------------------- /demo/applique/indexer.rb: -------------------------------------------------------------------------------- 1 | require 'indexer' 2 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Enable Your Project's Metadata 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gemspec 3 | 4 | -------------------------------------------------------------------------------- /demo/validator/applique/indexer.rb: -------------------------------------------------------------------------------- 1 | require 'indexer' 2 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - 2012 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /var/example: -------------------------------------------------------------------------------- 1 | This is just an example of custom metadata. 2 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - qed 2.9+ (test) 3 | - ae (test) 4 | 5 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | Indexer provides projects with a universal metadata format. 2 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: http://github.com/rubyworks/indexer/indexer.git 3 | -------------------------------------------------------------------------------- /var/organizations: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Rubyworks 3 | website: http://rubyworks.github.com 4 | -------------------------------------------------------------------------------- /demo/componenets/applique/indexer_revision.rb: -------------------------------------------------------------------------------- 1 | require 'indexer' 2 | 3 | include Indexer 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | doc 4 | log 5 | tmp 6 | web 7 | work/sandbox 8 | Gemfile.lock 9 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | --title 'Indexer Documentation' 3 | --private 4 | lib/**/*.rb 5 | - 6 | [A-Z]*.* 7 | -------------------------------------------------------------------------------- /lib/indexer/core_ext/hash/to_h.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | def to_h; self; end unless method_defined?(:to_h) 3 | end 4 | 5 | -------------------------------------------------------------------------------- /demo/metadata/01_introduction.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata 2 | 3 | TODO: Give a an explination of the purpose of Specification class here. 4 | -------------------------------------------------------------------------------- /demo/loadable/applique/indexer.rb: -------------------------------------------------------------------------------- 1 | When 'Given a `(((.*?)))` file' do |file, text| 2 | File.open(file, 'w'){ |f| f << text } 3 | end 4 | 5 | -------------------------------------------------------------------------------- /demo/metadata/applique/indexer.rb: -------------------------------------------------------------------------------- 1 | When 'Given a `(((.*?)))` file' do |file, text| 2 | File.open(file, 'w'){ |f| f << text } 3 | end 4 | 5 | -------------------------------------------------------------------------------- /demo/applique/instance_text.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | 3 | When /(\@\w+)/ do |iv, txt| 4 | instance_variable_set(iv, YAML.load(txt)) 5 | end 6 | 7 | -------------------------------------------------------------------------------- /lib/indexer/core_ext.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/core_ext/hash/to_h' 2 | require 'indexer/core_ext/hash/rekey' 3 | require 'indexer/core_ext/kernel/cli' 4 | 5 | -------------------------------------------------------------------------------- /bin/index: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #require 'indexer/cli/index' 3 | #Indexer::CLI::Index.execute 4 | 5 | require 'indexer' 6 | Indexer::Command.run 7 | 8 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/indexer 3 | code: http://github.com/rubyworks/indexer 4 | api: http://rubydoc.info/gems/indexer/frames 5 | 6 | -------------------------------------------------------------------------------- /work/deprecated/gemfile_generator/index-gemfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'indexer' 3 | require 'indexer/cli/gemfile' 4 | Indexer::CLI.gemfile(*ARGV) 5 | 6 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - name: trans 3 | email: transfire@gmail.com 4 | website: http://trans.gihub.com 5 | - name: postmodern 6 | website: http://postmodern.github.com 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | script: "bundle exec qed -Ilib demo/" 3 | rvm: 4 | - 1.8.7 5 | - 1.9.2 6 | - 1.9.3 7 | - rbx 8 | - rbx-19mode 9 | - jruby 10 | - jruby-19mode 11 | - ree 12 | 13 | -------------------------------------------------------------------------------- /lib/indexer/version/exceptions.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module Version 4 | 5 | # Base calss for all version errors. 6 | class Exception < RuntimeError 7 | end 8 | 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /demo/componenets/requirement/runtime.md: -------------------------------------------------------------------------------- 1 | ## Requirement#runtime? 2 | 3 | The `runtime?` method is the inverse of the `#development?` method. 4 | 5 | r = Requirement.new('foo', :development => true) 6 | 7 | r.refute.runtime? 8 | 9 | -------------------------------------------------------------------------------- /demo/version/number/19_match.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#match? 2 | 3 | The `match?` method 4 | 5 | v = Indexer::Version::Number[1,2,3] 6 | 7 | v.assert.match?('> 1.2') 8 | v.assert.match?('< 2.0') 9 | v.assert.match?('> 1.2', '< 2.0') 10 | 11 | -------------------------------------------------------------------------------- /work/deprecated/resources/spec/12_size.md: -------------------------------------------------------------------------------- 1 | ## Resources#size 2 | 3 | The `size` method 4 | 5 | r = V0::Resources.new( 6 | :home => 'http://foo.com', 7 | :work => 'http://foo.com/office' 8 | ) 9 | 10 | r.size.assert == 2 11 | 12 | -------------------------------------------------------------------------------- /etc/qed-simplecov.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start do 3 | coverage_dir 'log/coverage' 4 | add_group "Shared" do |src_file| 5 | /lib\/indexer\/v(\d+)(.*?)$/ !~ src_file.filename 6 | end 7 | add_group "Revision 0", "lib/indexer/v0" 8 | end 9 | 10 | -------------------------------------------------------------------------------- /work/deprecated/resources/spec/09_to_h.md: -------------------------------------------------------------------------------- 1 | ## Resources#to_h 2 | 3 | The `to_h` method 4 | 5 | r = V0::Resources.new( 6 | :home => 'http://foo.com' 7 | ) 8 | 9 | r.to_h.should == { 10 | 'home' => 'http://foo.com' 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /work/deprecated/resources/spec/10_to_hash.md: -------------------------------------------------------------------------------- 1 | ## Resources#to_hash 2 | 3 | The `to_hash` method 4 | 5 | r = V0::Resources.new( 6 | :home => 'http://foo.com' 7 | ) 8 | 9 | r.to_hash.should == { 10 | :home => 'http://foo.com' 11 | } 12 | 13 | -------------------------------------------------------------------------------- /demo/componenets/requirement/to_h.md: -------------------------------------------------------------------------------- 1 | ## Requirement#to_h 2 | 3 | The `to_h` method 4 | 5 | r = Requirement.new('foo') 6 | r.to_h.should == {'name' => 'foo'} 7 | 8 | r = Requirement.new('foo', :version => '~> 1.0.0') 9 | r.to_h.should == {'name' => 'foo', 'version' => '~> 1.0.0'} 10 | 11 | -------------------------------------------------------------------------------- /demo/componenets/requirement/optional.md: -------------------------------------------------------------------------------- 1 | ## Requirement#optional? 2 | 3 | The `optional?` method indicates if a requirement is not necessary. 4 | 5 | r = Requirement.new('foo', :optional => true) 6 | 7 | r.assert.optional? 8 | 9 | r.optional = false 10 | 11 | r.refute.optional? 12 | 13 | -------------------------------------------------------------------------------- /work/deprecated/resources/spec/99_method_missing.md: -------------------------------------------------------------------------------- 1 | ## Resources#method_missing 2 | 3 | The `method_missing` method allows arbitrary resources 4 | to be defined and read. 5 | 6 | r = V0::Resources.new 7 | 8 | r.vip = 'http://foo.com/viproom' 9 | 10 | r.vip.should == 'http://foo.com/viproom' 11 | -------------------------------------------------------------------------------- /demo/componenets/requirement/repository.md: -------------------------------------------------------------------------------- 1 | ## Requirement#repository 2 | 3 | The `repository` method 4 | 5 | r = Requirement.new('foo') 6 | 7 | r.repository = { 8 | :uri => 'http://wootworld.com/foo.git', 9 | :scm => 'git' 10 | } 11 | 12 | r.repository.assert.is_a?(Repository) 13 | 14 | -------------------------------------------------------------------------------- /demo/version/constraint/04_to_proc.md: -------------------------------------------------------------------------------- 1 | ## Version::Constraint#to_proc 2 | 3 | A version constraint object can be converter into a Proc object 4 | for making version comparisions. 5 | 6 | cv = Indexer::Version::Constraint['>', '1.0.0'] 7 | cp = cv.to_proc 8 | 9 | cp.call('1.1.1') #=> true 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/componenets/requirement/name.md: -------------------------------------------------------------------------------- 1 | ## Requirement#name 2 | 3 | The `name` method 4 | 5 | r = Requirement.parse('foo 1.0+') 6 | 7 | r.name.should == 'foo' 8 | 9 | While atypical, we can change the name of ther requirement with `#name=`. 10 | 11 | r.name = 'bar' 12 | 13 | r.name.should == 'bar' 14 | 15 | -------------------------------------------------------------------------------- /lib/indexer/conversion.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Conversion module provides routines for converting 4 | # other metadata sources to and from index format. 5 | # 6 | module Conversion 7 | end 8 | 9 | end 10 | 11 | require_relative 'conversion/gemspec' 12 | require_relative 'conversion/gemspec_exporter' 13 | require_relative 'conversion/gemfile' 14 | 15 | -------------------------------------------------------------------------------- /demo/version/number/20_to_str.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#to_str 2 | 3 | The Version::Number class can be thought of as a String. 4 | 5 | check do |v, s| 6 | v = Indexer::Version::Number.parse(v) 7 | v.to_str.assert == s 8 | end 9 | 10 | ok [1,2,3], '1.2.3' 11 | ok [1,2,3,'rc',1], '1.2.3.rc.1' 12 | ok [1,2,3,'alpha'], '1.2.3.alpha' 13 | 14 | -------------------------------------------------------------------------------- /lib/indexer/components.rb: -------------------------------------------------------------------------------- 1 | require_relative 'components/author' 2 | require_relative 'components/organization' 3 | require_relative 'components/copyright' 4 | require_relative 'components/conflict' 5 | require_relative 'components/resource' 6 | require_relative 'components/repository' 7 | require_relative 'components/requirement' 8 | require_relative 'components/engine' 9 | 10 | -------------------------------------------------------------------------------- /demo/loadable/32_find.md: -------------------------------------------------------------------------------- 1 | ## Metadata.find 2 | 3 | Given a `.index` file: 4 | 5 | --- 6 | name: foo 7 | version: 1.0.0 8 | 9 | Then `Metadata.find` will ascend upward in the directory heireachy looking for a 10 | `.index` file. 11 | 12 | file = Indexer::Metadata.find 13 | 14 | And we can verify it was found. 15 | 16 | File.basename(file).assert == '.index' 17 | 18 | -------------------------------------------------------------------------------- /demo/validator/01_introduction.md: -------------------------------------------------------------------------------- 1 | # Validator 2 | 3 | The Validator class models the strict, or _canonical_, specification of the `.index` 4 | file. It is used internally to validate the a `.index` file as it is loaded into 5 | the Specification class. 6 | 7 | NOTE: This set of tests includes the `Indexer::V0` module into the testing 8 | namespace to simplify testing to revision 0 of Indexer. 9 | -------------------------------------------------------------------------------- /demo/validator/10_authors.md: -------------------------------------------------------------------------------- 1 | # Validator#authors 2 | 3 | The `authors` field holds a list of orginating authors. 4 | 5 | data = Indexer::Validator.new 6 | 7 | data.authors = [ 8 | { 'name' => 'Bob Sawyer', 9 | 'email' => 'bob@mail.com' 10 | }, 11 | { 'name' => 'John Delight', 12 | 'email' => 'dlite@mail.com' 13 | } 14 | ] 15 | 16 | -------------------------------------------------------------------------------- /demo/version/number/11_release_candidate.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#release_candidate? 2 | 3 | The `release_candidate?` method returns `true` if the build 4 | is `rc`, otherwise `false`. 5 | 6 | check do |v| 7 | v = Indexer::Version::Number.parse(v) 8 | v.release_candidate? 9 | end 10 | 11 | ok [1,2,3,'rc',1] 12 | no [1,2,3] 13 | no [1,2,3,'alpha'] 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/validator/08_description.md: -------------------------------------------------------------------------------- 1 | ## Validator#description 2 | 3 | The `description` field is used to describe a project in detail. 4 | It SHOULD be a single paragraph. 5 | 6 | data = Indexer::Validator.new 7 | 8 | data.description = "HelloWorld is the best way to say hello.\nJust say it!" 9 | 10 | While description has no specific size limit, it SHOULD be less than 1,000 11 | characters. 12 | 13 | -------------------------------------------------------------------------------- /demo/validator/09_suite.md: -------------------------------------------------------------------------------- 1 | ## Validator#suite 2 | 3 | The `suite` field is used to identify the _suite_ of packages that a package 4 | belongs. For example Microsoft "Word" is part of the Microsoft "Office" suite. 5 | 6 | The `suite` field MUST be a string with only a single line of text. 7 | 8 | ok "Office" 9 | ok "RDoc" 10 | 11 | no 100 12 | no :symbol 13 | no "Foo\nBar" 14 | 15 | -------------------------------------------------------------------------------- /demo/metadata/09_suite.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#suite 2 | 3 | The `suite` field is used to identify the _suite_ of packages that a package 4 | belongs. For example Microsoft "Word" is part of the Microsoft "Office" suite. 5 | 6 | The `suite` field MUST be a string with only a single line of text. 7 | 8 | ok "Office" 9 | ok "RDoc" 10 | 11 | no 100 12 | no :symbol 13 | no "Foo\nBar" 14 | 15 | -------------------------------------------------------------------------------- /demo/version/constraint/09_to_gem_version.md: -------------------------------------------------------------------------------- 1 | ## Version#to_gem_version 2 | 3 | Since RubyGems supporst a more limited version constrain format, the 4 | Constraint class provides the `#to_gem_version` method to produce 5 | a string representation of the constraint that RubyGems can understand. 6 | 7 | version = Indexer::Version::Constraint.parse('1.2~') 8 | 9 | version.to_gem_version.should == "~> 1.2" 10 | 11 | -------------------------------------------------------------------------------- /demo/componenets/requirement/development.md: -------------------------------------------------------------------------------- 1 | ## Requirement#runtime? 2 | 3 | The `#development?` method indicates whether a requirement is for 4 | developing a project, rather than using it. 5 | 6 | r = Requirement.new('foo', :development => true) 7 | 8 | r.assert.development? 9 | 10 | Note that if a requirement is used for both runtime and development, 11 | it only need be listed as a runtime requirement. 12 | 13 | -------------------------------------------------------------------------------- /demo/componenets/requirement/parse.md: -------------------------------------------------------------------------------- 1 | ## Requirement#parse 2 | 3 | The `parse` method 4 | 5 | r = Requirement.parse('foo 1.0+') 6 | 7 | expect Indexer::ValidationError do 8 | Requirement.parse('---') 9 | end 10 | 11 | expect Indexer::ValidationError do 12 | Requirement.parse(1) 13 | end 14 | 15 | expect Indexer::ValidationError do 16 | Requirement.parse([1]) 17 | end 18 | 19 | -------------------------------------------------------------------------------- /demo/validator/12_resources.md: -------------------------------------------------------------------------------- 1 | ## Validator#resources 2 | 3 | The `resources` field holds a list of URLs index by type. 4 | 5 | data = Indexer::Validator.new 6 | 7 | data.resources = [{ 8 | 'home' => 'http://foo.org', 9 | 'docs' => 'http://foo.org/api' 10 | }] 11 | 12 | The `resource` field MUST be a Array. 13 | 14 | no 100 15 | no :symbol 16 | no "string" 17 | no Object.new 18 | 19 | -------------------------------------------------------------------------------- /demo/metadata/26_namespace.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#namespace 2 | 3 | The namespace field is toplevel module or class name of the project's API. 4 | For example, if the project uses `module Foo` to encapsulate it's 5 | functionality, than the namespace would be "Foo". 6 | 7 | spec = Indexer::Metadata.new 8 | 9 | spec.namespace = "Foo" 10 | 11 | The namespec must be a vaild constant name, and it may contain `::`. 12 | 13 | -------------------------------------------------------------------------------- /demo/metadata/applique/field_assignment.rb: -------------------------------------------------------------------------------- 1 | # Setup an AE ok-check for testing field assignment. 2 | # 3 | # @todo best match for this? 4 | When '`(((\w+)))` field (((is|holds)))' do |name, _| 5 | check "#{name} setting invalid" do |value| 6 | data = Indexer::Metadata.new 7 | begin 8 | data.send("#{name}=", value) 9 | true 10 | rescue Indexer::ValidationError 11 | false 12 | end 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /demo/validator/applique/field_assignment.rb: -------------------------------------------------------------------------------- 1 | # Setup an AE ok-check for testing field assignment. 2 | # 3 | # @todo best match for this? 4 | When '`(((\w+)))` field (((is|holds)))' do |name, _| 5 | check "#{name} setting invalid" do |value| 6 | data = Indexer::Validator.new 7 | begin 8 | data.send("#{name}=", value) 9 | true 10 | rescue Indexer::ValidationError 11 | false 12 | end 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /demo/loadable/30_read.md: -------------------------------------------------------------------------------- 1 | ## Metadata.read 2 | 3 | Given a `.index` file: 4 | 5 | --- 6 | name: foo 7 | version: 1.0.0 8 | 9 | Then `Metadata.read('.index')` will read in a file, parse and 10 | validate it and return a new Metadata object. 11 | 12 | metadata = Indexer::Metadata.read('.index') 13 | 14 | And we can verify it was read. 15 | 16 | metadata.assert.name == 'foo' 17 | metadata.assert.version.to_s == '1.0.0' 18 | 19 | -------------------------------------------------------------------------------- /demo/version/number/02_initialize.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#initialize 2 | 3 | A version number is composed of segments. 4 | 5 | v = Indexer::Version::Number.new(1,2,3,'alpha',4) 6 | 7 | There a few alternative initializers. The most consice is []. 8 | 9 | v = Indexer::Version::Number[1,2,3,'beta',4] 10 | 11 | There is also `parse` which takes a single string argument. 12 | 13 | v = Indexer::Version::Number.parse('1.2.3rc1') 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/version/number/10_prerelease.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#prerelease? 2 | 3 | The `prerelease?` method returns `true` if the build is `pre`, 4 | otherwise `false`. 5 | 6 | v = Indexer::Version::Number[1,2,3,'pre',4] 7 | 8 | v.assert.prerelease? 9 | 10 | 11 | v = Indexer::Version::Number[1,2,3] 12 | 13 | v.refute.prerelease? 14 | 15 | 16 | v = Indexer::Version::Number[1,2,3,'alpha'] 17 | 18 | v.refute.prerelease? 19 | 20 | -------------------------------------------------------------------------------- /demo/validator/13_repositories.md: -------------------------------------------------------------------------------- 1 | ## Validator#repositories 2 | 3 | The `repositories` field holds a list of repository URLs indexed by an id. 4 | 5 | data = Indexer::Validator.new 6 | 7 | data.repositories = [ 8 | { 9 | 'id' => 'main', 10 | 'uri' => 'https://github.com/foostuff/foo.git' 11 | } 12 | ] 13 | 14 | The field MUST be a Hash. 15 | 16 | no 100 17 | no :symbol 18 | no "string" 19 | no Object.new 20 | 21 | -------------------------------------------------------------------------------- /demo/importer/ruby.md: -------------------------------------------------------------------------------- 1 | # Ruby 2 | 3 | Importer can create an index via method_missing. 4 | 5 | importer = Indexer::Importer.new 6 | 7 | importer.name 'example' 8 | 9 | importer.authors 'trans ', 'postmodern' 10 | 11 | So then 12 | 13 | metadata = importer.metadata 14 | 15 | metadata.name.assert == 'example' 16 | 17 | metadata.authors.size == 2 18 | 19 | The Ruby-based importer module uses this fact when loading Ruby-based 20 | sources. 21 | 22 | -------------------------------------------------------------------------------- /demo/version/number/05_build.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#build 2 | 3 | The `build` method ... 4 | 5 | v = Indexer::Version::Number[1,2,3] 6 | 7 | v.build.should == nil 8 | 9 | 10 | v = Indexer::Version::Number[1,2,3,'pre'] 11 | 12 | v.build.should == 'pre' 13 | 14 | 15 | v = Indexer::Version::Number[1,2,3,'pre',2] 16 | 17 | v.build.should == 'pre.2' 18 | 19 | 20 | v = Indexer::Version::Number[1,2,3,'20101020'] 21 | 22 | v.build.should == '20101020' 23 | 24 | -------------------------------------------------------------------------------- /demo/validator/05_codename.md: -------------------------------------------------------------------------------- 1 | ## Validator#codename 2 | 3 | The `codename` field is used to name the specific version. 4 | 5 | data = Indexer::Validator.new 6 | data.codename = "Lazy Louse" 7 | 8 | The `codename` value MUST have only one lone of text. 9 | 10 | expect Indexer::ValidationError do 11 | data.codename = "foo\nbar" 12 | end 13 | 14 | TODO: Should a codename have a size limit? 15 | 16 | TODO: Should a codenmae not allow puncutation marks on the first character? 17 | 18 | -------------------------------------------------------------------------------- /demo/version/number/03_parse.md: -------------------------------------------------------------------------------- 1 | ## Version::Number.parse 2 | 3 | The `parse` method ... 4 | 5 | A String 6 | 7 | v = Indexer::Version::Number.parse('1.2.3') 8 | 9 | v.to_s.assert == '1.2.3' 10 | 11 | An Array 12 | 13 | v = Indexer::Version::Number.parse([1,2,3]) 14 | 15 | v.to_s.assert == '1.2.3' 16 | 17 | Another Version::Number object 18 | 19 | v1 = Indexer::Version::Number.parse('2.5.6') 20 | v2 = Indexer::Version::Number.parse(v1) 21 | 22 | v2.to_s.assert == '2.5.6' 23 | 24 | -------------------------------------------------------------------------------- /demo/validator/06_title.md: -------------------------------------------------------------------------------- 1 | ## Validator#title 2 | 3 | The `title` field is used in lue of the `name` for documentation 4 | purposes and the like. 5 | 6 | data = Indexer::Validator.new 7 | 8 | data.title = "Tom's Hello World Program" 9 | 10 | The `title` value MUST have only one lione of text. 11 | 12 | expect Indexer::ValidationError do 13 | data.title = "Foo\nBar" 14 | end 15 | 16 | TODO: Should it have a size limit? 17 | 18 | TODO: Should it not allow punctuation marks as the first character? 19 | 20 | -------------------------------------------------------------------------------- /demo/validator/07_summary.md: -------------------------------------------------------------------------------- 1 | ## Validator#summary 2 | 3 | The `summary` field is used to breifly describe a project. 4 | 5 | data = Indexer::Validator.new 6 | 7 | data.summary = "Convenient Way to Say Hello" 8 | 9 | The `summary` value MUST have only one line of text. 10 | 11 | expect Indexer::ValidationError do 12 | data.summary = "not\none\nline" 13 | end 14 | 15 | TODO: Should a summary have a size limit? 16 | 17 | TODO: Should a summary not allow puncutation marks on the first character? 18 | 19 | -------------------------------------------------------------------------------- /work/deprecated/cli/show.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/cli' 2 | 3 | module Indexer 4 | 5 | # 6 | class CLI::Show < CLI 7 | 8 | # 9 | def parse(opts) 10 | opts.banner = "Usage: #{$0} [options] [part ...]" 11 | end 12 | 13 | # 14 | def run 15 | Metadata.ensure_locked 16 | 17 | if Metadata.exists? 18 | metadata = Metadata.open 19 | puts metadata.about(*argv) 20 | else 21 | raise Error.exception(".index file not found", IOError) 22 | end 23 | end 24 | 25 | end 26 | 27 | end 28 | 29 | -------------------------------------------------------------------------------- /demo/version/number/09_stable_release.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#stable? 2 | 3 | The `stable?` method returns `true` if the build is `nil`, 4 | otherwise `false`. 5 | 6 | v = Indexer::Version::Number[1,2,3] 7 | 8 | v.assert.stable? 9 | 10 | v = Indexer::Version::Number[1,2,3,'pre',4] 11 | 12 | v.refute.stable? 13 | 14 | 15 | ## Version::Number#stable_release? 16 | 17 | v = Indexer::Version::Number[1,2,3] 18 | 19 | v.assert.stable_release? 20 | 21 | v = Indexer::Version::Number[1,2,3,'pre',4] 22 | 23 | v.refute.stable_release? 24 | -------------------------------------------------------------------------------- /work/reference/example.gemfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | metadata = YAML.load_file('.ruby') 6 | 7 | requirements = metadata['requirements'] || [] 8 | 9 | cc = lambda |ver| 10 | case ver.strip 11 | when /+$/ 12 | ">= #{$`}" 13 | case /-$/ 14 | "< #{$`}" 15 | case /~$/ 16 | "~> #{$`}" 17 | else 18 | ver 19 | end 20 | end 21 | 22 | requirements.each do |r| 23 | name = r['name'] 24 | vers = r['version'] 25 | grps = r['groups'] 26 | 27 | gem name, *vers.map{ |v| cc[v] }, :groups => grps 28 | end 29 | 30 | -------------------------------------------------------------------------------- /demo/validator/17_alternatives.md: -------------------------------------------------------------------------------- 1 | ## Validator#alternatives 2 | 3 | The `alternatives` field holds a list of other packages with which this 4 | package can act as a substitue and vice-versa. It might also represent 5 | the natural successor of a given package. 6 | 7 | The format of the field is an array. 8 | 9 | data = Indexer::Validator.new 10 | 11 | data.alternatives = ['BlueCloth','rdiscount'] 12 | 13 | The `alternatives` field can only by assigned an array. 14 | 15 | no 100 16 | no :symbol 17 | no "string" 18 | no Object.new 19 | 20 | -------------------------------------------------------------------------------- /demo/validator/24_engines.md: -------------------------------------------------------------------------------- 1 | # Validator#engines 2 | 3 | The `engines` field holds the RUBY_ENGINE and and RUBY_VERSION requirements for 4 | the project/package. If given, only matching Rubies can make use of it. 5 | 6 | The `engines` field MUST be an array of strings with only a single line of text 7 | in the format of `" []"`. 8 | 9 | data = Indexer::Validator.new 10 | 11 | data.engines = ['mri 1.8.7+', 'jruby'] 12 | 13 | It can't be anything else. 14 | 15 | no "Foo\nBar" 16 | no 100 17 | no :symbol 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/metadata/08_description.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#description 2 | 3 | The `description` field is used to describe a project in detail. 4 | It SHOULD be a single paragraph. 5 | 6 | spec = Indexer::Metadata.new 7 | 8 | spec.description = "HelloWorld is the best way to say hello.\nJust say it!" 9 | 10 | While description has no specific size limit, it SHOULD be less than 1,000 11 | characters. 12 | 13 | The Metadata class allows any object that responds to #to_s to be assigned to 14 | the `description` field. 15 | 16 | spec.description = :even_a_symbol 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/componenets/author/09_to_h.md: -------------------------------------------------------------------------------- 1 | ## Author#to_h 2 | 3 | Given a valid author instance. 4 | 5 | author = Author.new( 6 | :name => 'Thomas T. Thomas', 7 | :email => 'tom@mail.com', 8 | :website => 'http://tom.com', 9 | :role => 'development' 10 | ) 11 | 12 | The author can be converted to canonical form using #to_h. 13 | 14 | h = author.to_h 15 | 16 | h['name'].should == 'Thomas T. Thomas' 17 | h['email'].should == 'tom@mail.com' 18 | h['website'].should == 'http://tom.com' 19 | h['roles'].should == ['development'] 20 | 21 | 22 | -------------------------------------------------------------------------------- /demo/version/number/04_crush.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#crush? 2 | 3 | A version can be represented as a string in one of two ways --either 4 | as a strict point seprated tuple or *crushed* where string segements 5 | are merged with the following numerical segement. 6 | 7 | The Version::Number class will automatically reqcognize a *crushed* 8 | version if a segment of the version tuple is provided as such. 9 | 10 | v = Indexer::Version::Number.new(1,2,3,'alpha4') 11 | 12 | v.assert.crush? 13 | 14 | The output result of a crushed version. 15 | 16 | v.to_s.assert == '1.2.3alpha4' 17 | 18 | -------------------------------------------------------------------------------- /demo/applique/ae.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | require 'ae/should' 3 | 4 | # 5 | # Copy and paste for now, b/c lib is not mature. 6 | # 7 | 8 | #require 'ae//check' 9 | 10 | def check(msg=nil, &block) 11 | if block.arity == 0 12 | @__c__ = nil 13 | assert(block.call, msg) 14 | else 15 | @__c__ = [block, msg] 16 | end 17 | end 18 | 19 | def ok(*args) 20 | block, message = *@__c__ 21 | result = block.call(*args) 22 | assert(result, message) 23 | end 24 | 25 | def no(*args) 26 | block, message = *@__c__ 27 | result = block.call(*args) 28 | refute(result, message) 29 | end 30 | 31 | -------------------------------------------------------------------------------- /demo/valid/08_version_string.md: -------------------------------------------------------------------------------- 1 | ## Valid.version_string? / version_string! 2 | 3 | ### version_string? 4 | 5 | check do |str| 6 | Indexer::Valid.version_string?(str) 7 | end 8 | 9 | ok '1.0.0' 10 | ok '1.0.rc.2' 11 | ok '1.0.x.3' 12 | 13 | no 'a.b.c' 14 | no '&' 15 | no 'A.0.0' 16 | 17 | ### version_string! 18 | 19 | check do |str| 20 | ! Indexer::ValidationError.raised? do 21 | Indexer::Valid.version_string!(str) 22 | end 23 | end 24 | 25 | ok '1.0.0' 26 | ok '1.0.rc.2' 27 | ok '1.0.x.3' 28 | 29 | no 'a.b.c' 30 | no '&' 31 | no 'A.0.0' 32 | 33 | -------------------------------------------------------------------------------- /demo/validator/19_conflicts.md: -------------------------------------------------------------------------------- 1 | ## Validator#conflicts 2 | 3 | The `conflicts` field holds a list of packages with which the project is 4 | known not to be compatible. 5 | 6 | The format of the field is a mapping to package names indexing version 7 | constraints. 8 | 9 | data = Indexer::Validator.new 10 | 11 | data.conflicts = [ 12 | { 'name' => 'bad_robot', 'version' => ['>=1.0'] }, 13 | { 'name' => 'evil', 'version' => ['0+'] } 14 | ] 15 | 16 | The `conflicts` field can only by assigned such a Hash. 17 | 18 | no 100 19 | no :symbol 20 | no Object.new 21 | 22 | -------------------------------------------------------------------------------- /data/indexer/yaml.txt: -------------------------------------------------------------------------------- 1 | --- 2 | name: <%= name %> 3 | version: <%= version %> 4 | title: <%= title %> 5 | summary: <%= summary %> 6 | 7 | description: 8 | <%= description %> 9 | 10 | authors: 11 | - you 12 | 13 | requirements: 14 | - dependency 1.0~ 15 | - rake (build) 16 | - test (test) 17 | 18 | repositories: 19 | upstream: http://github.com/organization/app_name/app_name.git 20 | 21 | resources: 22 | home: http://organization.github.org/app_name 23 | code: http://github.com/organization/app_name 24 | 25 | categories: 26 | - foo 27 | 28 | copyrights: 29 | - 2012 Your Name (MIT) 30 | 31 | -------------------------------------------------------------------------------- /demo/validator/22_organizations.md: -------------------------------------------------------------------------------- 1 | ## Validator#organizations 2 | 3 | The `organizations` field holds a list of the company, club, foundation, 4 | or programming groups involved with a project. For users of GitHub this 5 | will most likey be the GitHub organization in which a project is hosted. 6 | 7 | data = Indexer::Validator.new 8 | 9 | data.organizations = [ 10 | { 'name' => 'Bobs Compnay', 11 | 'email' => 'bobinc@mail.com', 12 | 'roles' => ['sponsers'] 13 | }, 14 | { 'name' => 'Johns Programming Club', 15 | 'email' => 'dlite@mail.com' 16 | } 17 | ] 18 | 19 | 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | begin 4 | require 'yard' 5 | YARD::Rake::YardocTask.new 6 | rescue LoadError 7 | task :yard do 8 | abort "Could not require 'yard'" 9 | end 10 | end 11 | 12 | desc "run tests (needs qed)" 13 | task :test do 14 | sh "qed -Ilib demo/" 15 | end 16 | 17 | desc "coverage report" 18 | task :cov do 19 | sh "qed -r ./config/qed-simplecov -Ilib" 20 | end 21 | 22 | desc "build gem" 23 | task :gem do 24 | sh "gem build pkg/indexer.gemspec" 25 | end 26 | 27 | desc "release gem" 28 | task :release => [:gem] do 29 | gems = Dir['*.gem'].sort 30 | sh "gem push #{gems.last}" 31 | end 32 | 33 | -------------------------------------------------------------------------------- /lib/indexer/importer/gemfile.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # Build mixin for Bundler's Gemfile. 6 | # 7 | module GemfileImportation 8 | # 9 | # If the source file is a Gemfile, import it. 10 | # 11 | def import(source) 12 | case source 13 | when 'Gemfile' 14 | metadata.import_gemfile(source) 15 | true 16 | else 17 | super(source) if defined?(super) 18 | end 19 | end 20 | end 21 | 22 | # Include GemfileImportation mixin into Builder class. 23 | include GemfileImportation 24 | 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /lib/indexer/error.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Tag module for Metaspec Exceptions. 4 | # 5 | # Use this module to extend arbitrary errors raised by Metaspec, 6 | # so they can be easily identified as Metaspec errors if need be. 7 | module Error 8 | # Just catch the error and raise this instead. 9 | def self.exception(msg=nil,orig=$!) 10 | if Class === orig 11 | orig = orig.new(msg) 12 | elsif orig.nil? 13 | orig = StandardError.new(msg) 14 | else 15 | orig = orig.exception(msg) if msg 16 | end 17 | orig.extend self 18 | orig 19 | end 20 | end 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /demo/componenets/author/02_initialize.md: -------------------------------------------------------------------------------- 1 | ## Author#initialize 2 | 3 | The constructor takes a hash of entries. Recognized entries 4 | are `name`, `email`, `website` and `role`. 5 | 6 | author = Author.new( 7 | :name => 'Thomas T. Thomas', 8 | :email => 'tom@mail.com', 9 | :website => 'http://tom.com', 10 | :role => 'development' 11 | ) 12 | 13 | We should be able to query the author object for the various settings. 14 | 15 | author.name #=> 'Thomas T. Thomas' 16 | author.email #=> 'tom@mail.com' 17 | author.website #=> 'http://tom.com' 18 | author.roles #=> ['development'] 19 | 20 | -------------------------------------------------------------------------------- /demo/validator/21_created.md: -------------------------------------------------------------------------------- 1 | ## Validator#created 2 | 3 | The `created` field is the date the project was started. This field 4 | must be a string that comforms to the ISO UTC timstamp standard. 5 | The format of which is "YYYY-MM-DD HH:MM:SS". The time portion is 6 | optional. 7 | 8 | ok "2011-10-20" 9 | ok "2011-10-01 09:42:11" 10 | ok "2011-10-01 14:42:11" 11 | 12 | Invalid dates and times will be rejected. 13 | 14 | no "2011-50-01 09:42:11" 15 | no "2011-10-01 99:42:11" 16 | 17 | As will anything other than a string. 18 | 19 | no 100 20 | no :symbol 21 | no Object.new 22 | no Date.new 23 | no Time.new 24 | 25 | -------------------------------------------------------------------------------- /work/deprecated/resources/spec/11_each.md: -------------------------------------------------------------------------------- 1 | ## Resources#each 2 | 3 | The `to_hash` method 4 | 5 | r = V0::Resources.new( 6 | :home => 'http://foo.com', 7 | :work => 'http://foo.com/office' 8 | ) 9 | 10 | a = [] 11 | 12 | r.each{ |k,v| a << [k,v] } 13 | 14 | a.assert.include? [:home, 'http://foo.com'] 15 | a.assert.include? [:work, 'http://foo.com/office'] 16 | a.size.assert == 2 17 | 18 | Resources is Enumerable, so methods like #map also work. 19 | 20 | a = r.map{ |k,v| [k,v] } 21 | 22 | a.assert.include? [:home, 'http://foo.com'] 23 | a.assert.include? [:work, 'http://foo.com/office'] 24 | a.size.assert == 2 25 | 26 | -------------------------------------------------------------------------------- /demo/metadata/05_codename.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#codename 2 | 3 | The `codename` field is used to name the specific version. 4 | 5 | spec = Indexer::Metadata.new 6 | spec.codename = "Lazy Louse" 7 | 8 | The `codename` value MUST have only one lone of text. 9 | 10 | expect Indexer::ValidationError do 11 | spec.codename = "foo\nbar" 12 | end 13 | 14 | The Indexer::Metadata allows any object that responds to #to_s to be assigned. 15 | 16 | spec.codename = :LazyLouse 17 | spec.codename.assert == 'LazyLouse' 18 | 19 | TODO: Should a codename have a size limit? 20 | 21 | TODO: Should a codename not allow puncutation marks on the first character? 22 | 23 | -------------------------------------------------------------------------------- /demo/validator/11_paths.md: -------------------------------------------------------------------------------- 1 | ## Validator#paths 2 | 3 | The `paths` field is a means by which a project's paths can be 4 | designated. This example a Ruby developer can use it to specify 5 | the locations within the project that Ruby's load system should 6 | look for files when `#require` or `#load` are used. 7 | 8 | The `paths` value MUST be a Hash of String mapped to an Array of String. 9 | 10 | ok 'lib' => ['lib', 'ext'] 11 | 12 | no 100 13 | no :symbol 14 | no "string" 15 | no Object.new 16 | no ['lib'] 17 | 18 | By default the value is `{'lib' => ['lib'] }`. 19 | 20 | data = Indexer::Validator.new 21 | data.paths.should = { 'lib' => ['lib'] } 22 | 23 | -------------------------------------------------------------------------------- /lib/indexer/components/dependency.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Dependency class is essentially the same as {Requirement}, but 4 | # a dependency represents a binary package requirement that would 5 | # need to be installed using an operating systems own package 6 | # management system, or installed manually. 7 | # 8 | # Dependency is a sublcass of {Requirement}. It only exists as a separate 9 | # class becuase an OS package managers MIGHT require some additional 10 | # information --but as of yet that's not the case. 11 | # 12 | # TODO: Why not merge the two and add a field to distinguish them? 13 | # 14 | class Dependency < Requirement 15 | end 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /demo/validator/25_platform.md: -------------------------------------------------------------------------------- 1 | # Validator#platform 2 | 3 | The `platforms` field holds the RUBY_PLATFORM requirements for the project/package. 4 | If given, only matching platforms can make use of it. 5 | 6 | The `platforms` field MUST be an array of strings with only a single line of text 7 | that match valid value of RUBY_PLATFORM. 8 | 9 | data = Indexer::Validator.new 10 | 11 | data.platforms = ['x86_64-linux'] 12 | 13 | TODO: Should we do this? 14 | To ease matching to platforms a '?' and '*' can be used as a wildcard. 15 | 16 | data.platforms = ['win*'] 17 | 18 | The field can't be assigned anything else. 19 | 20 | no "Foo\nBar" 21 | no 100 22 | no :symbol 23 | 24 | -------------------------------------------------------------------------------- /demo/validator/14_date.md: -------------------------------------------------------------------------------- 1 | ## Validator#date 2 | 3 | The `date` field is the date the .ruby file was last edited, or if 4 | part of a package when the package was built. This field 5 | must be a string that comforms to the ISO UTC timstamp standard. 6 | The format of which is "YYYY-MM-DD HH:MM:SS". The time portion is 7 | optional. 8 | 9 | ok "2011-10-20" 10 | ok "2011-10-01 09:42:11" 11 | ok "2011-10-01 14:42:11" 12 | 13 | Invalid dates and times will be rejected. 14 | 15 | no "2011-50-01 09:42:11" 16 | no "2011-10-01 99:42:11" 17 | 18 | As will anything other than a string. 19 | 20 | no 100 21 | no :symbol 22 | no Object.new 23 | no Date.new 24 | no Time.new 25 | 26 | -------------------------------------------------------------------------------- /demo/metadata/07_summary.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#summary 2 | 3 | The `summary` field is used to breifly describe a project. 4 | 5 | spec = Indexer::Metadata.new 6 | 7 | spec.summary = "Convenient Way to Say Hello" 8 | 9 | The `summary` value will have all newline and multiple space characters 10 | removed. 11 | 12 | spec.summary = "not\none\t line" 13 | spec.summary.assert == "not one line" 14 | 15 | The summary can be assigned only object's that responds to #to_str. 16 | 17 | expect Indexer::ValidationError do 18 | spec.summary = :even_a_symbol 19 | end 20 | 21 | TODO: Should a summary have a size limit? 22 | 23 | TODO: Should a summary not allow puncutation marks on the first character? 24 | 25 | -------------------------------------------------------------------------------- /demo/loadable/31_load.md: -------------------------------------------------------------------------------- 1 | ## Metadata.load 2 | 3 | Given a `metadata.yml` file: 4 | 5 | --- 6 | name: foo 7 | version: 1.0.0 8 | copyright: 2010 T. J. Hooker 9 | require_paths: ['lib', 'vendor/ansi/lib'] 10 | 11 | Then `Metadata.load('metadata.yaml')` will read in a file, parse it and 12 | return a new Metadata object. 13 | 14 | metadata = Indexer::Metadata.load(File.new('metadata.yml')) 15 | 16 | And we can verify it was read. 17 | 18 | metadata.name.should == 'foo' 19 | metadata.version.should.to_s == '1.0.0' 20 | 21 | metadata.copyrights.first.year == '2010' 22 | metadata.copyrights.first.holder == 'T. J. Hooker' 23 | 24 | metadata.require_paths.should == ['lib', 'vendor/ansi/lib'] 25 | 26 | -------------------------------------------------------------------------------- /data/indexer/ruby.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | name <%= name.inspect %> 4 | version <%= version.inspect %> 5 | title <%= title.inspect %> 6 | summary <%= summary.inspect %> 7 | 8 | description \ 9 | <% description.inspect %> 10 | 11 | authors [ 12 | 'you ' 13 | ] 14 | 15 | requirements [ 16 | 'foo 1.0~', 17 | 'rake (build)', 18 | 'test (test)' 19 | ] 20 | 21 | repositories { 22 | 'upstream' => 'http://github.com/organization/app_name/<%= name %>.git' 23 | } 24 | 25 | resources { 26 | 'home' => 'http://organization.github.org/<%= name %>', 27 | 'code' => 'http://github.com/organization/<%= name %>' 28 | } 29 | 30 | categories ['foo'] 31 | 32 | copyrights: [ 33 | '2012 Your Name (MIT)' 34 | ] 35 | 36 | -------------------------------------------------------------------------------- /demo/validator/20_install_message.md: -------------------------------------------------------------------------------- 1 | ## Validator#install_message 2 | 3 | The `install_message` field holds the post install message, 4 | which is displayed after a package is installed to provide users 5 | with important information about their newly installed application 6 | or library. 7 | 8 | The `install_message` fields SHOULD not be used to convey unimportant 9 | and trival statements. You do not need to thank people for installing 10 | your software in the install message! 11 | 12 | The `install_message` field can only contain a string. 13 | 14 | no 100 15 | no :symbol 16 | no Object.new 17 | 18 | Other than that the string can contain nay text desired, but the string 19 | SHOULD be less that 1,000 characters. 20 | 21 | -------------------------------------------------------------------------------- /work/deprecated/cli/source.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/cli' 2 | 3 | module Indexer 4 | 5 | # 6 | # 7 | class CLI::Source < CLI 8 | 9 | # 10 | attr_accessor :stdout 11 | 12 | # 13 | def initialize 14 | @stdout = false 15 | end 16 | 17 | # 18 | def parse(opts) 19 | opts.banner = "Usage: #{$0} [options]" 20 | opts.on('-o', '--stdout', 'output to stdout instead of file') do 21 | @stdout = true 22 | end 23 | end 24 | 25 | # 26 | def run 27 | metadata = Indexer.import(*argv) 28 | if stdout 29 | puts metadata.to_yaml 30 | else 31 | metadata.save! # TODO: Make sure only save when different. 32 | end 33 | end 34 | 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /demo/metadata/24_engines.md: -------------------------------------------------------------------------------- 1 | # Indexer::Metadata#engines 2 | 3 | The `engines` field holds the RUBY_ENGINE and and RUBY_VERSION requirements for 4 | the project/package. If given, only matching Rubies can make use of it. 5 | 6 | The `engines` field MUST be an array of strings with only a single line of text 7 | in the format of `" []"`. 8 | 9 | spec = Indexer::Metadata.new 10 | 11 | spec.engines = ['mri 1.8.7+', 'jruby'] 12 | 13 | It can't be anything else. 14 | 15 | no "Foo\nBar" 16 | no 100 17 | no :symbol 18 | 19 | Metadata class also support the singular alias. 20 | 21 | spec.engine = ['mri 1.8.7+', 'jruby'] 22 | 23 | Which additionally allows for a single platform assignment. 24 | 25 | spec.engine = 'mri 1.8.7+' 26 | 27 | -------------------------------------------------------------------------------- /demo/validator/15_requirements.md: -------------------------------------------------------------------------------- 1 | ## Validator#requirements 2 | 3 | The `requirements` field is a list of packages which are required 4 | to use this application/library. 5 | 6 | The field value is an Array of Hashes. The format of the `requirements` 7 | fields is: 8 | 9 | requirements: 10 | - name: foo 11 | version: 1.0+ 12 | development: false 13 | group: 14 | - doc 15 | repo: 16 | type: git 17 | url: http://foo.com/foo.git 18 | - name: bar 19 | version: ~> 2.1 20 | : 21 | : 22 | 23 | Only `name` and `version` are required sub-fields. 24 | 25 | data = Indexer::Validator.new 26 | 27 | data.requirements = [ 28 | {'name'=>'foo', 'version'=>'1.0+'}, 29 | {'name'=>'foo', 'version'=>'1.0+', 'development'=>true, 'group'=>['doc']} 30 | ] 31 | 32 | -------------------------------------------------------------------------------- /demo/error.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Error 2 | 3 | The `Indexer::Error` module is a tag error. It can be 4 | raised like any other error, but if another error has been 5 | raised, i.e. if `$!` is set, then it will raise that error 6 | extended by `Error` module. 7 | 8 | begin 9 | fooballs 10 | rescue NameError 11 | expect NameError do 12 | raise Indexer::Error 13 | end 14 | end 15 | 16 | It can also be used to manually extend an error, in other words, 17 | to _tag_ an error. 18 | 19 | standard_error = StandardError.new 20 | standard_error.extend Indexer::Error 21 | 22 | begin 23 | raise standard_error 24 | rescue Indexer::Error => error 25 | assert StandardError === error 26 | end 27 | 28 | This allows Indexer exceptions to identified while still fully utilizing 29 | Ruby's exceptions system. 30 | 31 | -------------------------------------------------------------------------------- /demo/validator/03_name.md: -------------------------------------------------------------------------------- 1 | ## Validator#name 2 | 3 | A valid `name` is a word starting with a letter, ending with a letter or number 4 | and containing only `a-z`, `A-Z`, `0-9` and `_` or `-` characters. 5 | 6 | Examples of good @names are: 7 | 8 | - good 9 | - good_too 10 | - good-too 11 | - good2 12 | 13 | To verify this we can assign each name. 14 | 15 | data = Indexer::Validator.new 16 | 17 | @names.each do |name| 18 | data.name = name 19 | end 20 | 21 | And these are not good @names: 22 | 23 | - not good 24 | - not,good 25 | - good___ 26 | - good- 27 | 28 | Likewise, we can verify this by trying to assign each name. 29 | 30 | data = Indexer::Validator.new 31 | 32 | @names.each do |name| 33 | expect Indexer::ValidationError do 34 | data.name = name 35 | end 36 | end 37 | 38 | -------------------------------------------------------------------------------- /work/deprecated/cli/index.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/cli' 2 | 3 | module Indexer 4 | 5 | # 6 | # 7 | # 8 | class CLI::Index < CLI 9 | 10 | # 11 | def initialize 12 | @verbose = false 13 | end 14 | 15 | # 16 | def parse(opts) 17 | opts.banner = "Usage: #{$0} [options] [source ...]" 18 | opts.on_tail('--debug', 'run with $DEBUG set to true') do 19 | $DEBUG = true 20 | end 21 | opts.on_tail('-h', '--help', 'read this help message') do 22 | puts opts 23 | exit 24 | end 25 | end 26 | 27 | # 28 | def run 29 | Metadata.ensure_locked 30 | 31 | if Metadata.exists? 32 | metadata = Metadata.open 33 | puts metadata.about(*argv) 34 | else 35 | raise Error.exception(".index file not found", IOError) 36 | end 37 | end 38 | 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /demo/metadata/03_name.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#name 2 | 3 | A valid `name` is a word starting with a letter, ending with a letter or number 4 | and containing only `a-z`, `A-Z`, `0-9` and `_` or `-` characters. 5 | 6 | Examples of good @names are: 7 | 8 | - good 9 | - good_too 10 | - good-too 11 | - good2 12 | 13 | To verify this we can assign each name. 14 | 15 | metadata = Indexer::Metadata.new 16 | 17 | @names.each do |name| 18 | metadata.name = name 19 | end 20 | 21 | And these are not good @names: 22 | 23 | - not good 24 | - not,good 25 | - good___ 26 | - good- 27 | 28 | Likewise, we can verify this by trying to assign each name. 29 | 30 | metadata = Indexer::Metadata.new 31 | 32 | @names.each do |name| 33 | expect Indexer::ValidationError do 34 | metadata.name = name 35 | end 36 | end 37 | 38 | -------------------------------------------------------------------------------- /demo/loadable/33_save.md: -------------------------------------------------------------------------------- 1 | ## Metadata.save! 2 | 3 | Given a Metadata instance. 4 | 5 | spec = Indexer::Metadata.new 6 | spec.name = 'foo' 7 | spec.version = '1.0.0' 8 | 9 | We can save the Spec to disk in canonical form using 10 | the `#save!` method. 11 | 12 | spec.save!('save.yml') 13 | 14 | We can use `#read` to ensure it saved as we expected. 15 | 16 | spec = Indexer::Metadata.read('save.yml') 17 | 18 | And we can verify it was read. 19 | 20 | spec.name.should == 'foo' 21 | spec.version.should.to_s == '1.0.0' 22 | 23 | To be even more sure let's load in the raw YAML. 24 | 25 | yaml = YAML.load_file('save.yml') 26 | 27 | yaml['name'].should == 'foo' 28 | yaml['version'].should == '1.0.0' 29 | 30 | Notice also that certain fields automically get defaults 31 | values set. 32 | 33 | yaml['requirements'] == [] 34 | yaml['resources'] == {} 35 | 36 | -------------------------------------------------------------------------------- /demo/metadata/25_platform.md: -------------------------------------------------------------------------------- 1 | # Indexer::Metadata#platforms 2 | 3 | The `platform` field holds the RUBY_PLATFORM requirements for the project/package. 4 | If given, only matching platforms can make use of it. 5 | 6 | The `platform` field MUST be an array of strings with only a single line of text 7 | that match valid value of RUBY_PLATFORM. 8 | 9 | spec = Indexer::Metadata.new 10 | 11 | spec.platforms = ['x86_64-linux'] 12 | 13 | TODO: Should we do this? 14 | To ease matching to platforms a '?' and '*' can be used as a wildcard. 15 | 16 | spec.platforms = ['win*'] 17 | 18 | The field can't be assigned anything else. 19 | 20 | no 100 21 | no :symbol 22 | no "Foo\nBar" 23 | 24 | Metadata class also support the singular alias. 25 | 26 | spec.platform = ['win*'] 27 | 28 | Which additionally allows for a single platform assignment. 29 | 30 | spec.platform = 'x86_64-linux' 31 | 32 | -------------------------------------------------------------------------------- /demo/metadata/20_install_message.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#install_message 2 | 3 | The `install_message` field holds the post install message, 4 | which is displayed after a package is installed to provide users 5 | with important information about their newly installed application 6 | or library. 7 | 8 | The `install_message` fields SHOULD not be used to convey unimportant 9 | and trival statements. You do not need to thank people for installing 10 | your software in the install message! 11 | 12 | The `install_message` field can only contain a string, but in the Metadata 13 | call can be assembled form an array of strings as well. 14 | 15 | ok "Be sure to run `$foo bar`" 16 | ok ["Be sure to run", "`$foo bar`."] 17 | 18 | no 100 19 | no :symbol 20 | no Object.new 21 | 22 | Other than that the string can contain nay text desired, but the string 23 | SHOULD be less that 1,000 characters. 24 | 25 | -------------------------------------------------------------------------------- /demo/metadata/04_version.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#version 2 | 3 | The Indexer::Metadata class can hande 4 | The `version` field follows closely to the SemVer[http://semiver.org] standard. 5 | 6 | check "version validation error" do |v, r| 7 | spec = Indexer::Metadata.new 8 | spec.version = v 9 | spec.version.to_a.should == r 10 | end 11 | 12 | ### String 13 | 14 | ok "1.2.3", [1,2,3] 15 | ok "1.2.3.pre.4", [1,2,3,'pre',4] 16 | ok "1.2.3.pre4", [1,2,3,'pre',4] 17 | ok "1.2.3pre4", [1,2,3,'pre',4] 18 | 19 | ### Hash 20 | 21 | ok( {:major=>1, :minor=>2, :patch=>3}, [1,2,3] ) 22 | ok( {:major=>1, :minor=>0, :patch=>1, :build=>'pre3'}, [1,0,1,'pre',3] ) 23 | 24 | ok( {'major'=>1, 'minor'=>5, 'patch'=>1}, [1,5,1] ) 25 | 26 | ### Array 27 | 28 | ok [1, 2, 3], [1, 2, 3] 29 | ok [1, 2, 3, 'rc', 4], [1, 2, 3, 'rc', 4] 30 | 31 | 32 | -------------------------------------------------------------------------------- /demo/metadata/99_extra.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#method_missing 2 | 3 | To facilitate additonal uses beyond the well defined specification, 4 | the Metadata class is open to store extraneous information that 5 | a project developer may want to provide in the `.index` that is 6 | not supported by the specification. Besides personal usecases, 7 | it might also serve as a way to explore new specification fields 8 | for the future. 9 | 10 | Note that the value of the field can only be a Numeric or String, 11 | or an Array or Hash or the same. 12 | 13 | data = Indexer::Metadata.new 14 | 15 | data.notify = 'bob@gmail.com' 16 | 17 | We can see that the arbitrary field has been set. 18 | 19 | data.notify.should == 'bob@gmail.com' 20 | 21 | Anything else will raise a `ValidationError` exception. 22 | 23 | check do |obj| 24 | ! Indexer::ValidationError.raised? do 25 | data.notify = obj 26 | end 27 | end 28 | 29 | no :symbol 30 | no Object.new 31 | 32 | -------------------------------------------------------------------------------- /demo/validator/04_version.md: -------------------------------------------------------------------------------- 1 | ## Validator#version 2 | 3 | The `version` field is used to identify the specific version fo the project/package. 4 | Values should follow closely to the SemVer[http://semiver.org] standard. 5 | However, .ruby is somewhat more flexible to accommodate a larger birth of version 6 | numbering policies. As long as the version number (which is actually a string) 7 | starts with a numeric digit and consists of a series of dot and/or dash separated 8 | alphanumeric "point values", then .ruby can work with it. 9 | 10 | data = Indexer::Validator.new 11 | 12 | Here are examples of valid version numbers. 13 | 14 | ok "1.0.0" 15 | ok "1.0a" 16 | ok "1.0alpha" 17 | ok "1.0.beta" 18 | ok "1.0.1.rc.2" 19 | ok "1.0" 20 | 21 | And examples of invalid version numbers. 22 | 23 | no "a.b.c" 24 | no "1:1:1" 25 | no "1_1_1" # TODO: should we accept? 26 | no "a" 27 | no :a 28 | no 1 29 | no 1.1 30 | no Object.new 31 | 32 | -------------------------------------------------------------------------------- /work/reference/plugin_support/plugins.rb: -------------------------------------------------------------------------------- 1 | if RUBY_VERSION < '1.9' 2 | require 'dotruby/core_ext/rubygems' 3 | else 4 | require_relative 'core_ext/rubygems' 5 | end 6 | 7 | module DotRuby 8 | 9 | # 10 | PLUGIN_FILE = 'dotruby-*.rb' 11 | 12 | # 13 | def self.load_plugins 14 | find_plugins.each do |plugin| 15 | begin 16 | require plugin 17 | rescue LoadError => error 18 | warn "#{error}" 19 | end 20 | end 21 | end 22 | 23 | # 24 | def self.find_plugins 25 | matches = [] 26 | 27 | $LOAD_PATH.each do |dir| 28 | files = Dir.glob(File.join(dir, PLUGIN_FILE)) 29 | matches.concat(files) 30 | end 31 | 32 | if defined?(::Gem) 33 | files = ::Gem.search(PLUGIN_FILE) 34 | matches.concat(files) 35 | end 36 | 37 | if defined?(::Roll) 38 | files = ::Library.find_files(PLUGIN_FILE) 39 | matches.concat(files) 40 | end 41 | 42 | return matches.flatten.uniq 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /demo/metadata/21_created.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#created 2 | 3 | The `created` field is the date the project was started. 4 | 5 | This field must be a string that comforms to the ISO UTC timstamp standard. 6 | The format of which is "YYYY-MM-DD HH:MM:SS", wheret the time portion is 7 | optional. 8 | 9 | spec = Indexer::Metadata.new 10 | spec.created = "2011-10-20" 11 | spec.created = "2011-10-01 09:42:11" 12 | spec.created = "2011-10-01 14:42:11" 13 | 14 | Or, the assinged value can be a Date, Time, or DateTime object. 15 | 16 | spec.created = Date.new 17 | spec.created = Time.new 18 | spec.created = DateTime.new 19 | 20 | String values with invalid datetimes will raise an error, as will other 21 | type of objects. 22 | 23 | check "invalid date" do |d| 24 | ! Indexer::ValidationError.raised? do 25 | spec.created = d 26 | end 27 | end 28 | 29 | no "2011-50-01 09:42:11" 30 | no 100 31 | no :symbol 32 | no Object.new 33 | 34 | -------------------------------------------------------------------------------- /demo/metadata/06_title.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#title 2 | 3 | The `title` field is used in place of the `name` for documentation 4 | purposes and the like. 5 | 6 | spec = Indexer::Metadata.new 7 | 8 | spec.title = "Tom's Hello World Program" 9 | 10 | The `title` value MUST have only one line of text. 11 | 12 | expect Indexer::ValidationError do 13 | spec.title = "Foo\nBar" 14 | end 15 | 16 | The Metadata class will stip out excess space from a title. 17 | 18 | spec.title = "Foo Bar" 19 | spec.title.assert == "Foo Bar" 20 | 21 | spec.title = "Foo \t Bar" 22 | spec.title.assert == "Foo Bar" 23 | 24 | If no title is defined, but `name` has been assigned, then the title 25 | will default to the name capitalized. 26 | 27 | spec = Indexer::Metadata.new 28 | spec.name = "foo" 29 | spec.title.assert == "Foo" 30 | 31 | 32 | 33 | TODO: Should it have a size limit? 34 | 35 | TODO: Should it not allow punctuation marks as the first character? 36 | 37 | -------------------------------------------------------------------------------- /demo/validator/23_copyrights.md: -------------------------------------------------------------------------------- 1 | ## Validator#copyright 2 | 3 | The `copyrights` field is a sequence of mappings providing the copyright 4 | and license information. 5 | 6 | License values SHOULD be identifiers from SPDX. 7 | 8 | data = Indexer::Validator.new 9 | 10 | data.copyrights = [ 11 | { 'year' => '2010', 12 | 'holder' => 'T. Bone Willy', 13 | 'license' => 'MIT' 14 | }, 15 | { 'year' => '2011', 16 | 'holder' => 'J. Horn Silly', 17 | 'license' => 'GPL-3.0' 18 | } 19 | ] 20 | 21 | And it can be no tother type of object. 22 | 23 | no 100 24 | no :symbol 25 | no Object.new 26 | 27 | The field has no other restrictions, but as a matter of proper use it SHOULD 28 | not contain license disclamers --that is the domain of a README, COPYING or 29 | a similar file. 30 | 31 | The usecase for this field is to output a copyright notice for a command 32 | line `--version` inquery, or on a pop-up About window. 33 | 34 | -------------------------------------------------------------------------------- /demo/metadata/17_alternatives.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#alternatives 2 | 3 | The `alternatives` field holds a list of other packages with which this 4 | package can act as a reasonable substitue and vice-versa. 5 | 6 | The format of the field is an array. 7 | 8 | spec = Indexer::Metadata.new 9 | 10 | spec.alternatives = ['BlueCloth','rdiscount'] 11 | 12 | The Metadata class allows any object that responds to #to_ary to be 13 | assinged. 14 | 15 | o = Object.new 16 | 17 | def o.to_ary 18 | ['BlueCloth', 'rdiscount'] 19 | end 20 | 21 | spec.alternatives = o 22 | 23 | But the elements must be String, or respond to `#to_str`. 24 | 25 | # TODO: write this demonstration 26 | 27 | The `alternatives` field cannot accept anything else. 28 | 29 | check "invalid alternative" do |d| 30 | ! Indexer::ValidationError.raised? do 31 | spec.alternatives = d 32 | end 33 | end 34 | 35 | no 100 36 | no :symbol 37 | no "string" 38 | no Object.new 39 | 40 | -------------------------------------------------------------------------------- /demo/metadata/14_date.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#date 2 | 3 | The `date` field is the date the `.ruby` file was last edited, or if 4 | part of a package when the package was built. 5 | 6 | This field must be a string that comforms to the ISO UTC timstamp standard. 7 | The format of which is "YYYY-MM-DD HH:MM:SS", wheret the time portion is 8 | optional. 9 | 10 | spec = Indexer::Metadata.new 11 | spec.date = "2011-10-20" 12 | spec.date = "2011-10-01 09:42:11" 13 | spec.date = "2011-10-01 14:42:11" 14 | 15 | Or, the assinged value can be a Date, Time, or DateTime object. 16 | 17 | spec.date = Date.new 18 | spec.date = Time.new 19 | spec.date = DateTime.new 20 | 21 | String values with invalid datetimes will raise an error, as will other 22 | type of objects. 23 | 24 | check "invalid date" do |d| 25 | ! Indexer::ValidationError.raised? do 26 | spec.date = d 27 | end 28 | end 29 | 30 | no "2011-50-01 09:42:11" 31 | no 100 32 | no :symbol 33 | no Object.new 34 | 35 | -------------------------------------------------------------------------------- /demo/valid/05_word.md: -------------------------------------------------------------------------------- 1 | ## Valid.word? / word! 2 | 3 | A valid word is String with no whitepsace characters, including 4 | new lines. It must start with a letter and can only contain 5 | characters /[A-Z][a-z]_-/. 6 | 7 | ### word? 8 | 9 | check "word" do |word| 10 | Indexer::Valid.word?(word) 11 | end 12 | 13 | These are all valid words. 14 | 15 | ok "good" 16 | ok "still_good" 17 | ok "still-good" 18 | ok "good1" 19 | 20 | And these are not valid words. 21 | 22 | no "not good" 23 | no "not\ngood" 24 | no "not\tgood" 25 | no "1notgood" 26 | 27 | ### word! 28 | 29 | check do |word| 30 | ! Indexer::ValidationError.raised? do 31 | Indexer::Valid.word!(word) 32 | end 33 | end 34 | 35 | Again, these are all valid words. 36 | 37 | ok "good" 38 | ok "still_good" 39 | ok "still-good" 40 | ok "good1" 41 | 42 | And these are not valid words. 43 | 44 | no "not good" 45 | no "not\ngood" 46 | no "not\tgood" 47 | no "1notgood" 48 | 49 | -------------------------------------------------------------------------------- /demo/metadata/19_conflicts.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#conflicts 2 | 3 | The `conflicts` field holds a list of packages with which the project is 4 | known not to be compatible. 5 | 6 | The format of the field is a mapping to package names indexing version 7 | constraints. 8 | 9 | spec = Indexer::Metadata.new 10 | 11 | spec.conflicts = { 12 | 'bad_robot' => '>=1.0', 13 | 'evil' => '0+' 14 | } 15 | 16 | The conflicts fields also accepts an array of strings. 17 | 18 | spec.conflicts = [ 19 | 'bad_robot >=1.0', 20 | 'evil 0+' 21 | ] 22 | 23 | Or an array of hash entries. 24 | 25 | spec.conflicts = [ 26 | {:name => 'bad_robot', :version => '>=1.0'}, 27 | {:name => 'evil', :version => '0+' } 28 | ] 29 | 30 | The `conflicts` field can only by assigned such a Hash. 31 | 32 | check "invalid conflict" do |d| 33 | ! Indexer::ValidationError.raised? do 34 | spec.conflicts = d 35 | end 36 | end 37 | 38 | no 100 39 | no :symbol 40 | no Object.new 41 | 42 | 43 | -------------------------------------------------------------------------------- /demo/validator/99_extra.md: -------------------------------------------------------------------------------- 1 | ## Validator#extra 2 | 3 | To facilitate additonal uses beyond the well defined specification, 4 | the Metadata class is open to store extraneous information that 5 | a project developer may want to provide in the `.index` that is 6 | not supported by the specification. Besides personal usecases, 7 | it might also serve as a way to explore new specification fields 8 | for the future. 9 | 10 | Note that the value of the field can only be a Numeric or String, 11 | or an Array or Hash or the same. 12 | 13 | data = Indexer::Validator.new 14 | 15 | data.notify = 'bob@gmail.com' 16 | 17 | We can see that the arbitrary field has been set. 18 | 19 | data.notify.should == 'bob@gmail.com' 20 | 21 | Anything else will raise a `ValidationError` exception. 22 | 23 | check do |obj| 24 | ! Indexer::ValidationError.raised? do 25 | data.notify = obj 26 | end 27 | end 28 | 29 | no :symbol 30 | no Object.new 31 | 32 | If we try to read a open entry that is not present, `nil` is returned. 33 | 34 | data.foo.assert == nil 35 | 36 | -------------------------------------------------------------------------------- /lib/indexer/importer/yaml.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # Import metadata from a YAML source. 6 | # 7 | module YAMLImportation 8 | 9 | # 10 | # YAML import procedure. 11 | # 12 | def import(source) 13 | if File.file?(source) 14 | case File.extname(source) 15 | when '.yaml', '.yml' 16 | load_yaml(source) 17 | true 18 | else 19 | text = read(source) 20 | if text =~ /\A---/ 21 | load_yaml(source) 22 | true 23 | else 24 | super(source) if defined?(super) 25 | end 26 | end 27 | else 28 | super(source) if defined?(super) 29 | end 30 | end 31 | 32 | # 33 | # Import metadata from YAML file. 34 | # 35 | def load_yaml(file) 36 | metadata.merge!(YAML.load_file(file)) 37 | end 38 | 39 | end 40 | 41 | # Include YAMLImportation mixin into Builder class. 42 | include YAMLImportation 43 | 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /demo/metadata/12_resources.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#resources 2 | 3 | The `resources` field holds a list of URLs index by type. 4 | 5 | metadata = Indexer::Metadata.new 6 | 7 | metadata.resources = { 8 | 'home' => 'http://foo.org', 9 | 'docs' => 'http://foo.org/api' 10 | } 11 | 12 | The field can also be assigned an array of two-element arrays. 13 | 14 | metadata.resources = [ 15 | ['home', 'http://foo.org'], 16 | ['docs', 'http://foo.org/api'], 17 | ['chat', '#foo'] 18 | ] 19 | 20 | But the second element must be a valid URL or a hash tag for IRC chat. 21 | 22 | There are no restrictions on the index value other than it be a one 23 | line string. 24 | 25 | metadata.resources = [ 26 | ['whatever', 'http://foo.org'], 27 | ['something', 'http://foo.org/api'], 28 | ] 29 | 30 | However some _recognized_ resource identifiers are taken to be 31 | synonyms by the underlying Resources class. For example, 32 | 33 | metadata.resources = [ 34 | ['home', 'http://foo.org'], 35 | ['wiki', 'http://foo.org/api'], 36 | ['chat', '#foo'] 37 | ] 38 | 39 | -------------------------------------------------------------------------------- /lib/indexer/importer/version.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # Build mixin for Bundler's Gemfile. 6 | # 7 | module VersionImportation 8 | # 9 | # If the source file is a Gemfile, import it. 10 | # 11 | def import(source) 12 | case source 13 | when 'VERSION.txt', 'Version.txt' 14 | vers = File.read(source).strip 15 | metadata.version = vers 16 | when 'VERSION', 'Version' 17 | text = File.read(source).strip 18 | if yaml?(text) 19 | # don't really like this style b/c it's too subjective 20 | hash = YAML.load(text) 21 | hash = hash.inject{ |h,(k,v)| h[k.to_sym] = v; h } 22 | vers = hash.values_at(:major,:minor,:patch,:build).compact 23 | else 24 | vers = File.read(source).strip 25 | end 26 | metadata.version = vers 27 | else 28 | super(source) if defined?(super) 29 | end 30 | end 31 | end 32 | 33 | # Include VersionImportation mixin into Builder class. 34 | include VersionImportation 35 | 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /lib/indexer/revision.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Indexer's revision strategy is a "no project left behind" strategy. 4 | # When using the API, is a specification is loaded that is outdated, 5 | # it will be upconverted to the latest standard. 6 | # 7 | # If you absolutely *must* support an old revision then use an older 8 | # version of Indexer, or work with the metadata manually (via YAML). 9 | # 10 | module Revision 11 | extend self 12 | 13 | # 14 | # Revise the metadata to current standard. 15 | # 16 | def upconvert(data) 17 | revision = data['revision'] || data[:revision] 18 | 19 | revision = REVISION unless revision 20 | 21 | if revision != REVISION 22 | begin 23 | require "revisions/r#{revision}" 24 | __send__("r#{revision}", data) 25 | rescue LoadError 26 | raise ValidationError, "unknown revision #{revision}" 27 | end 28 | else 29 | data 30 | end 31 | end 32 | 33 | # 34 | # Update R2013 specification to current specification. 35 | # 36 | def r2013(data) 37 | data 38 | end 39 | 40 | end 41 | 42 | end 43 | 44 | -------------------------------------------------------------------------------- /lib/indexer/conversion/gemfile.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module Conversion 4 | 5 | # 6 | # Update Spec with a Gemfile. 7 | # 8 | # @param [nil,String,::Bundler::DSL,::Bundler::Definition] 9 | # Gemfile path, Bundler::Dsl or Bundler::Definition instance. 10 | # 11 | def import_gemfile(file=nil) 12 | require 'bundler' 13 | 14 | case file 15 | when String 16 | # FIXME: Is this the correct way to load a gemfile? 17 | bundle = ::Bundler::Dsl.new 18 | bundle.eval_gemfile(file) 19 | when NilClass 20 | bundle = ::Bundler.definition 21 | end 22 | 23 | begin 24 | merge!(bundle.metadata.to_h) 25 | rescue 26 | end 27 | 28 | bundle.dependencies.each do |d| 29 | add_requirement(d.name, 30 | :version => d.requirement.to_s, # may need to parse this 31 | :groups => d.groups, 32 | :development => (d.type == :development) 33 | #:engines => d.platforms ? 34 | #:platforms => ? 35 | ) 36 | end 37 | end 38 | 39 | alias_method :gemfile, :import_gemfile 40 | 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /lib/indexer/importer/ruby.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # Build metadata from a Ruby script. 6 | # 7 | module RubyImportation 8 | 9 | # 10 | # Ruby script import procedure. 11 | # 12 | def import(source) 13 | if File.file?(source) 14 | case File.extname(source) 15 | when '.rb' # TODO: Other ruby extensions ? 16 | load_ruby(source) 17 | true 18 | else 19 | text = read(source) 20 | if text !=~ /\A---/ # not YAML 21 | load_ruby(source) 22 | true 23 | else 24 | super(source) if defined?(super) 25 | end 26 | end 27 | else 28 | super(source) if defined?(super) 29 | end 30 | end 31 | 32 | # 33 | # Load ruby file by simply evaluating it in the context 34 | # of the Builder instance. 35 | # 36 | def load_ruby(file) 37 | instance_eval(File.read(file)) 38 | end 39 | 40 | end #module RubyImportation 41 | 42 | # Include RubyImportation mixin into Builder class. 43 | include RubyImportation 44 | 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /work/deprecated/model.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Model 4 | 5 | def self.r(revision) 6 | Class.new(self){ 7 | include const_get("V%s" % [revision || REVISION) 8 | } 9 | end 10 | 11 | =begin 12 | # What the heck does this do? 13 | # 14 | # author = Indexer::V0::Author.new(...) 15 | # author.is_a?(Indexer::Author) #=> true 16 | # 17 | # TODO: This is probably a dead giveaway that the inverse factory 18 | # we are using via module extensions, is not the way to go. 19 | # 20 | def self.inherited(base) 21 | basename = base.name.split('::').last 22 | if not Indexer.const_defined?(basename) 23 | mod = Module.new 24 | mod.module_eval %{ 25 | # Use constant hash instead? 26 | def self.V(revision) 27 | Indexer::V[revison]::#{basename} 28 | end 29 | def self.new(*args, &blk) 30 | Indexer::V[REVISION]::#{basename}.new(*args, &blk) 31 | end 32 | } 33 | Indexer.const_set(basename, mod) 34 | else 35 | mod = Indexer.const_get(basename) 36 | end 37 | base.send(:include, mod) 38 | end 39 | =end 40 | 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /demo/version/constraint/02_initialize.md: -------------------------------------------------------------------------------- 1 | ## Version::Constraint#initialize 2 | 3 | The Constraint class models a single version equality 4 | or inequality. It consists of the operator and the version 5 | number. 6 | 7 | c = Indexer::Version::Constraint.new(['>=', '1.0']) 8 | 9 | c.operator == '>=' 10 | c.number == '1.0' 11 | 12 | ### Using [] 13 | 14 | c = Indexer::Version::Constraint['>=', '1.0'] 15 | 16 | c.operator == '>=' 17 | c.number == '1.0' 18 | 19 | ### String Parsing 20 | 21 | The constraint constructor can also parse strings. 22 | 23 | check do |mp| 24 | mp.each do |str, (op, num)| 25 | c = Indexer::Version::Constraint.new(str) 26 | c.operator.should == op 27 | c.number.to_s.should == num 28 | end 29 | end 30 | 31 | ok '1.2.3' => ['==', '1.2.3'] 32 | ok '= 4.6.2' => ['==', '4.6.2'] 33 | ok '== 5.1.1' => ['==', '5.1.1'] 34 | ok '>= 2.3.4' => ['>=', '2.3.4'] 35 | ok '> 3.4.5' => ['>', '3.4.5'] 36 | ok '<= 2.3.4' => ['<=', '2.3.4'] 37 | ok '< 3.4.5' => ['<', '3.4.5'] 38 | 39 | Suffix notations are also parsable. 40 | 41 | ok '1.2+' => ['>=', '1.2'] 42 | ok '1.3-' => ['<' , '1.3'] 43 | ok '2.8~' => ['=~', '2.8'] 44 | 45 | -------------------------------------------------------------------------------- /demo/version/number/08_bump.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#bump 2 | 3 | A version number can be *bumped*, which means various segments 4 | can be incremented. Bumping is handled by the #bump method which 5 | is passed a symbol describing the type of bump to effect. 6 | 7 | ### Bump major number 8 | 9 | v = Indexer::Version::Number[1,0,0] 10 | v.bump(:major).to_s == '2.0.0' 11 | 12 | ### Bump minor number 13 | 14 | v = Indexer::Version::Number[1,0,0] 15 | v.bump(:minor).to_s == '1.1.0' 16 | 17 | ### Bump patch number 18 | 19 | v = Indexer::Version::Number[1,0,0] 20 | v.bump(:patch).to_s == '1.0.1' 21 | 22 | ### Bump build number 23 | 24 | v = Indexer::Version::Number[1,0,0,0] 25 | v.bump(:build).to_s == '1.0.0.1' 26 | 27 | ### Bump build number with state 28 | 29 | v = Indexer::Version::Number[1,0,0,'pre',1] 30 | v.bump(:build).to_s == '1.0.0.pre.2' 31 | 32 | ### Bump state segment 33 | 34 | v = Indexer::Version::Number[1,0,0,'pre',2] 35 | v.bump(:state).to_s == '1.0.0.rc.1' 36 | 37 | ### Bump last segment 38 | 39 | v = Indexer::Version::Number[1,0,0,'pre',3] 40 | v.bump(:last).to_s == '1.0.0.pre.4' 41 | 42 | ### Reset State 43 | 44 | v = Indexer::Version::Number[1,0,0,'pre',2] 45 | v.restate(:beta).to_s == '1.0.0.beta.1' 46 | 47 | -------------------------------------------------------------------------------- /demo/metadata/13_repositories.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#repositories 2 | 3 | The `repositories` field holds a list of repository URLs indexed by an id. 4 | 5 | spec = Indexer::Metadata.new 6 | 7 | spec.repositories = [ 8 | { 9 | 'uri' => 'https://github.com/foostuff/foo.git', 10 | 'scm' => 'git', 11 | 'name' => 'main' 12 | } 13 | ] 14 | 15 | spec.repositories.first.scm #=> 'git' 16 | 17 | The `scm` field can be omitted, in most cases the Repository class can infer 18 | it from the URI. 19 | 20 | spec.repositories = [ 21 | { 'uri' => 'https://github.com/foostuff/foo.git' } 22 | ] 23 | 24 | spec.repositories.first.scm #=> 'git' 25 | 26 | The field can also be assigned a simple hash of ids mapped to URIs. 27 | 28 | spec.repositories = { 29 | 'main' => 'https://github.com/foostuff/foo.git' 30 | } 31 | 32 | The field can also be assigned an associative array. 33 | 34 | spec.repositories = [ 35 | ['main', 'https://github.com/foostuff/foo.git'] 36 | ] 37 | 38 | Or just simple strings. 39 | 40 | spec.repositories = [ 41 | 'https://github.com/foostuff/foo.git' 42 | ] 43 | 44 | Note that is two entries share the same `id`, the first to appear is considered 45 | the primary repository, and the later are considreed fallbacks (such as an SVN 46 | mirror of a GIT repository, for example). 47 | 48 | -------------------------------------------------------------------------------- /lib/indexer/core_ext/kernel/cli.rb: -------------------------------------------------------------------------------- 1 | module Kernel 2 | private 3 | # 4 | # CLI is based on Clap library 5 | # Copyright (c) 2010 Michel Martens 6 | # 7 | def cli(*args) 8 | opts = args.pop 9 | argv = (args.first || ARGV).dup 10 | args = [] 11 | 12 | # Split option aliases. 13 | opts = opts.inject({}) do |h,(k,v)| 14 | k.to_s.split(/\s+/).each{|o| h[o]=v}; h 15 | end 16 | 17 | # Convert single dash flags into multiple flags. 18 | argv = argv.inject([]) do |a, v| 19 | if v[0,1] == '-' && v[1,1] != '-' 20 | a.concat(v[1..-1].chars.map{|c| "-#{c}"}) 21 | else 22 | a << v 23 | end 24 | a 25 | end 26 | 27 | while argv.any? 28 | item = argv.shift 29 | flag = opts[item] 30 | 31 | if flag 32 | # Work around lambda semantics in 1.8.7. 33 | arity = [flag.arity, 0].max 34 | 35 | # Raise if there are not enough parameters 36 | # available for the flag. 37 | if argv.size < arity 38 | raise ArgumentError 39 | end 40 | 41 | # Call the lambda with N items from argv, 42 | # where N is the lambda's arity. 43 | flag.call(*argv.shift(arity)) 44 | else 45 | 46 | # Collect the items that don't correspond to 47 | # flags. 48 | args << item 49 | end 50 | end 51 | 52 | args 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - var 6 | authors: 7 | - name: trans 8 | email: transfire@gmail.com 9 | website: http://trans.gihub.com 10 | - name: postmodern 11 | website: http://postmodern.github.com 12 | organizations: 13 | - name: Rubyworks 14 | website: http://rubyworks.github.com 15 | requirements: 16 | - groups: 17 | - test 18 | version: 2.9+ 19 | development: true 20 | name: qed 21 | - groups: 22 | - test 23 | development: true 24 | name: ae 25 | conflicts: [] 26 | alternatives: [] 27 | resources: 28 | - type: home 29 | uri: http://rubyworks.github.com/indexer 30 | label: Homepage 31 | - type: code 32 | uri: http://github.com/rubyworks/indexer 33 | label: Source Code 34 | - type: api 35 | uri: http://rubydoc.info/gems/indexer/frames 36 | label: API Guide 37 | repositories: 38 | - name: upstream 39 | scm: git 40 | uri: http://github.com/rubyworks/indexer/indexer.git 41 | categories: 42 | - metadata 43 | copyrights: 44 | - holder: Rubyworks 45 | year: '2012' 46 | license: BSD-2-Clause 47 | customs: 48 | - example 49 | paths: 50 | lib: 51 | - lib 52 | name: indexer 53 | title: Indexer 54 | summary: Enable Your Project's Metadata 55 | created: '2011-06-01' 56 | description: Indexer provides projects with a universal metadata format. 57 | example: This is just an example of custom metadata. 58 | version: 0.3.1 59 | date: '2013-08-07' 60 | -------------------------------------------------------------------------------- /lib/indexer/importer/gemspec.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # It is not the recommended that a .gemspec be the usual source of metadata. 6 | # Rather it is recommended that a the gemspec be produced from the metadata 7 | # instead. (Rumber's metadata covers almost every aspect of a emspec, and 8 | # a gemspec can be augmented where needed.) Nonetheless, a gemspec can serve 9 | # as a good soruce for creating an initial metadata file. 10 | # 11 | module GemspecImportation 12 | 13 | # 14 | # If the source file is a gemspec, import it. 15 | # 16 | def import(source) 17 | case File.extname(source) 18 | when '.gemspec' 19 | # TODO: handle YAML-based gemspecs 20 | gemspec = ::Gem::Specification.load(source) 21 | metadata.import_gemspec(gemspec) 22 | true 23 | else 24 | super(source) if defined?(super) 25 | end 26 | end 27 | 28 | # 29 | #def local_files(root, glob, *flags) 30 | # bits = flags.map{ |f| File.const_get("FNM_#{f.to_s.upcase}") } 31 | # files = Dir.glob(File.join(root,glob), bits) 32 | # files = files.map{ |f| f.sub(root,'') } 33 | # files 34 | #end 35 | end 36 | 37 | # Include GemspecImportation mixin into Builder class. 38 | include GemspecImportation 39 | 40 | end 41 | 42 | end 43 | 44 | -------------------------------------------------------------------------------- /demo/version/number/07_segments.md: -------------------------------------------------------------------------------- 1 | ## Version::Number Segments 2 | 3 | A version number is divided up into segements, or *points*. 4 | Some of the points have names. THe first three are widely known 5 | as the *major*, *minor* and *patch* numbers, respectively. 6 | 7 | v = Indexer::Version::Number[1,2,3] 8 | 9 | v.major.assert == 1 10 | v.minor.assert == 2 11 | v.patch.assert == 3 12 | 13 | The Version::Number class also recognizes state, or status. 14 | 15 | v = Indexer::Version::Number[1,2,3,'beta',4] 16 | 17 | v.state.assert == 'beta' 18 | v.status.assert == 'beta' 19 | 20 | From the state on is considered the *build* portion of the version. 21 | 22 | v.build.assert == 'beta.4' 23 | 24 | The *build number* is the number directly following the state. 25 | 26 | v.build_number.assert == 4 27 | 28 | While it is not recommended, technically the Version::Number class 29 | can handle an arbitrarily long tuple. 30 | 31 | v = Indexer::Version::Number[1,2,3,4,5,6,'beta',7] 32 | 33 | v.major.assert == 1 34 | v.minor.assert == 2 35 | v.patch.assert == 3 36 | 37 | v.state.assert == 'beta' 38 | 39 | However, there are no recognized names for the segments between 40 | the third, patch entry and the state entry. 41 | 42 | Short version can also be used. 43 | 44 | v = Indexer::Version::Number[1,'rc',7] 45 | 46 | v.major.assert == 1 47 | v.minor.assert == nil 48 | v.patch.assert == nil 49 | 50 | v.state.assert == 'rc' 51 | 52 | -------------------------------------------------------------------------------- /demo/metadata/11_paths.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#paths 2 | 3 | The `paths` field is a means by which project paths can be designated. 4 | In general, it is intended for the path keys to correspond to the FHS 5 | (File Hierarchy Standard), although there is no absolute requirement. 6 | 7 | For example, Ruby developer's use the `lib` entry to specify the locations 8 | within the project that Ruby's load system should look for files when `#require` 9 | or `#load` are used. 10 | 11 | The `paths` value MUST be a Hash. 12 | 13 | spec = Indexer::Metadata.new 14 | spec.paths = { 'lib' => ['lib'] } 15 | 16 | Or any object that responds to `#to_hash`. 17 | 18 | o = Object.new 19 | 20 | def o.to_hash 21 | { 'lib' => ['lib'] } 22 | end 23 | 24 | spec.paths = o 25 | 26 | The hash values should be arrays, but a string can be used and it 27 | will be converted into an array. 28 | 29 | spec.paths = { "foo" => "foo/path" } 30 | spec.paths.assert == { "foo" => ["foo/path"] } 31 | 32 | Any other object will cause an error. 33 | 34 | check "invalid load_path" do |d| 35 | ! Indexer::ValidationError.raised? do 36 | spec.paths = d 37 | end 38 | end 39 | 40 | no 100 41 | no :symbol 42 | no Object.new 43 | 44 | The elements must also be valid path strings. 45 | 46 | no 'load'=>[100, 200] 47 | 48 | By default the value, when no paths are set, is `{ 'lib=> ['lib']}`. 49 | 50 | spec = Indexer::Metadata.new 51 | spec.paths.should = {'lib' => ['lib']} 52 | 53 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | #!mast -x etc .index .yardopts bin data lib man *.md *.txt 2 | .index 3 | .yardopts 4 | bin/index 5 | data/indexer/index.kwalify 6 | data/indexer/index.yes 7 | data/indexer/index.yesi 8 | data/indexer/ruby.txt 9 | data/indexer/yaml.txt 10 | lib/indexer/attributes.rb 11 | lib/indexer/command.rb 12 | lib/indexer/components/author.rb 13 | lib/indexer/components/conflict.rb 14 | lib/indexer/components/copyright.rb 15 | lib/indexer/components/dependency.rb 16 | lib/indexer/components/engine.rb 17 | lib/indexer/components/organization.rb 18 | lib/indexer/components/repository.rb 19 | lib/indexer/components/requirement.rb 20 | lib/indexer/components/resource.rb 21 | lib/indexer/components.rb 22 | lib/indexer/conversion/gemfile.rb 23 | lib/indexer/conversion/gemspec.rb 24 | lib/indexer/conversion/gemspec_exporter.rb 25 | lib/indexer/conversion.rb 26 | lib/indexer/core_ext/hash/rekey.rb 27 | lib/indexer/core_ext/hash/to_h.rb 28 | lib/indexer/core_ext/kernel/cli.rb 29 | lib/indexer/core_ext.rb 30 | lib/indexer/error.rb 31 | lib/indexer/gemfile.rb 32 | lib/indexer/importer/file.rb 33 | lib/indexer/importer/gemfile.rb 34 | lib/indexer/importer/gemspec.rb 35 | lib/indexer/importer/ruby.rb 36 | lib/indexer/importer/version.rb 37 | lib/indexer/importer/yaml.rb 38 | lib/indexer/importer.rb 39 | lib/indexer/loadable.rb 40 | lib/indexer/metadata.rb 41 | lib/indexer/model.rb 42 | lib/indexer/revision.rb 43 | lib/indexer/valid.rb 44 | lib/indexer/validator.rb 45 | lib/indexer/version/constraint.rb 46 | lib/indexer/version/exceptions.rb 47 | lib/indexer/version/number.rb 48 | lib/indexer.rb 49 | README.md 50 | HISTORY.md 51 | -------------------------------------------------------------------------------- /demo/validator/02_initialize.md: -------------------------------------------------------------------------------- 1 | ## Validator#initialize 2 | 3 | ### Bare Instance 4 | 5 | A bare Validator object can be created by passing no arguments 6 | to the initializer. 7 | 8 | data = Indexer::Validator.new 9 | 10 | data.revision.should == Indexer::REVISION 11 | 12 | In addition, certain attributes will have default values. 13 | 14 | data.copyrights.should == [] 15 | data.authors.should == [] 16 | data.organizations.should == [] 17 | data.requirements.should == [] 18 | data.conflicts.should == [] 19 | data.repositories.should == [] 20 | data.resources.should == [] 21 | 22 | data.paths.should == { 'lib' => ['lib'] } 23 | 24 | ### Validator Argument 25 | 26 | A Validator object can be created with initial values by passing a data 27 | hash to the initializer. 28 | 29 | data = Indexer::Validator.new(:name=>'foo', :version=>'0.1.2') 30 | 31 | data.name.should == 'foo' 32 | data.version.should == '0.1.2' 33 | 34 | Entries passed to the initializer are assigned via Validator's setters 35 | and are validated upon assignment, so no invalid values can get into the 36 | object's state, e.g. 37 | 38 | expect Indexer::ValidationError do 39 | Indexer::Validator.new(:name=>1) 40 | end 41 | 42 | ### Initial Validity 43 | 44 | The only way for a Validator object to be in an invalid state is 45 | by the creation of an instance without providing a `name` and `version`. 46 | Both name and version are required for a specification to be valid. 47 | 48 | data = Indexer::Validator.new 49 | 50 | data.refute.valid? 51 | 52 | All other fields can be `nil` or the default values automatically assigned 53 | as shown above. 54 | 55 | -------------------------------------------------------------------------------- /work/deprecated/cli/init.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/cli' 2 | require 'erb' 3 | 4 | module Indexer 5 | 6 | # 7 | # 8 | # 9 | class CLI::Init < CLI 10 | 11 | attr_accessor :yaml 12 | 13 | # 14 | def initialize 15 | @yaml = false 16 | end 17 | 18 | # 19 | def parse(opts) 20 | opts.banner = "Usage: #{$0} [options]" 21 | opts.on_tail('--yaml', 'use a YAML-based Metafile') do 22 | self.yaml = true 23 | end 24 | end 25 | 26 | # 27 | def run 28 | if argv.first 29 | outfile = argv.first 30 | else 31 | outfile = "Indexfile" 32 | end 33 | 34 | if File.exist?(outfile) 35 | raise Error.exception("#{$0}: #{outfile} file already exists.", IOError) 36 | end 37 | 38 | template_dir = File.join(DATADIR, "v#{REVISION}") 39 | 40 | if yaml 41 | template_file = File.join(template_dir, 'yaml.txt') 42 | else 43 | template_file = File.join(template_dir, 'ruby.txt') 44 | end 45 | 46 | metadata = Metadata.new 47 | 48 | if gemspec = Dir['{,pkg/}*.gemspec'].first 49 | metadata.import_gemspec(gemspec) 50 | end 51 | 52 | template = ERB.new(File.read(template_file)) 53 | result = template.result(Form.new(metadata).binding) 54 | 55 | File.open(outfile, 'w') do |f| 56 | f << result 57 | end 58 | end 59 | 60 | # 61 | # Helper class for generating template. 62 | # 63 | class Form 64 | def initialize(metadata) 65 | @metadata = metadata 66 | end 67 | def method_missing(s, *a, &b) 68 | @metadata.public_send(s, *a, &b) || '' 69 | end 70 | public :binding 71 | end 72 | 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /demo/metadata/10_authors.md: -------------------------------------------------------------------------------- 1 | # Indexer::Metadata#authors 2 | 3 | The `authors` field holds a list of orginating authors. 4 | 5 | The Metadata class allows, of course, the canonical format. 6 | 7 | spec = Indexer::Metadata.new 8 | 9 | spec.organizations = [ 10 | { 'name' => 'Bobs Williams', 11 | 'email' => 'bob@mail.com', 12 | 'roles' => ['sponser'] 13 | }, 14 | { 'name' => 'John Delight', 15 | 'email' => 'dlite@mail.com', 16 | 'roles' => ['development'] 17 | } 18 | ] 19 | 20 | But it also can handle and Array of Arrays. The order of the inner array 21 | is `name, email, werbsite`. Only name is not optional. 22 | 23 | spec.authors = [ 24 | ['Bob Williams', 'bob@mail.com'], 25 | ['John Delight', 'dlite@mail.com', 'http://foo.org'] 26 | ] 27 | 28 | It will also handle an Array of strings, parsing out the given parts. 29 | 30 | spec.authors = [ 31 | 'Bob Williams ', 32 | 'John Delight http://foo.org' 33 | ] 34 | 35 | It does this via recognition that an email address would be in `<...>` 36 | brackets and URL validation for the website. 37 | 38 | It can even handle combination of the above. 39 | 40 | spec.authors = [ 41 | {'name' => 'Bob Williams', 'email' => 'bob@mail.com'}, 42 | ['Coco Chappel', ''], 43 | ['John Delight ', 'http://foo.org'] 44 | ] 45 | 46 | spec.authors[0].name.assert == 'Bob Williams' 47 | spec.authors[0].email.assert == 'bob@mail.com' 48 | 49 | spec.authors[1].name.assert == 'Coco Chappel' 50 | spec.authors[1].email.assert == 'coco@mail.com' 51 | 52 | spec.authors[2].name.assert == 'John Delight' 53 | spec.authors[2].email.assert == 'dlite@mail.com' 54 | spec.authors[2].website.assert == 'http://foo.org' 55 | 56 | 57 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 0.3.0 / 2012-01-19 4 | 5 | The 0.3 release makes three significant improvements. First, instead of 6 | using `paths['load']` for the Ruby load path setting, it designates the use 7 | of `paths['lib']`. In this manner the keys of the paths field can be generally 8 | based on standard locations in a project, and more generally on FHS. Secondly, 9 | this release also removes the `extra` field. Instead custom entries can be added 10 | at the root of the document. Note that this means that parts of the API may 11 | return `nil` instead of raising an error when queried for an undefined field. 12 | To ensure that metadata van still be strongly validated the specification adds 13 | a `customs` field where the names of custom fields are specified. 14 | 15 | Changes: 16 | 17 | * Designate `paths['lib']` for Ruby's load path, instead of `paths['load']`. 18 | * Remove `extra` field from specification; custom fields can be in root not. 19 | * Add `customs` field to allow customization in a robust manner. 20 | 21 | 22 | ## 0.2.0 / 2012-12-27 23 | 24 | The `load_path` field has been deprecated and replaced with the `paths` field, 25 | which holds a mapping of names to local path locations within the project. This 26 | makes identifying paths completely generic. For Ruby projects use the 'load' entry. 27 | 28 | This release also makes Kramdown the fallback for importing microformat data from 29 | markdown documents if Redcarpet is not available. This makes it usable with JRuby. 30 | (Kramdown may become the only support markdown render in the future.) 31 | 32 | Changes: 33 | 34 | * Deprecate `load_path` array in favor of `paths` hash. 35 | * Support Kramdown as fallback Markdown renderer. 36 | * Add requirement optional field support to microformat importer. 37 | 38 | 39 | ## 0.1.0 / 2012-12-09 40 | 41 | Initial release of Indexer. 42 | 43 | Changes: 44 | 45 | * All of them ;) 46 | -------------------------------------------------------------------------------- /lib/indexer/components/conflict.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # The Conflict class models the name and versions of 4 | # packages that have know incompatibilities. 5 | # 6 | class Conflict < Model 7 | 8 | # Parse `data` into a Dependency instance. 9 | # 10 | # TODO: What about respond_to?(:to_str) for String, etc. 11 | def self.parse(data) 12 | case data 13 | when String 14 | parse_string(data) 15 | when Array 16 | parse_array(data) 17 | when Hash 18 | parse_hash(data) 19 | else 20 | raise(ValidationError, "Conflict") 21 | end 22 | end 23 | 24 | private 25 | 26 | # 27 | # 28 | def self.parse_string(data) 29 | name, version = data.split(/\s+/) 30 | new(:name=>name, :version=>version) 31 | end 32 | 33 | # 34 | # 35 | def self.parse_array(data) 36 | name, version = *data 37 | new(:name=>name, :version=>version) 38 | end 39 | 40 | # 41 | # 42 | def self.parse_hash(data) 43 | new(data) 44 | end 45 | 46 | public 47 | 48 | # 49 | # The name of the package that causes the conflict. 50 | # 51 | # Yea it's *ALWAYS* THEIR fault ;-) 52 | # 53 | attr :name 54 | 55 | # 56 | # Set the name of the package. 57 | # 58 | def name=(name) 59 | @data[:name] = name.to_s 60 | end 61 | 62 | # 63 | # The versions constraint of the conflicting package. 64 | # This is used when only certain versions of the package 65 | # are the problem. 66 | # 67 | attr_reader :version 68 | 69 | # 70 | # Set the version constraint. 71 | # 72 | def version=(version) 73 | @data[:version] = Version::Constraint.parse(version) 74 | end 75 | 76 | # 77 | # 78 | # 79 | def to_h 80 | {'name'=>name, 'version'=>version.to_s} 81 | end 82 | 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /lib/indexer/components/engine.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # The Engine class models the name and version of a 4 | # the language necessray to run the software. 5 | # 6 | class Engine < Model 7 | 8 | # Parse `data` into a Dependency instance. 9 | # 10 | # TODO: What about respond_to?(:to_str) for String, etc. 11 | def self.parse(data) 12 | case data 13 | when Engine 14 | data 15 | when String 16 | parse_string(data) 17 | when Array 18 | parse_array(data) 19 | when Hash 20 | parse_hash(data) 21 | else 22 | raise(ValidationError, "Engine") 23 | end 24 | end 25 | 26 | private 27 | 28 | # 29 | # 30 | def self.parse_string(data) 31 | name, version = data.split(/\s+/) 32 | new(:name=>name, :version=>version) 33 | end 34 | 35 | # 36 | # 37 | def self.parse_array(data) 38 | name, version = *data 39 | new(:name=>name, :version=>version) 40 | end 41 | 42 | # 43 | # 44 | def self.parse_hash(data) 45 | new(data) 46 | end 47 | 48 | public 49 | 50 | # 51 | # The name of the package that causes the conflict. 52 | # 53 | # Yea it's *ALWAYS* THEIR fault ;-) 54 | # 55 | attr :name 56 | 57 | # 58 | # Set the name of the package. 59 | # 60 | def name=(name) 61 | @data[:name] = name.to_s 62 | end 63 | 64 | # 65 | # The versions constraint of the conflicting package. 66 | # This is used when only certain versions of the package 67 | # are the problem. 68 | # 69 | attr_reader :version 70 | 71 | # 72 | # Set the version constraint. 73 | # 74 | def version=(version) 75 | @data[:version] = Version::Constraint.parse(version) 76 | end 77 | 78 | # 79 | # 80 | # 81 | def to_h 82 | {'name'=>name, 'version'=>version.to_s} 83 | end 84 | 85 | end 86 | 87 | end 88 | -------------------------------------------------------------------------------- /lib/indexer.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | # Name of this program. 3 | NAME = 'indexer' 4 | 5 | # Current stable revision of the specification (by year). 6 | REVISION = 2013 7 | 8 | # File name of locked metadata file. 9 | LOCK_FILE = '.index' 10 | 11 | # Default metadata file name for use by end-developer. 12 | USER_FILES = '{Index,Indexfile,Metadata}{,.rb,.yml,.yaml}' 13 | 14 | # Indexer library directory. 15 | LIBDIR = File.dirname(__FILE__) + '/indexer' 16 | 17 | # Indexer library directory. 18 | DATADIR = File.dirname(__FILE__) + '/../data/indexer' 19 | 20 | # Metadata from the project's `indexer.yml` index file. 21 | # This is used as a fallback for #const_missing. 22 | # 23 | # Returns [Hash] of metadata. 24 | def self.index 25 | @index ||= ( 26 | require 'yaml' 27 | dir = File.dirname(__FILE__) 28 | file = Dir[File.join(dir, "{#{NAME}.yml,../.index}")].first 29 | file ? YAML.load_file(file) : {} 30 | ) 31 | end 32 | 33 | # Project metadata via RubyGems, fallback to index file. 34 | # 35 | # TODO: The #to_s on the gemspec return value is a bit too simplistic. But how to fix? 36 | # The goal is reduce the value to a basic type (String, Hash, Array, Numeric). 37 | # 38 | def self.const_missing(const_name) 39 | name = const_name.to_s.downcase 40 | begin 41 | Gem.loaded_specs[NAME].send(name).to_s 42 | rescue StandardError 43 | index[name] || super(const_name) 44 | end 45 | end 46 | end 47 | 48 | require 'yaml' 49 | require 'time' 50 | 51 | require 'indexer/version/exceptions' 52 | require 'indexer/version/number' 53 | require 'indexer/version/constraint' 54 | 55 | require 'indexer/core_ext' 56 | require 'indexer/command' 57 | require 'indexer/error' 58 | require 'indexer/valid' 59 | require 'indexer/revision' 60 | 61 | require 'indexer/model' 62 | require 'indexer/components' 63 | require 'indexer/metadata' 64 | require 'indexer/importer' 65 | 66 | -------------------------------------------------------------------------------- /lib/indexer/gemfile.rb: -------------------------------------------------------------------------------- 1 | #module Indexer 2 | 3 | # Make sure Indexer is loaded. 4 | require 'indexer' unless defined?(Indexer) 5 | 6 | require 'bundler' 7 | 8 | # Bundler integration. 9 | # 10 | # This does not support Bundler's `:git` references 11 | # or `:require` option (at least not yet). 12 | # 13 | module Bundler 14 | 15 | # Mixin for Bundler::Dsl. 16 | # 17 | class Dsl 18 | 19 | # 20 | # Dynamically update a Gemfile from the `.index` file. Just call `index` 21 | # from your Gemfile. 22 | # 23 | # rubyfile 24 | # 25 | # This is analogous to the Gemfile's `gemspec` method. 26 | # 27 | def index 28 | spec = Indexer::Metadata.open 29 | spec.requirements.each do |req| 30 | next if req.external? 31 | gem(req.name, req.version, :group=>req.groups) 32 | end 33 | end 34 | 35 | # 36 | def metadata 37 | @metadata ||= Indexer::Metadata.new 38 | end 39 | 40 | # 41 | alias :_method_missing :method_missing 42 | 43 | # 44 | # Evaluating on the Builder instance, allows Ruby basic metadata 45 | # to be built via this method. 46 | # 47 | def method_missing(s, *a, &b) 48 | r = s.to_s.chomp('=') 49 | case a.size 50 | when 0 51 | if metadata.respond_to?(s) 52 | return metadata.__send__(s, &b) 53 | end 54 | when 1 55 | if metadata.respond_to?("#{r}=") 56 | return metadata.__send__("#{r}=", *a) 57 | end 58 | else 59 | if metadata.respond_to?("#{r}=") 60 | return metadata.__send__("#{r}=", a) 61 | end 62 | end 63 | 64 | _method_missing(s, *a, &b) 65 | #super(s, *a, &b) # if cases don't match-up 66 | end 67 | 68 | end 69 | 70 | end 71 | 72 | #end 73 | 74 | #::Bundler::Dsl.__send__(:include, Indexer::Bundler::Dsl) 75 | 76 | -------------------------------------------------------------------------------- /data/indexer/index.yesi: -------------------------------------------------------------------------------- 1 | --- 2 | HEADER-DIVISION: 3 | apply-tag: "!http://rubyworks.github.org/dotruby" 4 | description: 5 | Specification for canonical .ruby files. 6 | author: trans 7 | 8 | TEMPLATE-DIVISION: 9 | revision : PIC ZZ9; TAG !!int; REQ true 10 | name : PIC AW[29]; REQ true 11 | version : PIC ZZ9{.ZZ9}[5]; REX semver 12 | codename : PIC X[30] 13 | title : PIC X[30] 14 | date : PIC 9999-99-99{_99:99:99}[0,1] 15 | created : PIC 9999-99-99{_99:99:99}[0,1] 16 | summary : PIC X[79] 17 | description: PIC X[1000] 18 | authors: 19 | - name : PIC X[30] 20 | email : REX email 21 | url : REX url 22 | roles : 23 | - PIC X[30] 24 | suite: PIC X[30] 25 | organization: PIC X[30] 26 | copyrights: 27 | - holder : PIC X[30] 28 | year : PIC 9999{-9999}[0,1] 29 | license : PIC W[30] 30 | requirements: &r 31 | - name: PIC W[30] 32 | version: REX // 33 | groups: 34 | - PIC W[30] 35 | development: TAG !!bool 36 | optional: TAG !!bool 37 | engine: 38 | - name: PIC W[30] 39 | version: PIC W[30] 40 | platform: 41 | - PIC W[30] 42 | repository: 43 | name: PIC W[30] 44 | uri: REX uri 45 | scm: PIC W[15] 46 | dependencies: *r 47 | conflicts: 48 | - name: PIC W[30] 49 | version: PIC X[30] 50 | substitues: 51 | - PIC W[30] 52 | replaces: 53 | - PIC W[30] 54 | resources: 55 | - name: PIC X[30] 56 | uri: REX uri 57 | repositories: 58 | - name: PIC X[30] 59 | uri: REX uri 60 | scm: PIC W[30] 61 | load_path: 62 | - REX file; DEF 'lib' 63 | install_message: PIC X[1000] 64 | extra: {} 65 | 66 | CONSTRAINTS-DIVISION: {} 67 | 68 | -------------------------------------------------------------------------------- /demo/metadata/22_organizations.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#organizations 2 | 3 | The `organizations` field holds a list of the company, club, foundation, 4 | or programming groups involved with a project. For users of GitHub this 5 | will most likey be the GitHub organization in which a project is hosted. 6 | 7 | The Metadata class allows, of course, the canonical format. 8 | 9 | data = Indexer::Metadata.new 10 | 11 | data.organizations = [ 12 | { 'name' => 'Bobs Company', 13 | 'email' => 'bob@mail.com', 14 | 'roles' => ['sponsers'] 15 | }, 16 | { 'name' => 'Johns Club', 17 | 'email' => 'dlite@mail.com', 18 | } 19 | ] 20 | 21 | But it also can handle and Array of Arrays. The order of the inner array 22 | is `name, email, werbsite`. Only name is not optional. 23 | 24 | data.organizations = [ 25 | ['Bobs Company', 'bob@mail.com'], 26 | ['Johns Club', 'dlite@mail.com', 'http://foo.org'] 27 | ] 28 | 29 | It will also handle an Array of strings, parsing out the given parts. 30 | 31 | data.organizations = [ 32 | 'Bobs Company ', 33 | 'Johns Club http://foo.org' 34 | ] 35 | 36 | It does this via recognition that an email address would be in `<...>` 37 | brackets and URL validation for the website. 38 | 39 | It can even handle combination of the above. 40 | 41 | data.organizations = [ 42 | {'name' => 'Bobs Company', 'email' => 'bob@mail.com'}, 43 | ['Coco Chappel', ''], 44 | ['Johns Club ', 'http://foo.org'] 45 | ] 46 | 47 | data.organizations[0].name.assert == 'Bobs Company' 48 | data.organizations[0].email.assert == 'bob@mail.com' 49 | 50 | data.organizations[1].name.assert == 'Coco Chappel' 51 | data.organizations[1].email.assert == 'coco@mail.com' 52 | 53 | data.organizations[2].name.assert == 'Johns Club' 54 | data.organizations[2].email.assert == 'dlite@mail.com' 55 | data.organizations[2].website.assert == 'http://foo.org' 56 | 57 | 58 | -------------------------------------------------------------------------------- /demo/metadata/02_initialize.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#initialize 2 | 3 | ### Bare Instance 4 | 5 | A Metadata object can be created using the typical `#new` class method. 6 | 7 | metadata = Indexer::Metadata.new 8 | 9 | As we are testing current revision, so we can see that the revision is set 10 | to `Indexer::REVISION` which alays holds the current revision number, which 11 | is always the year the revision was froozen. 12 | 13 | metadata.revision.should == Indexer::REVISION 14 | 15 | In addition, certain attributes will have default values. 16 | 17 | metadata.copyrights.should == [] 18 | metadata.authors.should == [] 19 | metadata.requirements.should == [] 20 | metadata.dependencies.should == [] 21 | metadata.conflicts.should == [] 22 | metadata.repositories.should == [] 23 | #metadata.resources.should == [] 24 | 25 | metadata.paths.should == { 'lib' => ['lib'] } 26 | 27 | ### Metadata Arguments 28 | 29 | A Metadata object can be created with initial values by passing a metadata 30 | hash to the initializer. 31 | 32 | metadata = Indexer::Metadata.new(:name=>'foo', :version=>'0.1.2') 33 | 34 | metadata.name.should == 'foo' 35 | metadata.version.to_s.should == '0.1.2' 36 | 37 | Entries passed to the initializer are assigned via Spec's setters 38 | and are validated upon assignment, so no invalid values can get into the 39 | object's state, e.g. 40 | 41 | expect Indexer::ValidationError do 42 | Indexer::Metadata.new(:name=>1) 43 | end 44 | 45 | ### Metadata Block 46 | 47 | Metadata can also take a block which is yeilded on `self`. 48 | 49 | metadata = Indexer::Metadata.new do |m| 50 | m.name = 'foo' 51 | m.version = '0.1.2' 52 | end 53 | 54 | ### Initial Validity 55 | 56 | The only way for a Metadata object to be in an invalid state is 57 | by the creation of an instance without providing a `name` and `version`. 58 | Both name and version are required for a specification to be valid. 59 | 60 | metadata = Indexer::Metadata.new 61 | 62 | metadata.refute.valid? 63 | 64 | All other fields can be `nil` or the default values automatically assigned 65 | as shown above. 66 | 67 | -------------------------------------------------------------------------------- /work/deprecated/revisions/r2013/conflict.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module R2013 4 | 5 | # Conflict describes potential dependency incompatibilities. 6 | # 7 | module Conflict 8 | 9 | # Initialize new instance of Conflict. 10 | # 11 | def initialize(name, version=nil) 12 | self.name = name 13 | self.version = version 14 | end 15 | 16 | public 17 | 18 | # 19 | # The name of the package that causes the conflict. 20 | # 21 | # Yea it's *ALWAYS* THEIR fault ;-) 22 | # 23 | attr_reader :name 24 | 25 | # 26 | # Set the name of the package. 27 | # 28 | def name=(name) 29 | @name = name.to_s 30 | end 31 | 32 | # 33 | # The versions constraint of the conflicting package. 34 | # This is used when only certain versions of the package 35 | # are the problem. 36 | # 37 | attr_reader :version 38 | 39 | # 40 | # Set the version constraint. 41 | # 42 | def version=(version) 43 | @version = Version::Constraint.parse(version) 44 | end 45 | 46 | # 47 | def self.included(base) 48 | base.extend Parsing 49 | end 50 | 51 | module Parsing 52 | 53 | # Parse `data` into a Dependency instance. 54 | # 55 | # TODO: What about respond_to?(:to_str) for String, etc. 56 | def parse(data) 57 | case data 58 | when String 59 | parse_string(data) 60 | when Array 61 | new(*data) 62 | when Hash 63 | parse_hash(data) 64 | else 65 | raise(ValidationError, "Conflict") 66 | end 67 | end 68 | 69 | private 70 | 71 | # 72 | # 73 | def parse_string(data) 74 | name, version = data.split(/\s+/) 75 | new(name, version) 76 | end 77 | 78 | # 79 | # 80 | def parse_hash(data) 81 | name = data.delete('name') || data.delete(:name) 82 | version = data.delete('version') || data.delete(:version) 83 | new(name, version) 84 | end 85 | 86 | end 87 | 88 | end 89 | 90 | end 91 | 92 | end 93 | 94 | -------------------------------------------------------------------------------- /work/reference/plugin_support/rubygems.rb: -------------------------------------------------------------------------------- 1 | if defined?(Gem) 2 | 3 | module Gem 4 | 5 | # Search RubyGems for matching paths in current gem versions. 6 | # 7 | # For RubyGems older than version 1.7 (actually I don't know 8 | # the exact cut-off, so let me know if you discover otherwise) 9 | # then this search will return matches from ALL gems. 10 | # 11 | # For RubyGems 1.7+ it returns matches ONLY from active gems or 12 | # the latest versions of non-active gems. 13 | # 14 | # The later is proper functionality. But the API on the old version 15 | # of RubyGems is not condusive, and worse, now docs for it are hard 16 | # to find. 17 | # 18 | def self.search(match, options={}) 19 | return [] unless defined?(::Gem) 20 | if Gem::VERSION < '1.7' 21 | matches = Gem::find_files(match) 22 | else 23 | matches = [] 24 | Gem::Specification.current_specs.each do |spec| 25 | glob = File.join(spec.lib_dirs_glob, match) 26 | list = Dir[glob] #.map{ |f| f.untaint } 27 | matches.concat(list) 28 | end 29 | end 30 | matches.map{ |d| d.chomp('/') } 31 | end 32 | 33 | class Specification 34 | # Return a list of actives specs, or latest version if not active. 35 | # 36 | def self.current_specs 37 | named = Hash.new{|h,k| h[k] = [] } 38 | each{ |spec| named[spec.name] << spec } 39 | list = [] 40 | named.each do |name, vers| 41 | if spec = vers.find{ |s| s.activated? } 42 | list << spec 43 | else 44 | spec = vers.max{ |a,b| a.version <=> b.version } 45 | list << spec 46 | end 47 | end 48 | return list 49 | end 50 | 51 | # Return full path of requireable file given relative file name. 52 | # Returns +nil+ if there is no requirable file found by that name. 53 | # 54 | def find_requirable_file(file) 55 | root = full_gem_path 56 | 57 | require_paths.each do |lib| 58 | base = "#{root}/#{lib}/#{file}" 59 | Gem.suffixes.each do |suf| 60 | path = "#{base}#{suf}" 61 | return path if File.file? path 62 | end 63 | end 64 | 65 | return nil 66 | end 67 | end 68 | 69 | end 70 | 71 | end 72 | -------------------------------------------------------------------------------- /work/deprecated/cli/gemspec.rb: -------------------------------------------------------------------------------- 1 | require 'indexer/cli' 2 | require 'fileutils' 3 | 4 | module Indexer 5 | 6 | # 7 | # 8 | # 9 | class CLI::Gemspec < CLI 10 | 11 | # 12 | attr_accessor :force 13 | 14 | # 15 | attr_accessor :static 16 | 17 | # 18 | attr_accessor :which 19 | 20 | # 21 | def initialize 22 | @force = false 23 | @static = false 24 | @which = REVISION 25 | end 26 | 27 | # 28 | def parse(opt) 29 | opt.banner = "Usage: #{$0} [options] [name]" 30 | opt.separator "" 31 | opt.separator "Create a gemspec file. If name is given then the file\n" + 32 | "will be called `name.gemspec', otherwise just `.gemspec'." 33 | opt.separator "" 34 | opt.on('--force', '-f', "overwrite gemspec if present") do 35 | force = true 36 | end 37 | opt.on('--static', '-s', "provide a static gemspec") do 38 | static = true 39 | end 40 | opt.on('--revision', '-r INT', "specification revison") do |i| 41 | which = i.to_i 42 | end 43 | end 44 | 45 | # 46 | def run 47 | file = argv.shift 48 | 49 | if file 50 | if file.extname(file) != '.gemspec' 51 | warn "gemspec file without .gemspec extension" 52 | end 53 | else 54 | # TODO: look for pre-existent gemspec, but to do that right we should get 55 | # the name from the .index file if it eixts. 56 | file = '.gemspec' # Dir['{,*}.gemspec'].first || '.gemspec' 57 | end 58 | 59 | #lib_file = File.join(DIR, "v#{which}", "gemspec.rb") 60 | 61 | if File.exist?(file) && !force 62 | $stderr.puts "`#{file}' already exists, use -f/--force to overwrite." 63 | exit -1 64 | end 65 | 66 | code = V[which]::GemspecExporter.source_code 67 | 68 | File.open(file, 'w') do |f| 69 | f << code 70 | f << "\nIndexer::V#{which}::Gemspec.instance" 71 | end 72 | 73 | if static 74 | spec = eval(File.read(file), CleanBinding.new, file) 75 | File.open(file, 'w') do |f| 76 | f << spec.to_yaml 77 | end 78 | end 79 | end 80 | 81 | # 82 | # 83 | # 84 | module CleanBinding 85 | def self.new 86 | binding 87 | end 88 | end 89 | 90 | end 91 | 92 | end 93 | -------------------------------------------------------------------------------- /demo/metadata/15_requirements.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#requirements 2 | 3 | The `requirements` field is a list of packages which are required 4 | to use this application/library. 5 | 6 | The canonical field value is an Array of Hashes. The format of the 7 | `requirements` fields is: 8 | 9 | requirements: 10 | - name: foo 11 | version: 1.0+ 12 | development: false 13 | groups: 14 | - doc 15 | repository: 16 | type: git 17 | url: http://foo.com/foo.git 18 | - name: bar 19 | version: ~> 2.1 20 | : 21 | : 22 | 23 | Only `name` and `version` are required sub-fields. 24 | 25 | spec = Indexer::Metadata.new 26 | 27 | spec.requirements = [ 28 | {'name'=>'foo', 'version'=>'1.0+'}, 29 | {'name'=>'bar', 'version'=>'1.1~', 'development'=>true, 'groups'=>['doc']} 30 | ] 31 | 32 | The Metadata class allows some flexibilty in defining requirements. For instance each 33 | entry can be a string in the format of `name [constraint] [(group...)]`. 34 | 35 | spec = Indexer::Metadata.new 36 | 37 | spec.requirements = [ 38 | 'foo >= 1.0', 39 | 'bar 1.1~ (doc)', 40 | 'baz' 41 | ] 42 | 43 | In the case of a string, if any group is given, the requirement is taken to 44 | be a development requirement. 45 | 46 | spec.requirements[1].assert.development? 47 | 48 | The exact interface to `Requirement.new` is `(name, specifics)` so 49 | array elements of this king can be used as well. 50 | 51 | spec = Indexer::Metadata.new 52 | 53 | spec.requirements = [ 54 | ['foo', {'version'=>'>= 1.0'} ], 55 | ['bar', {'version'=>'1.1~', 'groups'=>['doc']} ] 56 | ] 57 | 58 | And combinations of all these can be used. 59 | 60 | spec = Indexer::Metadata.new 61 | 62 | spec.requirements = [ 63 | 'foo =1.2.3', 64 | ['bar', {'version'=>'1.1~', 'groups'=>['doc']} ] 65 | ] 66 | 67 | ### Hash 68 | 69 | For simple requirements a hash can also be used. 70 | 71 | spec = Indexer::Metadata.new 72 | 73 | spec.requirements = { 74 | 'foo' => '>= 1.0', 75 | 'bar' => '1.1~' 76 | } 77 | 78 | More complex hashes can be used as well. 79 | 80 | spec = Indexer::Metadata.new 81 | 82 | spec.requirements = { 83 | 'foo' => {'version'=>'>= 1.0'}, 84 | 'bar' => {'version'=>'1.1~', 'groups'=>['doc']} 85 | } 86 | 87 | -------------------------------------------------------------------------------- /demo/version/number/06_cmp.md: -------------------------------------------------------------------------------- 1 | ## Version::Number#<=> 2 | 3 | The inclusion of Comparable mixin into the Version class 4 | along with the definition of #<=>, provides the class with 5 | all the common inequality methods. 6 | 7 | ### Equality 8 | 9 | check do |n1, n2| 10 | v1 = Indexer::Version::Number.parse(n1) 11 | v2 = Indexer::Version::Number.parse(n2) 12 | v1 == v2 13 | end 14 | 15 | ok '1.0.0', '1.0.0' 16 | ok '1.0.0', '1.0' 17 | 18 | ### Greater-Than 19 | 20 | check do |n1, n2| 21 | v1 = Indexer::Version::Number.parse(n1) 22 | v2 = Indexer::Version::Number.parse(n2) 23 | v1 > v2 24 | end 25 | 26 | ok '1.0.1', '1.0.0' 27 | ok '1.1.0', '1.0.0' 28 | ok '2.0.0', '1.0.0' 29 | ok '1.1', '1.0.0' 30 | ok '1.0.0', '1.0.0.rc.3' 31 | 32 | no '1.0.0', '1.0.0' 33 | 34 | ### Less-Than 35 | 36 | check do |n1, n2| 37 | v1 = Indexer::Version::Number.parse(n1) 38 | v2 = Indexer::Version::Number.parse(n2) 39 | v1 < v2 40 | end 41 | 42 | ok '1.0.1', '1.0.2' 43 | ok '1.1.0', '1.2.0' 44 | ok '2.0.0', '2.1.0' 45 | ok '1.2.3', '2.0.0' 46 | ok '1.2.3', '2.0' 47 | ok '1.1', '1.2.0' 48 | ok '1.0.0.rc.2', '1.0.0' 49 | 50 | no '1.0.0', '1.0.0' 51 | 52 | ### Greater-Than-Or-Equal 53 | 54 | check do |n1, n2| 55 | v1 = Indexer::Version::Number.parse(n1) 56 | v2 = Indexer::Version::Number.parse(n2) 57 | v1 >= v2 58 | end 59 | 60 | ok '1.0.1', '1.0.0' 61 | ok '1.1.0', '1.0.0' 62 | ok '2.0.0', '1.0.0' 63 | ok '1.1', '1.0.0' 64 | ok '1.0.0', '1.0.0.rc.3' 65 | ok '1.0.0', '1.0.0' 66 | 67 | ### Less-Than-Or-Equal 68 | 69 | check do |n1, n2| 70 | v1 = Indexer::Version::Number.parse(n1) 71 | v2 = Indexer::Version::Number.parse(n2) 72 | v1 <= v2 73 | end 74 | 75 | ok '1.0.1', '1.0.2' 76 | ok '1.1.0', '1.2.0' 77 | ok '2.0.0', '2.1.0' 78 | ok '1.1', '1.2.0' 79 | ok '1.0.0.rc.2', '1.0.0' 80 | ok '1.0.0', '1.0.0' 81 | 82 | ### Approximate Constraint 83 | 84 | check do |n1, n2| 85 | v1 = Indexer::Version::Number.parse(n1) 86 | v2 = Indexer::Version::Number.parse(n2) 87 | v1 =~ v2 88 | end 89 | 90 | ok '1.0.0', '1.0' 91 | ok '1.0.2', '1.0' 92 | ok '2.1.1', '2.1' 93 | 94 | no '1.1.0', '1.2.0' 95 | no '1.1.0', '2.0' 96 | 97 | # TODO: Should this be ok ? 98 | no '1.0.0.rc.2', '1.0' 99 | 100 | -------------------------------------------------------------------------------- /lib/indexer/components/copyright.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Copyright class models a copyright holer, the year of copyright 4 | # and the accosiated license. 5 | # 6 | class Copyright < Model 7 | 8 | # Parse copyright entry. 9 | # 10 | def self.parse(copyright, default_license=nil) 11 | case copyright 12 | when Array 13 | year, holder, license = *copyright 14 | when Hash 15 | year = copyright['year'] || copyright[:year] 16 | holder = copyright['holder'] || copyright[:holder] 17 | license = copyright['license'] || copyright[:license] 18 | when String 19 | case copyright 20 | when /(\d\d\d\d)\s+(.*?)\s*\((.*?)\)$/ 21 | year, holder, license = $1, $2, $3 22 | when /(\d\d\d\d)\s+(.*?)\s*$/ 23 | year, holder, license = $1, $2, nil 24 | when /(opyright|\(c\))(.*?)\s*\((.*?)\)$/ 25 | year, holder, license = nil, $1, $2 26 | when /(opyright|\(c\))(.*?)\s*$/ 27 | year, holder, license = nil, $1, nil 28 | end 29 | else 30 | raise ValidationError, "copyright" 31 | end 32 | license = license || default_license 33 | new(:holder=>holder, :year=>year, :license=>license) 34 | end 35 | 36 | # 37 | def initialize(data) 38 | super(data) 39 | raise(ValidationError, "copyright must have a holder") unless holder 40 | end 41 | 42 | # 43 | attr :year 44 | 45 | # 46 | attr :holder 47 | 48 | # 49 | attr :license 50 | 51 | # 52 | def year=(year) 53 | Valid.copyright_year!(year, "copyright.year") 54 | @data[:year] = year 55 | end 56 | 57 | # 58 | def holder=(holder) 59 | Valid.oneline!(holder, "copyright.holder") 60 | @data[:holder] = holder 61 | end 62 | 63 | # 64 | def license=(license) 65 | if license.nil? 66 | @data.delete(:license) 67 | else 68 | Valid.oneline!(license, "copyright.license") 69 | @data[:license] = license 70 | end 71 | end 72 | 73 | # 74 | # Standard copyright stamp. 75 | # 76 | def to_s 77 | s = ["Copyright (c)"] 78 | s << year if year 79 | s << holder 80 | s << "(#{license})" if license 81 | s.join(' ') + ". All Rights Reserved." 82 | end 83 | 84 | # 85 | def to_h 86 | h = {} 87 | h['holder'] = holder 88 | h['year'] = year if year 89 | h['license'] = license if license 90 | h 91 | end 92 | 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /lib/indexer/core_ext/hash/rekey.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | 3 | unless method_defined?(:rekey) 4 | 5 | # Rekey a hash: 6 | # 7 | # rekey() 8 | # rekey(from_key => to_key, ...) 9 | # rekey{|from_key| to_key} 10 | # rekey{|from_key, value| to_key} 11 | # 12 | # If a key map is given, then the first key is changed to the second key. 13 | # 14 | # foo = { :a=>1, :b=>2 } 15 | # foo.rekey(:a=>'a') #=> { 'a'=>1, :b=>2 } 16 | # foo.rekey(:b=>:x) #=> { :a =>1, :x=>2 } 17 | # foo.rekey('foo'=>'bar') #=> { :a =>1, :b=>2 } 18 | # 19 | # If a block is given, converts all keys in the Hash accroding to the 20 | # given block procedure. If the block returns +NA+ for a given key, 21 | # then that key will be left intact. 22 | # 23 | # foo = { :name=>'Gavin', :wife=>:Lisa } 24 | # foo.rekey{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } 25 | # foo #=> { :name =>"Gavin", :wife=>:Lisa } 26 | # 27 | # If no key map or block is given, then all keys are converted 28 | # to Symbols. 29 | # 30 | # Note that if both a +key_map+ and a block are given, the +key_map+ is 31 | # applied first then the block. 32 | # 33 | # CREDIT: Trans, Gavin Kistner 34 | 35 | def rekey(key_map=nil, &block) 36 | if !(key_map or block) 37 | block = lambda{|k| k.to_sym} 38 | end 39 | 40 | key_map ||= {} 41 | 42 | hash = dup.replace({}) # to keep default_proc 43 | 44 | (keys - key_map.keys).each do |key| 45 | hash[key] = self[key] 46 | end 47 | 48 | key_map.each do |from, to| 49 | hash[to] = self[from] if key?(from) 50 | end 51 | 52 | if block 53 | hash2 = dup.replace({}) 54 | case block.arity 55 | when 2 # TODO: is this condition needed? 56 | hash.each do |k, v| 57 | nk = block.call(k,v) 58 | nk = (nk ? nk : k) 59 | hash2[nk] = v 60 | end 61 | else 62 | hash.each do |k, v| 63 | nk = block.call(k) 64 | nk = (nk ? nk : k) 65 | hash2[nk] = v 66 | end 67 | end 68 | else 69 | hash2 = hash 70 | end 71 | 72 | hash2 73 | end 74 | 75 | # Synonym for Hash#rekey, but modifies the receiver in place (and returns it). 76 | # 77 | # foo = { :name=>'Gavin', :wife=>:Lisa } 78 | # foo.rekey!{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } 79 | # foo #=> { "name"=>"Gavin", "wife"=>:Lisa } 80 | # 81 | # CREDIT: Trans, Gavin Kistner 82 | 83 | def rekey!(key_map=nil, &block) 84 | replace(rekey(key_map, &block)) 85 | end 86 | 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /work/deprecated/revisions/r2013/copyright.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module R2013 4 | 5 | # Copyright models a copyright holer, the year of copyright 6 | # and the accosiated license. 7 | # 8 | module Copyright 9 | 10 | # 11 | def initialize(holder, year=nil, license=nil) 12 | self.holder = holder 13 | self.year = year if year 14 | self.license = license if license 15 | end 16 | 17 | # 18 | attr :year 19 | 20 | # 21 | attr :holder 22 | 23 | # 24 | attr :license 25 | 26 | # 27 | def year=(year) 28 | Valid.copyright_year!(year, "copyright.year") 29 | @year = year 30 | end 31 | 32 | # 33 | def holder=(holder) 34 | Valid.oneline!(holder, "copyright.holder") 35 | @holder = holder 36 | end 37 | 38 | # 39 | def license=(license) 40 | Valid.oneline!(license, "copyright.license") 41 | @license = license 42 | end 43 | 44 | # Standard copyright stamp. 45 | # 46 | def to_s 47 | s = ["Copyright (c)"] 48 | s << year if year 49 | s << holder 50 | s << "(#{license})" if license 51 | s.join(' ') + ". All Rights Reserved." 52 | end 53 | 54 | # 55 | def to_h 56 | h = {} 57 | h['holder'] = holder 58 | h['year'] = year if year 59 | h['license'] = license if license 60 | h 61 | end 62 | 63 | # 64 | def self.included(base) 65 | base.extend Parsing 66 | end 67 | 68 | # 69 | # 70 | module Parsing 71 | 72 | # Parse copyright entry. 73 | # 74 | def parse(copyright, default_license=nil) 75 | case copyright 76 | when Array 77 | year, holder, license = *copyright 78 | when Hash 79 | year = copyright['year'] || copyright[:year] 80 | holder = copyright['holder'] || copyright[:holder] 81 | license = copyright['license'] || copyright[:license] 82 | when String 83 | case copyright 84 | when /(\d\d\d\d)\s+(.*?)\s*\((.*?)\)$/ 85 | year, holder, license = $1, $2, $3 86 | when /(\d\d\d\d)\s+(.*?)\s*$/ 87 | year, holder, license = $1, $2, nil 88 | when /(opyright|\(c\))(.*?)\s*\((.*?)\)$/ 89 | year, holder, license = nil, $1, $2 90 | when /(opyright|\(c\))(.*?)\s*$/ 91 | year, holder, license = nil, $1, nil 92 | end 93 | else 94 | raise ValidationError, "copyright" 95 | end 96 | new(holder, year, license || default_license) 97 | end 98 | 99 | end #module Parsing 100 | 101 | end #module Copyright 102 | 103 | end #module R2013 104 | 105 | end #module Indexer 106 | -------------------------------------------------------------------------------- /lib/indexer/components/organization.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Organization is used to model companies involved in project. 4 | # 5 | class Organization < Model 6 | 7 | # Parse `entry` and create Organization object. 8 | def self.parse(entry) 9 | case entry 10 | when Organization 11 | entry 12 | when String 13 | parse_string(entry) 14 | when Array 15 | parse_array(entry) 16 | when Hash 17 | new(entry) 18 | end 19 | end 20 | 21 | # 22 | def self.parse_array(array) 23 | data = {} 24 | array.each do |value| 25 | v = value.strip 26 | case v 27 | when /^(.*?)\s*\<(.*?@.*?)\>/ 28 | data[:name] = $1 unless $1.empty? 29 | data[:email] = $2 30 | when /^(http|https)\:/ 31 | data[:website] = v 32 | when /\@/ 33 | data[:email] = v 34 | else 35 | data[:name] = v 36 | end 37 | end 38 | new(data) 39 | end 40 | 41 | # 42 | def self.parse_string(string) 43 | if md = /(.*?)\s*\<(.*?)\>/.match(string) 44 | new(:name=>md[1], :email=>md[2]) 45 | else 46 | new(:name=>string) 47 | end 48 | end 49 | 50 | # 51 | def initialize(data) 52 | super(data) 53 | #raise ArgumentError, "person must have a name" unless name 54 | end 55 | 56 | # 57 | def initialize_attributes 58 | @data = { 59 | :roles => [] 60 | } 61 | end 62 | 63 | # 64 | attr :name 65 | 66 | # 67 | def name=(name) 68 | Valid.oneline!(name, :name) 69 | @data[:name] = name 70 | end 71 | 72 | # 73 | attr :email 74 | 75 | # 76 | def email=(email) 77 | Valid.email!(email, :email) 78 | @data[:email] = email 79 | end 80 | 81 | # 82 | attr :website 83 | 84 | # 85 | def website=(website) 86 | Valid.url!(website, :website) 87 | @data[:website] = website 88 | end 89 | 90 | # List of roles the person plays in the project. 91 | # This can be any string or array of strings. 92 | attr :roles 93 | 94 | # 95 | def roles=(roles) 96 | @data[:roles] = ( 97 | r = [roles].flatten 98 | r.each{ |x| Valid.oneline?(x) } 99 | r 100 | ) 101 | end 102 | 103 | # Signular term for #roles can be used as well. 104 | alias :role :roles 105 | alias :role= :roles= 106 | 107 | # Group is a synonym for role. 108 | alias :group :roles 109 | alias :group= :roles= 110 | 111 | # Team is a common synonym for role too. 112 | alias :team :roles 113 | alias :teams= :roles= 114 | 115 | # Convert to simple Hash represepentation. 116 | def to_h 117 | h = {} 118 | h['name'] = name 119 | h['email'] = email if email 120 | h['website'] = website if website 121 | h['roles'] = roles if not roles.empty? 122 | h 123 | end 124 | 125 | # CONSIDER: Only name has to be equal? 126 | def ==(other) 127 | return false unless Organization === other 128 | return false unless name == other.name 129 | return true 130 | end 131 | end 132 | 133 | end 134 | -------------------------------------------------------------------------------- /lib/indexer/components/author.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Author is used to model Authors and Maintainers. 4 | # 5 | # TODO: Should Author have an `organization` field. If so 6 | # is it a map with `name` and `website` fields? 7 | # 8 | # TODO: Should we have `team` field (think Github)? 9 | # 10 | class Author < Model 11 | 12 | # Parse `entry` and create Author object. 13 | def self.parse(entry) 14 | case entry 15 | when Author 16 | entry 17 | when String 18 | parse_string(entry) 19 | when Array 20 | parse_array(entry) 21 | when Hash 22 | new(entry) 23 | end 24 | end 25 | 26 | # 27 | def self.parse_array(array) 28 | data = {} 29 | array.each do |value| 30 | v = value.strip 31 | case v 32 | when /^(.*?)\s*\<(.*?@.*?)\>/ 33 | data[:name] = $1 unless $1.empty? 34 | data[:email] = $2 35 | when /^(http|https)\:/ 36 | data[:website] = v 37 | when /\@/ 38 | data[:email] = v 39 | else 40 | data[:name] = v 41 | end 42 | end 43 | new(data) 44 | end 45 | 46 | # 47 | def self.parse_string(string) 48 | if md = /(.*?)\s*\<(.*?)\>/.match(string) 49 | new(:name=>md[1], :email=>md[2]) 50 | else 51 | new(:name=>string) 52 | end 53 | end 54 | 55 | # 56 | def initialize(data) 57 | super(data) 58 | #raise ArgumentError, "person must have a name" unless name 59 | end 60 | 61 | # 62 | def initialize_attributes 63 | @data = { 64 | :roles => [] 65 | } 66 | end 67 | 68 | # 69 | attr :name 70 | 71 | # 72 | def name=(name) 73 | Valid.oneline!(name, :name) 74 | @data[:name] = name 75 | end 76 | 77 | # 78 | attr :email 79 | 80 | # 81 | def email=(email) 82 | Valid.email!(email, :email) 83 | @data[:email] = email 84 | end 85 | 86 | # 87 | attr :website 88 | 89 | # 90 | def website=(website) 91 | Valid.url!(website, :website) 92 | @data[:website] = website 93 | end 94 | 95 | # 96 | attr :team 97 | 98 | # TODO: validate team 99 | def team=(team) 100 | @data[:team] = team 101 | end 102 | 103 | # List of roles the person plays in the project. 104 | # This can be any string or array of strings. 105 | attr :roles 106 | 107 | # 108 | def roles=(role) 109 | @data[:roles] = ( 110 | r = [role].flatten 111 | r.each{ |x| Valid.oneline?(x) } 112 | r 113 | ) 114 | end 115 | 116 | alias :role :roles 117 | alias :role= :roles= 118 | 119 | alias :group :roles 120 | alias :group= :roles= 121 | 122 | # 123 | def to_h 124 | h = {} 125 | h['name'] = name 126 | h['email'] = email if email 127 | h['website'] = website if website 128 | h['roles'] = roles if not roles.empty? 129 | h 130 | end 131 | 132 | # CONSIDE: Only name has to be equal? 133 | def ==(other) 134 | return false unless Author === other 135 | return false unless name == other.name 136 | return true 137 | end 138 | end 139 | 140 | end 141 | -------------------------------------------------------------------------------- /work/deprecated/gemfile_generator/generate_gemfile.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module CLI 4 | 5 | # 6 | # Command line interface for `rumble gemfile` command. 7 | # 8 | def gemfile(*argv) 9 | force = false 10 | static = false 11 | 12 | require 'optparse' 13 | require 'fileutils' 14 | 15 | # TODO: techincally this is not the correct way to find this file. we can 16 | # either use rbconfig.rb or move it to lib along with this code 17 | file = File.expand_path(File.dirname(__FILE__) + '/../data/indexer/example.gemfile') 18 | 19 | OptionParser.new do |opt| 20 | opt.banner = "Usage: #{$0} [name]" 21 | opt.separator "" 22 | opt.separator "Create a Gemfile." 23 | opt.separator "" 24 | opt.on('--force', '-f', "overwrite Gemfile if present") do 25 | force = true 26 | end 27 | opt.on('--static', '-s', "provide a static Gemfile") do 28 | static = true 29 | end 30 | opt.on_tail('--debug', '-D', "display debugging information") do 31 | $DEBUG = true 32 | end 33 | opt.on_tail('--help', '-h', "display this help message") do 34 | puts opt 35 | exit 0 36 | end 37 | end.parse!(ARGV) 38 | 39 | name = ARGV.shift 40 | 41 | if name 42 | destination = name 43 | else 44 | destination = 'Gemfile' 45 | end 46 | 47 | if File.exist?(destination) && !force 48 | $stderr.puts "Gemfile already exists, use -f/--force to overwrite." 49 | else 50 | begin 51 | if static 52 | save_gemfile 53 | else 54 | FileUtils.cp(file, destination) 55 | end 56 | rescue Exception => error 57 | puts error 58 | raise error if $DEBUG 59 | end 60 | end 61 | end 62 | 63 | # 64 | # Save Gemfile based on requirements in `.ruby`. If a `Gemfile` already 65 | # exists it will look for a special clause to insert the script. 66 | # 67 | # # .ruby 68 | # ... 69 | # # end .ruby 70 | # 71 | # The clause will be replaced by the generated gemfile script. This 72 | # allows the file to be augemented manually. 73 | # 74 | # @todo Maybe organize into group blocks. 75 | # 76 | def self.save_gemfile(file='Gemfile') 77 | text = gemfile_script 78 | 79 | if File.exist?('Gemfile') 80 | gemfile_text = File.read('Gemfile') 81 | if md = /^# \.index.*?^# end \.index/m.match(gemfile_text) 82 | gemfile_text.sub!(md[0], text) 83 | else 84 | gemfile_text << "\n\n" << text 85 | end 86 | else 87 | gemfile_text = text 88 | end 89 | 90 | File.open(file, 'w') do |f| 91 | f << gemfile_text 92 | end 93 | end 94 | 95 | # 96 | # Put together a Gemfile script based in requirements given in a 97 | # a project's `.index` file. 98 | # 99 | # @return [String] Gemfile script 100 | # 101 | def self.gemfile_script 102 | spec = Spec.find 103 | 104 | script = ['# .index'] 105 | spec.requirements.each do |name, req| 106 | script << "gem %s, %s, :group=>%s" % [req.name.inspect, req.version.to_s,inspect, req.groups.inspect] 107 | end 108 | script << '# end .index' 109 | 110 | script.join("\n") 111 | end 112 | 113 | end 114 | 115 | end 116 | -------------------------------------------------------------------------------- /lib/indexer/version/constraint.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module Version 4 | 5 | # The Constraint class models a single version equality or inequality. 6 | # It consists of the operator and the version number. 7 | #-- 8 | # TODO: Please improve me! 9 | # 10 | # TODO: This should ultimately replace the class methods of Version::Number. 11 | # 12 | # TODO: Do we need to support version "from-to" spans ? 13 | #++ 14 | class Constraint 15 | 16 | # 17 | def self.parse(constraint) 18 | new(constraint) 19 | end 20 | 21 | # 22 | def self.[](operator, number) 23 | new([operator, number]) 24 | end 25 | 26 | # 27 | def initialize(constraint) 28 | @operator, @number = parse(constraint || '0+') 29 | 30 | case constraint 31 | when Array 32 | @stamp = "%s %s" % [@operator, @number] 33 | when String 34 | constraint = "0+" if constraint.strip == "" 35 | @stamp = constraint 36 | end 37 | end 38 | 39 | # Constraint operator. 40 | attr :operator 41 | 42 | # Verison number. 43 | attr :number 44 | 45 | # 46 | def to_s 47 | @stamp 48 | end 49 | 50 | # Converts the version into a constraint string recognizable 51 | # by RubyGems. 52 | #-- 53 | # TODO: Better name Constraint#to_s2. 54 | #++ 55 | def to_gem_version 56 | op = (operator == '=~' ? '~>' : operator) 57 | "%s %s" % [op, number] 58 | end 59 | 60 | # Convert constraint to Proc object which can be 61 | # used to test a version number. 62 | def to_proc 63 | lambda do |v| 64 | n = Version::Number.parse(v) 65 | n.send(operator, number) 66 | end 67 | end 68 | 69 | private 70 | 71 | # 72 | def parse(constraint) 73 | case constraint 74 | when Array 75 | op, num = constraint 76 | when "" 77 | op, val = ">=", "0" 78 | when /^(.*?)\~$/ 79 | op, val = "=~", $1 80 | when /^(.*?)\+$/ 81 | op, val = ">=", $1 82 | when /^(.*?)\-$/ 83 | op, val = "<", $1 84 | when /^(=~|~>|<=|>=|==|=|<|>)?\s*(\d+(:?[-.]\w+)*)$/ 85 | if op = $1 86 | op = '=~' if op == '~>' 87 | op = '==' if op == '=' 88 | val = $2.split(/\W+/) 89 | else 90 | op = '==' 91 | val = constraint.split(/\W+/) 92 | end 93 | else 94 | raise ArgumentError #constraint.split(/\s+/) 95 | end 96 | return op, Version::Number.new(*val) 97 | end 98 | 99 | # Parse package entry into name and version constraint. 100 | #def parse(package) 101 | # parts = package.strip.split(/\s+/) 102 | # name = parts.shift 103 | # vers = parts.empty? ? nil : parts.join(' ') 104 | # [name, vers] 105 | #end 106 | 107 | public 108 | 109 | # Parses a string constraint returning the operation as a lambda. 110 | def self.constraint_lambda(constraint) 111 | new(constraint).to_proc 112 | end 113 | 114 | # Parses a string constraint returning the operator and value. 115 | def self.parse_constraint(constraint) 116 | c = new(constraint) 117 | return c.operator, c.number 118 | end 119 | 120 | end 121 | 122 | end 123 | 124 | end 125 | -------------------------------------------------------------------------------- /demo/metadata/23_copyrights.md: -------------------------------------------------------------------------------- 1 | ## Indexer::Metadata#copyright 2 | 3 | The `copyrights` field is a list of copyright and licens information. 4 | 5 | spec = Indexer::Metadata.new 6 | 7 | The copyright list is canonically a mapping. 8 | 9 | spec.copyrights = [ 10 | { 'year' => '2010', 11 | 'holder' => 'T. Bone Willy', 12 | 'license' => 'MIT' 13 | }, 14 | { 'year' => '2011', 15 | 'holder' => 'J. Horn Silly', 16 | 'license' => 'GPL-3.0' 17 | } 18 | ] 19 | 20 | The copyrights can also be given as strings. 21 | 22 | spec.copyrights = [ 23 | "Copyright (c) 2010 T. Bone Willy (MIT)", 24 | "Copyright (c) 2010 J. Horn Silly (GPL-3.0)" 25 | ] 26 | 27 | Or as a single string, for which there is a singular alias. 28 | 29 | spec.copyright = "Copyright (c) 2010 T. Bone Willy" 30 | spec.copyrights.first.year.should == '2010' 31 | 32 | But the plural method also can handle this form, even if it reads 33 | a bit oddly. 34 | 35 | spec.copyrights = "Copyright (c) 2010 T. Bone Willy" 36 | spec.copyrights.first.year.should == '2010' 37 | 38 | A single hash can be passed as well. 39 | 40 | spec.copyrights = { 41 | 'year' => '2010', 42 | 'holder' => 'T. Bone Willy', 43 | 'license' => 'MIT' 44 | } 45 | spec.copyrights.first.year.should == '2010' 46 | spec.copyrights.first.license.should == 'MIT' 47 | 48 | It can also be given as an array of three element arrays. 49 | 50 | spec.copyrights = [ 51 | ["2010", "T. Bone Willy", "BSD-2-Clause"] 52 | ] 53 | 54 | spec.copyrights[0].year.should == "2010" 55 | spec.copyrights[0].holder.should == "T. Bone Willy" 56 | spec.copyrights[0].license.should == "BSD-2-Clause" 57 | 58 | The Array entry form can be used as a single assigment 59 | on the singular method. 60 | 61 | spec.copyright = ["2010", "T. Bone Willy", "BSD-2-Clause"] 62 | 63 | spec.copyrights[0].year.should == "2010" 64 | spec.copyrights[0].holder.should == "T. Bone Willy" 65 | spec.copyrights[0].license.should == "BSD-2-Clause" 66 | 67 | But not the plural. 68 | 69 | expect Indexer::ValidationError do 70 | spec.copyrights = ["2010", "T. Bone Willy", "BSD-2-Clause"] 71 | end 72 | 73 | NOTE: Maybe in the future a more intelligent parser could handle this. 74 | 75 | But it can't be any other value. 76 | 77 | no 100 78 | no :symbol 79 | no Object.new 80 | 81 | Each copyright entry can be convert to a standard copyright _stamp_ using 82 | the `#to_s` method. 83 | 84 | spec.copyrights = [ 85 | ["2010", "T. Bone Willy", "BSD-2-Clause"], 86 | ["2011", "J. Horn Silly", "GPL-3.0"] 87 | ] 88 | 89 | stamp = spec.copyrights.first.to_s 90 | 91 | stamp #=> "Copyright (c) 2010 T. Bone Willy (BSD-2-Clause). All Rights Reserved." 92 | 93 | The singular method #copyright will output a complete copyright statement. 94 | 95 | statement = spec.copyright 96 | 97 | statement.should = \ 98 | "Copyright (c) 2010 T. Bone Willy (BSD-2-Clause). All Rights Reserved.\n" + 99 | "Copyright (c) 2011 J. Horn Silly (GPL-3.0). All Rights Reserved." 100 | 101 | A canonical hash can be produced using #to_h. 102 | 103 | spec.copyrights[0].to_h.should == { 104 | 'year' => '2010', 105 | 'holder' => 'T. Bone Willy', 106 | 'license' => 'BSD-2-Clause' 107 | } 108 | 109 | The usecase for this field is to output a copyright notice for a command 110 | line `--version` inquery, or on a pop-up About window. 111 | 112 | -------------------------------------------------------------------------------- /lib/indexer/components/repository.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # The Repository class models a packages SCM repository location. 4 | # It consists of two parts, the `scm` type of repository, it's `url`. 5 | # 6 | class Repository < Model 7 | 8 | # Parse `data` returning a new Repository instance. 9 | # 10 | # @param data [String,Array,Array,Hash] 11 | # Repository information. 12 | # 13 | # @return [Repository] repository instance 14 | #-- 15 | # TODO: Should String be allowed, and thus no `id`? 16 | #++ 17 | def self.parse(data) 18 | case data 19 | when String 20 | new('uri'=>data) 21 | when Array 22 | h, d = {}, data.dup # TODO: data.rekey(&:to_s) 23 | h.update(d.pop) while Hash === d.last 24 | h['name'] = d.shift.to_s unless d.empty? 25 | h['uri'] = d.shift.to_s unless d.empty? 26 | h['scm'] = d.shift.to_s unless d.empty? 27 | new(h) 28 | when Hash 29 | new(data) 30 | else 31 | raise(ValidationError, "not a valid repository") 32 | end 33 | end 34 | 35 | # 36 | # Initialize new Repository instance. 37 | # 38 | def initialize(data={}) 39 | super(data) 40 | 41 | self.scm = infer_scm(uri) unless scm 42 | end 43 | 44 | # 45 | # A name that can be used to identify the purpose of a 46 | # particular repository. 47 | # 48 | attr_reader :name 49 | 50 | # 51 | # Set the repository name. This can be any one line description but 52 | # it generally should be a brief one-word indictor such as "origin" 53 | # "upstream", "experimental", "joes-fork", etc. 54 | # 55 | def name=(name) 56 | Valid.oneline!(name) # should be word! 57 | @data[:name] = name.to_str 58 | end 59 | 60 | # 61 | # The repository's URI. 62 | # 63 | attr_reader :uri 64 | 65 | # 66 | # Set repository URI 67 | # 68 | def uri=(uri) 69 | Valid.oneline!(uri) 70 | #Valid.uri!(uri) # TODO: any other limitations? 71 | @data[:scm] = infer_scm(uri) 72 | @data[:uri] = uri 73 | end 74 | 75 | # 76 | # 77 | # 78 | attr_reader :scm 79 | 80 | # 81 | # Set the SCM type of repository. The type is a single downcased word. 82 | # Generally recognized types are: 83 | # 84 | # * git 85 | # * hg 86 | # * svn 87 | # * cvs 88 | # * darcs 89 | # 90 | # But any type can be used. 91 | # 92 | def scm=(scm) 93 | Valid.word!(scm) 94 | @data[:scm] = scm.to_str.downcase 95 | end 96 | 97 | # 98 | # Prefix URI that can be used to link to source code. 99 | # 100 | # This name is the traditional one from a time when CVS was the 101 | # most popular SCM. 102 | # 103 | attr_reader :webcvs 104 | 105 | # 106 | # 107 | # 108 | def webcvs=(uri) 109 | Valid.oneline!(uri) 110 | Valid.uri!(uri) # TODO: any other limitations? 111 | @data[:webcvs] = uri 112 | end 113 | 114 | # TODO: Should we rename Repository#webcvs to just #web ? 115 | 116 | # 117 | alias_method :web, :webcvs 118 | alias_method :web=, :webcvs= 119 | 120 | private 121 | 122 | # 123 | def infer_scm(uri) 124 | case uri 125 | when /^git:/, /\.git$/ 126 | 'git' 127 | when /^hg:/, /\.hg$/ 128 | 'hg' 129 | when /^svn:/ 130 | 'svn' 131 | when /darcs/ 132 | 'darcs' 133 | else 134 | nil 135 | end 136 | end 137 | 138 | end 139 | 140 | end 141 | -------------------------------------------------------------------------------- /work/reference/gemspec_exporter.rb: -------------------------------------------------------------------------------- 1 | # TEMPORARY REFERENCE - handling no root 2 | 3 | def self.gemspec(spec, root=nil) 4 | require_rubygems 5 | 6 | if spec.resources 7 | homepage = spec.resources.homepage 8 | else 9 | homepage = nil 10 | end 11 | 12 | if homepage && md = /(\w+).rubyforge.org/.match(homepage) 13 | rubyforge_project = md[1] 14 | else 15 | # b/c it has to be something according to Eric Hodel. 16 | rubyforge_project = spec.name.to_s 17 | end 18 | 19 | ::Gem::Specification.new do |gemspec| 20 | gemspec.name = spec.name.to_s 21 | gemspec.version = spec.version.to_s 22 | gemspec.require_paths = spec.load_path.to_a 23 | 24 | gemspec.summary = spec.summary.to_s 25 | gemspec.description = spec.description.to_s 26 | gemspec.authors = spec.authors.to_a 27 | gemspec.email = spec.email.to_s 28 | gemspec.licenses = spec.licenses.to_a 29 | 30 | gemspec.homepage = spec.homepage.to_s 31 | 32 | # -- platform -- 33 | 34 | # TODO: how to handle multiple platforms? 35 | #gemspec.platform = options[:platform] #|| verfile.platform #'ruby' ??? 36 | #if spec.platform != 'ruby' 37 | # gemspec.require_paths.concat(gemspec.require_paths.collect{ |d| File.join(d, platform) }) 38 | #end 39 | 40 | # -- rubyforge project -- 41 | gemspec.rubyforge_project = rubyforge_project 42 | 43 | # -- dependencies -- 44 | spec.requirements.each do |dep| 45 | if dep.development? 46 | gemspec.add_development_dependency( *[dep.name, dep.constraint].compact ) 47 | else 48 | next if dep.optional? 49 | gemspec.add_runtime_dependency( *[dep.name, dep.constraint].compact ) 50 | end 51 | end 52 | 53 | gemspec.requirements = spec.dependencies.map do |dep| 54 | [dep.name, dep.constraint].compact.join(' ') 55 | end 56 | 57 | # -- install message -- 58 | if spec.install_message 59 | gemspec.post_install_message = spec.install_message 60 | end 61 | 62 | # -- compiled extensions -- 63 | if root 64 | exts = local_files(root, 'ext/**/extconfig.rb') 65 | gemspec.extensions = exts unless exts.empty? 66 | end 67 | 68 | # -- executables -- 69 | # TODO: bin/ is a convention, is there are reason to do otherwise? 70 | if root 71 | bindir = local_files(root, 'bin').first 72 | execs = local_files(root, 'bin/*') 73 | 74 | gemspec.bindir = bindir if bindir 75 | gemspec.executables = execs 76 | end 77 | 78 | # -- distributed files -- 79 | if root 80 | if manifest_file = Dir.glob(File.join(root, 'manifest{,.txt}')).first 81 | manifest = File.read(manifest_file).split("\n") 82 | filelist = manifest.select{ |f| File.file?(f) } 83 | gemspec.files = filelist 84 | else 85 | gemspec.files = root.glob_relative("**/*").map{ |f| f.to_s } 86 | end 87 | end 88 | 89 | # -- rdocs (argh!) -- 90 | if root 91 | readme = local_files(root, 'README{,.*}', :casefold).first 92 | 93 | rdoc_extra = local_files(root, '[A-Z]*.*') 94 | rdoc_extra.unshift readme if readme 95 | rdoc_extra.uniq! 96 | 97 | gemspec.extra_rdoc_files = rdoc_extra 98 | 99 | rdoc_options = [] #['--inline-source'] 100 | rdoc_options.concat ["--title", "#{spec.title}"] #if spec.title 101 | rdoc_options.concat ["--main", readme] if readme 102 | 103 | gemspec.rdoc_options = rdoc_options 104 | end 105 | 106 | # DEPRECATED: -- test files -- 107 | #gemspec.test_files = manifest.select do |f| 108 | # File.basename(f) =~ /test\// && File.extname(f) == '.rb' 109 | #end 110 | end 111 | 112 | end 113 | 114 | -------------------------------------------------------------------------------- /work/deprecated/revisions/r2013/author.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module R2013 4 | 5 | # Author is used to model Authors and Maintainers. 6 | # 7 | # TODO: Should Author have an `organization` field. If so 8 | # is it a map with `name` and `website` fields? 9 | # 10 | # TODO: Should we have `team` field (think Github)? 11 | # 12 | module Author 13 | 14 | # 15 | def initialize(settings) 16 | @roles = [] 17 | 18 | settings.each do |field, value| 19 | send("#{field}=", value) 20 | end 21 | 22 | #raise ArgumentError, "an author must have a name" unless name 23 | end 24 | 25 | # 26 | attr :name 27 | 28 | # 29 | def name=(name) 30 | Valid.oneline!(name, :name) 31 | @name = name 32 | end 33 | 34 | # 35 | attr :email 36 | 37 | # 38 | def email=(email) 39 | Valid.email!(email, :email) 40 | @email = email 41 | end 42 | 43 | # 44 | attr :website 45 | 46 | # 47 | def website=(website) 48 | Valid.url!(website, :website) 49 | @website = website 50 | end 51 | 52 | # TODO: deprecate? 53 | attr :team 54 | 55 | # TODO: validate team 56 | def team=(team) 57 | @team = team 58 | end 59 | 60 | # List of roles the person plays in the project. 61 | # This can be any string or array of strings. 62 | attr :roles 63 | 64 | # 65 | def roles=(role) 66 | @roles = ( 67 | r = [role].flatten 68 | r.each{ |x| Valid.oneline?(x) } 69 | r 70 | ) 71 | end 72 | 73 | alias :role :roles 74 | alias :role= :roles= 75 | 76 | alias :group :groups 77 | alias :group= :groups= 78 | 79 | # 80 | def to_h 81 | h = {} 82 | h['name'] = name 83 | h['email'] = email if email 84 | h['website'] = website if website 85 | h['roles'] = roles if not roles.empty? 86 | h 87 | end 88 | 89 | # CONSIDE: Only name has to be equal? 90 | def ==(other) 91 | return false unless Author === other 92 | return false unless name == other.name 93 | return true 94 | end 95 | 96 | # 97 | def self.included(base) 98 | base.extend Parsing 99 | end 100 | 101 | # 102 | module Parsing 103 | 104 | # Parse `entry` and create Author object. 105 | def parse(entry) 106 | case entry 107 | when Author 108 | entry 109 | when String 110 | parse_string(entry) 111 | when Array 112 | parse_array(entry) 113 | when Hash 114 | new(entry) 115 | end 116 | end 117 | 118 | # 119 | def parse_array(array) 120 | data = {} 121 | array.each do |value| 122 | v = value.strip 123 | case v 124 | when /^(.*?)\s*\<(.*?@.*?)\>/ 125 | data[:name] = $1 unless $1.empty? 126 | data[:email] = $2 127 | when /^(http|https)\:/ 128 | data[:website] = v 129 | when /\@/ 130 | data[:email] = v 131 | else 132 | data[:name] = v 133 | end 134 | end 135 | new(data) 136 | end 137 | 138 | # 139 | def parse_string(string) 140 | if md = /(.*?)\s*\<(.*?)\>/.match(string) 141 | new(:name=>md[1], :email=>md[2]) 142 | else 143 | new(:name=>string) 144 | end 145 | end 146 | 147 | end 148 | 149 | end 150 | 151 | end 152 | 153 | end 154 | -------------------------------------------------------------------------------- /lib/indexer/conversion/gemspec.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module Conversion 4 | 5 | # 6 | # Create a Gem::Specification from Metadata. 7 | # 8 | # Becuase the specificaiton is extensive, a Gem::Specification 9 | # can be created that is sufficient for most needs. However, there 10 | # are a few points where the two do not intersect. In these cases 11 | # the remaining gemspec fields need to be provided post conversion. 12 | # 13 | # Gem::Specification fields that the specification can't provide include: 14 | # 15 | # * platform 16 | # 17 | # In addition, some information can only be provided if a project's `root` 18 | # directory is given. In these cases the most common project conventions 19 | # are utilized in determining field values. Gem::Specification fields that 20 | # draw from project files include: 21 | # 22 | # * files 23 | # * bindir 24 | # * extensions 25 | # * rdoc_options 26 | # * rdoc_extra_files 27 | # 28 | # Any or all of which can be reassigned post conversion, if need be. 29 | # 30 | # @param [Hash] options 31 | # Convertion options. 32 | # 33 | # @option options [NilClass,String] :root 34 | # project root directory 35 | # 36 | def to_gemspec(options={}) 37 | options[:data] = self.to_h 38 | GemspecExporter.new(options).to_gemspec 39 | end 40 | 41 | # 42 | # Import a Gem::Specification into Spec. This is intended to make it 43 | # fairly easy to build the metadata from a pre-existing `.gemspec`. 44 | # 45 | # By making this an instance method, it is possible to import other 46 | # resources into the Spec prior to a `.gemspec`. 47 | # 48 | # @todo Ensure all data possible is gathered from the gemspec. 49 | # 50 | def import_gemspec(gemspec) 51 | require 'rubygems' 52 | 53 | if not Gem::Specification === gemspec 54 | # TODO: YAML-based gem specs ? 55 | gemspec = Gem::Specification.load(gemspec) 56 | end 57 | 58 | # TODO: ensure this is robust 59 | authors = ( 60 | zip = [gemspec.authors].flatten.zip([gemspec.email].flatten) 61 | zip.map do |(name, email)| 62 | email ? {:name=>name, :email=>email} : {:name=>name} 63 | end 64 | ) 65 | 66 | # TODO: how to handle license(s) ? 67 | 68 | self.name = gemspec.name 69 | self.version = gemspec.version.to_s 70 | self.date = gemspec.date 71 | self.title = gemspec.name.capitalize 72 | self.summary = gemspec.summary 73 | self.description = gemspec.description || gemspec.summary 74 | self.authors = authors 75 | self.paths['load'] = gemspec.require_paths 76 | self.homepage = gemspec.homepage 77 | 78 | #self.engines = gemspec.platform 79 | #self.extensions = gemspec.extensions 80 | 81 | # TODO: Spec currently doesn't support multiple constraints for requirements. 82 | # Probably 99.999% of the time it doesn't matter. 83 | gemspec.dependencies.each do |d| 84 | if d.type == :runtime 85 | add_requirement(d.name, :versions=>d.requirements_list.first) 86 | else 87 | add_requirement(d.name, :versions=>d.requirements_list.first, :development=>true) 88 | end 89 | end 90 | end 91 | 92 | end 93 | 94 | end 95 | 96 | 97 | =begin 98 | 99 | case gemspec 100 | when ::Gem::Specification 101 | spec = gemspec 102 | else 103 | file = Dir[root + "{*,}.gemspec"].first 104 | return unless file 105 | 106 | text = File.read(file) 107 | if text =~ /\A---/ 108 | spec = ::Gem::Specification.from_yaml(text) 109 | else 110 | spec = ::Gem::Specification.load(file) 111 | end 112 | end 113 | 114 | =end 115 | -------------------------------------------------------------------------------- /data/indexer/index.yes: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 3 | type: int 4 | required: true 5 | name: 6 | type: str 7 | regexp: /^\w+$/ # package name 8 | required: true 9 | version: 10 | type: str 11 | regexp: /^\d(\.\w+)*/ 12 | required: true 13 | codename: 14 | type: str 15 | regexp: /[^\n]/ # no newline 16 | title 17 | type: str 18 | regexp: /[^\n]/ # no newline 19 | date: 20 | type: str 21 | pic: &pic-date 9999-99-99[ 99:99:99] 22 | created: 23 | type: str 24 | pic: &pic-date 25 | summary: 26 | type: str 27 | regexp: /[^\n]/ # no newline 28 | description: 29 | type: str 30 | authors: 31 | type: seq 32 | value: 33 | type: map 34 | mapping: 35 | name: 36 | type: str 37 | email: 38 | type: str 39 | regexp: /@/ 40 | url: 41 | type: str 42 | roles: 43 | type: seq 44 | suite: 45 | type: str 46 | regexp: /[^\n]/ # no newline 47 | organization: 48 | type: str 49 | regexp: /[^\n]/ # no newline 50 | copyrights: 51 | type: seq 52 | value: 53 | type: map 54 | template: 55 | holder: 56 | type: str 57 | regexp: /[^\n]/ # no newline 58 | year: 59 | type: str 60 | regexp: /^\d{4}([-,]^\d{4})?$/ 61 | license: 62 | type: str 63 | regexp: /[^\n]/ # no newline 64 | requirements: &requirements 65 | type: seq 66 | value: 67 | type: map 68 | template: 69 | name: 70 | type: str 71 | regexp: /[^\n]/ # no newline 72 | version: 73 | type: seq 74 | value: 75 | type: str 76 | regexp: /^\d(\.\w+)*/ 77 | groups: 78 | type: seq 79 | value: 80 | type: str 81 | regexp: /[^\n]/ # no newline 82 | development: 83 | type: bool 84 | optional: 85 | type: bool 86 | engine: 87 | type: seq 88 | value: 89 | type: map 90 | template: 91 | name: 92 | type: str 93 | regexp: /[^\n]/ # no newline 94 | version: 95 | type: str 96 | regexp: /^\d/ 97 | platform: 98 | type: seq 99 | value: 100 | type: str 101 | regexp: /[^\n]/ # no newline 102 | repository: 103 | type: map 104 | template: 105 | url: 106 | type: str 107 | regexp: /[^\n]/ # url 108 | scm: 109 | type: str 110 | regexp: /[^\n]/ # no newline 111 | dependencies: *requirements 112 | conflicts: 113 | type: seq 114 | value: 115 | type: map 116 | map: 117 | name: 118 | type: str 119 | regexp: /^\w+$/ # package name 120 | version: 121 | type: seq 122 | value: 123 | type: str 124 | regexp: /[^\n]/ # version constraint 125 | substitues: 126 | type: seq 127 | value: 128 | type: str 129 | regexp: /^\w+$/ # package name 130 | replaces: 131 | type: seq 132 | value: 133 | type: str 134 | regexp: /^\w+$/ # package name 135 | resources: 136 | type: seq 137 | value: 138 | type: map 139 | map: 140 | name: 141 | type: str 142 | regexp: /[^\n]/ # no newline 143 | uri: 144 | type: str 145 | regexp: /^http/ # url/irc 146 | required: true 147 | repositories: 148 | type: seq 149 | value: 150 | type: map 151 | map: 152 | name: 153 | type: str 154 | regexp: /[^\n]/ # no newline 155 | url: 156 | type: str 157 | regexp: // # url 158 | required: true 159 | scm: 160 | type: str 161 | regexp: /^\w$/ # word 162 | load_path: 163 | type: seq 164 | value: 165 | type: str 166 | regexp: /\S/ # path 167 | default: [lib] 168 | install_message: 169 | type: str 170 | extra: 171 | type: map 172 | 173 | -------------------------------------------------------------------------------- /lib/indexer/importer/file.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | class Importer 4 | 5 | # Import metadata from individual files. 6 | # 7 | module FileImportation 8 | 9 | # 10 | # Files import procedure. 11 | # 12 | def import(source) 13 | if File.directory?(source) 14 | load_directory(source) 15 | true 16 | else 17 | super(source) if defined?(super) 18 | end 19 | end 20 | 21 | # 22 | # Import files from a given directory. This will only import files 23 | # that have a name corresponding to a metadata attribute, unless 24 | # the file name is listed in the `customs` file within the directory. 25 | # 26 | # However, files with an extension of `.index` will be loaded as YAML 27 | # wholeclothe and not as a single attribute. 28 | # 29 | # @todo Subdirectories are simply omitted. Maybe do otherwise in future? 30 | # 31 | def load_directory(folder) 32 | if File.directory?(folder) 33 | customs = read_customs(folder) 34 | 35 | files = Dir[File.join(folder, '*')] 36 | 37 | files.each do |file| 38 | next if File.directory?(file) 39 | name = File.basename(file).downcase 40 | next load_yaml(file) if File.extname(file) == '.index' 41 | next load_field_file(file) if customs.include?(name) 42 | next load_field_file(file) if metadata.attributes.include?(name.to_sym) 43 | end 44 | end 45 | end 46 | 47 | # 48 | # Import a field setting from a file. 49 | # 50 | # TODO: Ultimately support JSON and maybe other types, and possibly 51 | # use mime-types library to recognize them. 52 | # 53 | def load_field_file(file) 54 | if File.directory?(file) 55 | # ... 56 | else 57 | case File.extname(file).downcase 58 | when '.yaml', '.yml' 59 | name = File.basename(file).downcase 60 | name = name.chomp('.yaml').chomp('.yml') 61 | metadata[name] = YAML.load_file(file) 62 | # TODO: should yaml files with explict extension by merged instead? 63 | #metadata.merge!(YAML.load_file(file)) 64 | when '.text', '.txt' 65 | name = File.basename(file).downcase 66 | name = name.chomp('.text').chomp('.txt') 67 | text = File.read(file) 68 | metadata[name] = text.strip 69 | else 70 | text = File.read(file) 71 | if /\A---/ =~ text 72 | name = File.basename(file).downcase 73 | metadata[name] = YAML.load(text) 74 | else 75 | name = File.basename(file).downcase 76 | metadata[name] = text.strip 77 | end 78 | end 79 | end 80 | end 81 | 82 | # 83 | def read_customs(folder) 84 | list = [] 85 | file = Dir[File.join(folder, 'customs{,.*}')].first 86 | if file 87 | if %w{.yaml .yml}.include?(File.extname(file)) 88 | list = YAML.load_file(file) || [] 89 | raise TypeError, "index: customs is not a array" unless Array === list 90 | else 91 | text = File.read(file) 92 | if yaml?(text) 93 | list = YAML.load_file(file) || [] 94 | raise TypeError, "index: customs is not a array" unless Array === list 95 | else 96 | list = text.split("\n") 97 | list = list.collect{ |pattern| pattern.strip } 98 | list = list.reject { |pattern| pattern.empty? } 99 | list = list.collect{ |pattern| Dir[File.join(folder, pattern)] }.flatten 100 | end 101 | end 102 | end 103 | return list 104 | end 105 | 106 | end 107 | 108 | # Include FileImportation mixin into Builder class. 109 | include FileImportation 110 | 111 | end 112 | 113 | end 114 | -------------------------------------------------------------------------------- /work/reference/r1.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | Gem::Specification.new do |gemspec| 6 | 7 | manifest = Dir.glob('manifest{,.txt)', File::FNM_CASEFOLD).first 8 | 9 | scm = case 10 | when File.directory?('.git') 11 | :git 12 | end 13 | 14 | files = case 15 | when manifest 16 | File.readlines(manifest). 17 | map{ |line| line.srtip }. 18 | reject{ |line| line.empty? || line[0,1] == '#' } 19 | when scm == :git 20 | `git ls-files -z`.split("\0") 21 | else 22 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 23 | end.select{ |path| File.file?(path) } 24 | 25 | patterns = { 26 | :bin_files => 'bin/*', 27 | :lib_files => 'lib/{**/}*.rb', 28 | :ext_files => 'ext/{**/}extconf.rb', 29 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 30 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 31 | } 32 | 33 | glob_files = lambda { |pattern| 34 | Dir.glob(pattern).select { |path| 35 | File.file?(path) && files.include?(path) 36 | } 37 | } 38 | 39 | #files = glob_files[patterns[:files]] 40 | 41 | executables = glob_files[patterns[:bin_files]].map do |path| 42 | File.basename(path) 43 | end 44 | 45 | extensions = glob_files[patterns[:ext_files]].map do |path| 46 | File.basename(path) 47 | end 48 | 49 | metadata = YAML.load_file('.ruby') 50 | 51 | # build-out the gemspec 52 | 53 | case metadata['revision'] 54 | when 0 55 | gemspec.name = metadata['name'] 56 | gemspec.version = metadata['version'] 57 | gemspec.summary = metadata['summary'] 58 | gemspec.description = metadata['description'] 59 | 60 | metadata['authors'].each do |author| 61 | gemspec.authors << author['name'] 62 | 63 | if author.has_key?('email') 64 | if gemspec.email 65 | gemspec.email << author['email'] 66 | else 67 | gemspec.email = [author['email']] 68 | end 69 | end 70 | end 71 | 72 | gemspec.licenses = metadata['licenses'] 73 | 74 | metadata['requirements'].each do |req| 75 | name = req['name'] 76 | version = req['version'] 77 | groups = req['groups'] 78 | 79 | if req['development'] 80 | # populate development dependencies 81 | if gemspec.respond_to?(:add_development_dependency) 82 | gemspec.add_development_dependency(name,*version) 83 | else 84 | gemspec.add_dependency(name,*version) 85 | end 86 | else 87 | # populate runtime dependencies 88 | if gemspec.respond_to?(:add_runtime_dependency) 89 | gemspec.add_runtime_dependency(name,*version) 90 | else 91 | gemspec.add_dependency(name,*version) 92 | end 93 | end 94 | end 95 | 96 | # convert external dependencies into a requirements 97 | if metadata['external_dependencies'] 98 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 99 | metadata['external_dependencies'].each do |req| 100 | gemspec.requirements << req.to_s 101 | end 102 | end 103 | 104 | # determine homepage from resources 105 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 106 | gemspec.homepage = homepage.last if homepage 107 | 108 | gemspec.require_paths = metadata['load_path'] 109 | gemspec.post_install_message = metadata['install_message'] 110 | 111 | # RubyGems specific metadata 112 | gemspec.files = files 113 | gemspec.extensions = extensions 114 | gemspec.executables = executables 115 | 116 | if Gem::VERSION < '1.7.' 117 | gemspec.default_executable = gemspec.executables.first 118 | end 119 | 120 | gemspec.test_files = glob_files[patterns[:test_files]] 121 | 122 | unless gemspec.files.include?('.document') 123 | gemspec.extra_rdoc_files = glob_files[patterns[:doc_files]] 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /work/deprecated/revisions/r2013/repository.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module R2013 4 | 5 | # Repository models a packages SCM repository location. 6 | # It consists of two parts, the `scm` type of repository, it's `url`. 7 | # 8 | module Repository 9 | 10 | # 11 | # Initialize new Repository instance. 12 | # 13 | def initialize(data={}) 14 | data.each do |field, value| 15 | send("#{field}=", value) 16 | end 17 | 18 | self.scm = infer_scm(uri) unless scm 19 | end 20 | 21 | # 22 | # A name that can be used to identify the purpose of a 23 | # particular repository. 24 | # 25 | attr_reader :name 26 | 27 | # 28 | # Set the repository name. This can be any one line description but 29 | # it generally should be a brief one-word indictor such as "origin" 30 | # "upstream", "experimental", "joes-fork", etc. 31 | # 32 | def name=(name) 33 | Valid.oneline!(name) # should be word! 34 | @name = name.to_str 35 | end 36 | 37 | # 38 | # The repository's URI. 39 | # 40 | attr_reader :uri 41 | 42 | # 43 | # Set repository URI 44 | # 45 | def uri=(uri) 46 | Valid.oneline!(uri) 47 | #Valid.uri!(uri) # TODO: any other limitations? 48 | @scm = infer_scm(uri) 49 | @uri = uri 50 | end 51 | 52 | # 53 | # 54 | # 55 | attr_reader :scm 56 | 57 | # 58 | # Set the SCM type of repository. The type is a single downcased word. 59 | # Generally recognized types are: 60 | # 61 | # * git 62 | # * hg 63 | # * svn 64 | # * cvs 65 | # * darcs 66 | # 67 | # But any type can be used. 68 | # 69 | def scm=(scm) 70 | Valid.word!(scm) 71 | @scm = scm.to_str.downcase 72 | end 73 | 74 | # 75 | # Prefix URI that can be used to link to source code. 76 | # 77 | # This name is the traditional one from a time when CVS was the 78 | # most popular SCM. 79 | # 80 | attr_reader :webcvs 81 | 82 | # 83 | # 84 | # 85 | def webcvs=(uri) 86 | Valid.oneline!(uri) 87 | Valid.uri!(uri) # TODO: any other limitations? 88 | @webcvs = uri 89 | end 90 | 91 | # TODO: Should we rename Repository#webcvs to just #web ? 92 | 93 | # 94 | alias_method :web, :webcvs 95 | alias_method :web=, :webcvs= 96 | 97 | # 98 | def to_h 99 | h = {} 100 | h['uri'] = uri 101 | h['scm'] = scm if scm 102 | h['name'] = name if name 103 | h['webcvs'] = webcvs if webcvs 104 | h 105 | end 106 | 107 | private 108 | 109 | # 110 | def infer_scm(uri) 111 | case uri 112 | when /^git:/, /\.git$/ 113 | 'git' 114 | when /^hg:/, /\.hg$/ 115 | 'hg' 116 | when /^svn:/ 117 | 'svn' 118 | when /darcs/ 119 | 'darcs' 120 | else 121 | nil 122 | end 123 | end 124 | 125 | #singleton 126 | 127 | # 128 | def self.included(base) 129 | base.extend Parsing 130 | end 131 | 132 | # 133 | # 134 | module Parsing 135 | 136 | # Parse `data` returning a new Repository instance. 137 | # 138 | # @param data [String,Array,Array,Hash] 139 | # Repository information. 140 | # 141 | # @return [Repository] repository instance 142 | #-- 143 | # TODO: Should String be allowed, and thus no `id`? 144 | #++ 145 | def parse(data) 146 | case data 147 | when String 148 | new('uri'=>data) 149 | when Array 150 | h, d = {}, data.dup # TODO: data.rekey(&:to_s) 151 | h.update(d.pop) while Hash === d.last 152 | h['name'] = d.shift.to_s unless d.empty? 153 | h['uri'] = d.shift.to_s unless d.empty? 154 | h['scm'] = d.shift.to_s unless d.empty? 155 | new(h) 156 | when Hash 157 | new(data) 158 | else 159 | raise(ValidationError, "not a valid repository") 160 | end 161 | end 162 | 163 | end 164 | 165 | end 166 | 167 | end 168 | -------------------------------------------------------------------------------- /lib/indexer/importer.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Import external sources into metadata. 4 | # 5 | def self.import(*sources) 6 | Importer.import(*sources) 7 | end 8 | 9 | # Importer class takes disperate data sources and imports them 10 | # into a Metadata instance. 11 | # 12 | # Mixins are used to inject import behavior by overriding the `#import` method. 13 | # Any such mixin's #import method must call `#super` if it's method doesn't 14 | # apply, allowing the routine to fallback the other possible import methods. 15 | # 16 | class Importer 17 | 18 | # 19 | # Require all import mixins. 20 | # 21 | # This method calls `super` if it is defined which makes it easy 22 | # for plugins to add new importers. 23 | # 24 | def self.require_importers 25 | require_relative 'importer/file' 26 | require_relative 'importer/ruby' 27 | require_relative 'importer/yaml' 28 | require_relative 'importer/gemspec' 29 | require_relative 'importer/gemfile' 30 | require_relative 'importer/version' 31 | #require_relative 'importer/html' 32 | #require_relative 'importer/markdown' 33 | #require_relative 'importer/rdoc' 34 | #require_relative 'importer/textile' 35 | 36 | # for plugins to easily add additional importers 37 | super if defined?(super) 38 | end 39 | 40 | # 41 | # Import metadata from external sources. 42 | # 43 | def self.import(*source) 44 | options = (Hash === source.last ? source.pop : {}) 45 | 46 | require_importers 47 | 48 | #metadata = nil 49 | 50 | ## use source of current metadata if none given 51 | ## TODO: Only search the current directory or search up to root? 52 | if source.empty? 53 | if file = Dir[LOCK_FILE].first #or `Metadata.exists?` ? 54 | data = YAML.load_file(file) 55 | source = Array(data['source']) 56 | end 57 | end 58 | 59 | if source.empty? 60 | source = [USER_FILE] 61 | end 62 | 63 | source.each do |file| 64 | unless File.exist?(file) 65 | warn "metadata source file not found - `#{file}'" 66 | end 67 | end 68 | 69 | importer = Importer.new #(metadata) 70 | 71 | source.each do |src| 72 | importer.import(src) 73 | end 74 | 75 | return importer.metadata 76 | end 77 | 78 | # 79 | # Initialize importer. 80 | # 81 | def initialize(metadata=nil) 82 | @metadata = metadata || Metadata.new 83 | @file_cache = {} 84 | end 85 | 86 | # 87 | # Metadata being built. 88 | # 89 | attr :metadata 90 | 91 | # 92 | # 93 | # 94 | def import(source) 95 | success = super(source) if defined?(super) 96 | if success 97 | metadata.sources << source unless metadata.sources.include?(source) 98 | else 99 | raise "metadata source not found or not a known type -- #{source}" 100 | end 101 | end 102 | 103 | # 104 | # Provides a file contents cache. This is used by the YAMLImportation 105 | # script, for instance, to see if the file begins with `---`, in 106 | # which case the file is taken to be YAML format, even if the 107 | # file's extension is not `.yml` or `.yaml`. 108 | # 109 | def read(file) 110 | @file_cache[file] ||= File.read(file) 111 | end 112 | 113 | # 114 | # Evaluating on the Importer instance, allows Ruby basic metadata 115 | # to be built via this method. 116 | # 117 | def method_missing(s, *a, &b) 118 | return if s == :import 119 | 120 | r = s.to_s.chomp('=') 121 | case a.size 122 | when 0 123 | if metadata.respond_to?(s) 124 | return metadata.__send__(s, &b) 125 | end 126 | when 1 127 | if metadata.respond_to?("#{r}=") 128 | return metadata.__send__("#{r}=", *a) 129 | end 130 | else 131 | if metadata.respond_to?("#{r}=") 132 | return metadata.__send__("#{r}=", a) 133 | end 134 | end 135 | 136 | super(s, *a, &b) # if cases don't match-up 137 | end 138 | 139 | # 140 | # Is `text` a YAML document? It detrmines this simply 141 | # be checking for `---` at the top of the text. 142 | # 143 | # @todo Ignore top comments. 144 | # 145 | def yaml?(text) 146 | text =~ /\A(---|%TAG|%YAML)/ 147 | end 148 | end 149 | 150 | end 151 | -------------------------------------------------------------------------------- /lib/indexer/loadable.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | module Loadable 4 | 5 | # 6 | # Open metadata file and ensure strict validation to canonical format. 7 | # 8 | # @param [String] file or directory 9 | # The file name from which to read the YAML metadata, 10 | # or a directory from which to lookup the file. 11 | # 12 | def open(file=Dir.pwd) 13 | file = find(file) if File.directory?(file) 14 | valid(YAML.load_file(file)) 15 | end 16 | 17 | # 18 | # Like #open, but do not ensure strict validation to canonical format. 19 | # 20 | # @param [String] file or directory 21 | # The file name from which to read the YAML metadata, 22 | # or a directory from which to lookup the file. 23 | # 24 | def read(file=Dir.pwd) 25 | file = find(file) if File.directory?(file) 26 | new(YAML.load_file(file)) 27 | end 28 | 29 | # 30 | # Create new Metadata instance from atypical sources. 31 | # 32 | # TODO: Use Importer to construct Metadata instance. 33 | # 34 | def import(*sources) 35 | end 36 | 37 | # 38 | # Load from YAML string or IO. 39 | # 40 | # @param [String,#read] String or IO object 41 | # The file name from which to read the YAML metadata. 42 | # 43 | def load(io) 44 | new(YAML.load(io)) 45 | end 46 | 47 | # 48 | # Load from YAML file. 49 | # 50 | # @param [String] file 51 | # The file name from which to read the YAML metadata. 52 | # 53 | def load_file(file) 54 | new(YAML.load_file(file)) 55 | end 56 | 57 | # 58 | # Find project root and read the index file. 59 | # 60 | # @param [String] from 61 | # The directory from which to start the upward search. 62 | # 63 | def find(from=Dir.pwd) 64 | File.join(root(from), LOCK_FILE) 65 | end 66 | 67 | # 68 | # Find project root by looking upward for a locked metadata file. 69 | # 70 | # @param [String] from 71 | # The directory from which to start the upward search. 72 | # 73 | # @return [String] 74 | # The path to the locked metadata file. 75 | # 76 | # @raise [Errno::ENOENT] 77 | # The locked metadata file could not be located. 78 | # 79 | def root(from=Dir.pwd) 80 | if not path = exists?(from) 81 | lock_file_missing(from) 82 | end 83 | path 84 | end 85 | 86 | # 87 | # Does a locked metadata file exist? 88 | # 89 | # @return [true,false] Whether locked metadata file exists. 90 | # 91 | def exists?(from=Dir.pwd) 92 | home = File.expand_path('~') 93 | path = File.expand_path(from) 94 | while path != '/' and path != home 95 | if File.file?(File.join(path,LOCK_FILE)) 96 | return path 97 | else 98 | path = File.dirname(path) 99 | end 100 | false #lock_file_missing(from) 101 | end 102 | false #lock_file_missing(from) 103 | end 104 | 105 | # 106 | # Raise lock file missing error. 107 | # 108 | def lock_file_missing(from=nil) 109 | raise Error.exception("could not locate .index file", Errno::ENOENT) 110 | end 111 | 112 | # 113 | # Alias for exists? 114 | # 115 | def exist?(from=Dir.pwd) 116 | exists?(from) 117 | end 118 | 119 | # 120 | # Lockdown the metadata (via Importer) and return updated metadata. 121 | # 122 | # Options :force 123 | # 124 | def lock(*sources) 125 | opts = (Hash === sources.last ? sources.pop : {}) 126 | 127 | file = nil 128 | needed = true 129 | sources = sources.flatten 130 | 131 | if sources.empty? 132 | if file = exists? 133 | metadata = Metadata.open 134 | sources = metadata.sources 135 | else 136 | #sources = Dir.glob(USER_FILES, File::FNM_CASEFOLD) 137 | raise Error.exception("Could not find a metadata source.") if sources.empty? 138 | end 139 | end 140 | 141 | if file && !opts[:force] 142 | date = sources.map{ |s| File.mtime(s) }.max 143 | needed = false if File.mtime(file) > date 144 | end 145 | 146 | Importer.import(*sources) if needed 147 | end 148 | 149 | # 150 | # Lockdown the metadata (via Importer) and save. 151 | # 152 | def lock!(*sources) 153 | metadata = lock(*sources) 154 | metadata.save! if metadata 155 | end 156 | 157 | end 158 | 159 | end 160 | -------------------------------------------------------------------------------- /work/reference/pom_classes/resources.rb: -------------------------------------------------------------------------------- 1 | module POM 2 | 3 | # The Resource class models a table of project 4 | # releated URIs. Each entry has a name and URI. 5 | # The class is Enumerable so each entry can 6 | # be iterated over, much like a hash. 7 | # 8 | # The class also recognizes common entry names 9 | # and aliases, which can be accessed via method 10 | # calls. 11 | # 12 | # TODO: Consider if this should instead be an 13 | # associative array or [type, url]. Could there 14 | # not be more than one url for a given type? 15 | 16 | class Resources 17 | include Enumerable 18 | 19 | # Valid URL regular expression. 20 | URL = /^(\w+)\:\/\/\S+$/ 21 | 22 | @key_aliases = {} 23 | 24 | # 25 | def self.key_aliases 26 | @key_aliases 27 | end 28 | 29 | # 30 | def self.attr_accessor name, *aliases 31 | code = [] 32 | ([name] + aliases).each do |method| 33 | key_aliases[method.to_sym] = name.to_sym 34 | code << "def #{method}" 35 | code << " self['#{name}']" 36 | code << "end" 37 | code << "def #{method}=(val)" 38 | code << " self['#{name}'] = val" 39 | code << "end" 40 | end 41 | module_eval code.join("\n") 42 | end 43 | 44 | # New Resources object. The initializer can 45 | # take a hash of name to URL. 46 | def initialize(data={}) 47 | @table = {} 48 | 49 | data = {} if data.nil? 50 | 51 | data.each do |key, url| 52 | self[key] = url 53 | end 54 | end 55 | 56 | # 57 | def key_index(key) 58 | key = key.to_sym 59 | self.class.key_aliases[key] || key 60 | end 61 | 62 | # 63 | def [](key) 64 | @table[key_index(key)] 65 | end 66 | 67 | # 68 | def []=(key, url) 69 | raise ArgumentError, "Not a valid URL - `#{url}' for `#{key}'" unless URL =~ url 70 | @table[key_index(key)] = url 71 | end 72 | 73 | # Offical project website. 74 | attr_accessor :home, :homepage 75 | 76 | # Location of development site. 77 | attr_accessor :work, :dev, :development 78 | 79 | # Package distribution service webpage. 80 | attr_accessor :gem, :ditro, :distributor 81 | 82 | # Location to downloadable package(s). 83 | attr_accessor :download 84 | 85 | # Browse source code. 86 | attr_accessor :code, :source, :source_code 87 | 88 | # User discussion forum. 89 | attr_accessor :forum 90 | 91 | # Mailing list email or web address to online version. 92 | attr_accessor :mail, :email, :mailinglist 93 | 94 | # Location of issue tracker. 95 | attr_accessor :bugs, :issues 96 | 97 | # Location of support forum. 98 | attr_accessor :support 99 | 100 | # Location of documentation. 101 | attr_accessor :docs, :documentation, :doc 102 | 103 | # Location of API reference documentation. 104 | attr_accessor :api, :reference, :system_guide 105 | 106 | # Location of wiki. 107 | attr_accessor :wiki, :user_guide 108 | 109 | # Resource to project blog. 110 | attr_accessor :blog, :weblog 111 | 112 | # IRC channel 113 | attr_accessor :irc, :chat 114 | 115 | # Convert to Hash by duplicating the underlying 116 | # hash table. 117 | def to_h 118 | @table.dup 119 | end 120 | 121 | # 122 | def to_data 123 | h = {} 124 | to_h.each do |k,v| 125 | h[k.to_s] = v 126 | end 127 | return h 128 | end 129 | 130 | # 131 | def empty? 132 | @table.empty? 133 | end 134 | 135 | # Iterate over each enty, including aliases. 136 | def each(&block) 137 | @table.each(&block) 138 | end 139 | 140 | # The size of the table, including aliases. 141 | def size 142 | @table.size 143 | end 144 | 145 | # 146 | def inspect 147 | @table.inspect 148 | end 149 | 150 | # 151 | def merge!(res) 152 | res.each do |key, url| 153 | self[key] = url 154 | end 155 | end 156 | 157 | # If a method is missing and it is a setter method 158 | # (ending in '=') then a new entry by that name 159 | # will be added to the table. If a plain method 160 | # then the name will be looked for in the table. 161 | def method_missing(sym, *args) 162 | meth = sym.to_s 163 | name = meth.chomp('=') 164 | case meth 165 | when /=$/ 166 | self[name] = args.first 167 | else 168 | super(sym, *args) if block_given? or args.size > 0 169 | self[name] 170 | end 171 | end 172 | 173 | end 174 | 175 | end 176 | 177 | -------------------------------------------------------------------------------- /work/reference/old.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | Gem::Specification.new do |gemspec| 6 | 7 | manifest = Dir.glob('manifest{,.txt)', File::FNM_CASEFOLD).first 8 | 9 | scm = case 10 | when File.directory?('.git') 11 | :git 12 | end 13 | 14 | files = case 15 | when manifest 16 | File.readlines(manifest). 17 | map{ |line| line.srtip }. 18 | reject{ |line| line.empty? || line[0,1] == '#' } 19 | when scm == :git 20 | `git ls-files -z`.split("\0") 21 | else 22 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 23 | end.select{ |path| File.file?(path) } 24 | 25 | patterns = { 26 | :bin_files => 'bin/*', 27 | :lib_files => 'lib/{**/}*.rb', 28 | :ext_files => 'ext/{**/}extconf.rb', 29 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 30 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 31 | } 32 | 33 | glob_files = lambda { |pattern| 34 | Dir.glob(pattern).select { |path| 35 | File.file?(path) && files.include?(path) 36 | } 37 | } 38 | 39 | #files = glob_files[patterns[:files]] 40 | 41 | executables = glob_files[patterns[:bin_files]].map do |path| 42 | File.basename(path) 43 | end 44 | 45 | extensions = glob_files[patterns[:ext_files]].map do |path| 46 | File.basename(path) 47 | end 48 | 49 | metadata = YAML.load_file('.ruby') 50 | 51 | # build-out the gemspec 52 | 53 | case metadata['revision'] 54 | when 0 55 | gemspec.name = metadata['name'] 56 | gemspec.version = metadata['version'] 57 | gemspec.summary = metadata['summary'] 58 | gemspec.description = metadata['description'] 59 | 60 | metadata['authors'].each do |author| 61 | gemspec.authors << author['name'] 62 | 63 | if author.has_key?('email') 64 | if gemspec.email 65 | gemspec.email << author['email'] 66 | else 67 | gemspec.email = [author['email']] 68 | end 69 | end 70 | end 71 | 72 | gemspec.licenses = metadata['licenses'] 73 | 74 | metadata['requirements'].each do |req| 75 | name = req['name'] 76 | version = req['version'] 77 | groups = req['groups'] 78 | 79 | #development = req['development'] 80 | #if development 81 | # # populate development dependencies 82 | # if gemspec.respond_to?(:add_development_dependency) 83 | # gemspec.add_development_dependency(name,*version) 84 | # else 85 | # gemspec.add_dependency(name,*version) 86 | # end 87 | #else 88 | # # populate runtime dependencies 89 | # if gemspec.respond_to?(:add_runtime_dependency) 90 | # gemspec.add_runtime_dependency(name,*version) 91 | # else 92 | # gemspec.add_dependency(name,*version) 93 | # end 94 | #end 95 | 96 | if groups.empty? or groups.include?('runtime') 97 | # populate runtime dependencies 98 | if gemspec.respond_to?(:add_runtime_dependency) 99 | gemspec.add_runtime_dependency(name,*version) 100 | else 101 | gemspec.add_dependency(name,*version) 102 | end 103 | else 104 | # populate development dependencies 105 | if gemspec.respond_to?(:add_development_dependency) 106 | gemspec.add_development_dependency(name,*version) 107 | else 108 | gemspec.add_dependency(name,*version) 109 | end 110 | end 111 | end 112 | 113 | # convert external dependencies into a requirements 114 | if metadata['external_dependencies'] 115 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 116 | metadata['external_dependencies'].each do |req| 117 | gemspec.requirements << req.to_s 118 | end 119 | end 120 | 121 | # determine homepage from resources 122 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 123 | gemspec.homepage = homepage.last if homepage 124 | 125 | gemspec.require_paths = metadata['load_path'] || ['lib'] 126 | gemspec.post_install_message = metadata['install_message'] 127 | 128 | # RubyGems specific metadata 129 | gemspec.files = files 130 | gemspec.extensions = extensions 131 | gemspec.executables = executables 132 | 133 | if Gem::VERSION < '1.7.' 134 | gemspec.default_executable = gemspec.executables.first 135 | end 136 | 137 | gemspec.test_files = glob_files[patterns[:test_files]] 138 | 139 | unless gemspec.files.include?('.document') 140 | gemspec.extra_rdoc_files = glob_files[patterns[:doc_files]] 141 | end 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /data/indexer/index.kwalify: -------------------------------------------------------------------------------- 1 | --- 2 | type: map 3 | mapping: 4 | revision 5 | type: int 6 | required: true 7 | name: 8 | type: str 9 | pattern: /^\w+$/ # package name 10 | required: true 11 | version: 12 | type: str 13 | pattern: /^\d(\.\w+)*/ 14 | required: true 15 | codename: 16 | type: str 17 | pattern: /[^\n]/ # no newline 18 | title 19 | type: str 20 | pattern: /[^\n]/ # no newline 21 | date: 22 | type: str 23 | pattern: /^\d{4}-\d{2}-\d{2}(\d{2}){0,3}$/ # utc date 24 | created: 25 | type: str 26 | pattern: /^\d{4}-\d{2}-\d{2}(\d{2}){0,3}$/ # utc date 27 | summary: 28 | type: str 29 | pattern: /[^\n]/ # no newline 30 | description: 31 | type: str 32 | authors: 33 | type: seq 34 | sequence: 35 | - type: map 36 | mapping: 37 | name: 38 | type: str 39 | email: 40 | type: str 41 | pattern: /@/ 42 | url: 43 | type: str 44 | role: 45 | type: seq 46 | suite: 47 | type: str 48 | pattern: /[^\n]/ # no newline 49 | organization: 50 | type: str 51 | pattern: /[^\n]/ # no newline 52 | copyright: 53 | type: seq 54 | sequence: 55 | - type: map 56 | mapping: 57 | holder: 58 | type: str 59 | pattern: /[^\n]/ # no newline 60 | year: 61 | type: str 62 | pattern: /^\d{4}([-,]^\d{4})?$/ 63 | license: 64 | type: str 65 | pattern: /[^\n]/ # no newline 66 | requirements: &requirements 67 | type: seq 68 | sequence: 69 | - type: map 70 | mapping: 71 | name: 72 | type: str 73 | pattern: /[^\n]/ # no newline 74 | version: 75 | type: seq 76 | sequence: 77 | - type: str 78 | pattern: /^\d(\.\w+)*/ 79 | group: 80 | type: seq 81 | sequence: 82 | - type: str 83 | pattern: /[^\n]/ # no newline 84 | development: 85 | type: bool 86 | optional: 87 | type: bool 88 | engine: 89 | type: seq 90 | sequence: 91 | - type: map 92 | mapping: 93 | name: 94 | type: str 95 | pattern: /[^\n]/ # no newline 96 | version: 97 | type: str 98 | pattern: /^\d/ 99 | platform: 100 | type: seq 101 | sequence: 102 | - type: str 103 | pattern: /[^\n]/ # no newline 104 | repository: 105 | - type: map 106 | mapping: 107 | url: 108 | type: str 109 | pattern: /[^\n]/ # url 110 | scm: 111 | type: str 112 | pattern: /[^\n]/ # no newline 113 | dependencies: *requirements 114 | conflicts: 115 | type: seq 116 | sequence: 117 | - type: map 118 | mapping: 119 | name: 120 | type: str 121 | pattern: /^\w+$/ # package name 122 | version: 123 | type: seq 124 | sequence: 125 | - type: str 126 | pattern: /[^\n]/ # version constraint 127 | substitues: 128 | type: seq 129 | sequence: 130 | - type: str 131 | pattern: /^\w+$/ # package name 132 | replaces: 133 | type: seq 134 | sequence: 135 | - type: str 136 | pattern: /^\w+$/ # package name 137 | 138 | resources: 139 | type: seq 140 | sequence: 141 | - type: map 142 | mapping: 143 | id: 144 | type: str 145 | pattern: /[^\n]/ # no newline 146 | url: 147 | type: str 148 | pattern: // # url 149 | required: true 150 | repositories: 151 | type: seq 152 | sequence: 153 | - type: map 154 | mapping: 155 | id: 156 | type: str 157 | pattern: /[^\n]/ # no newline 158 | url: 159 | type: str 160 | pattern: // # url 161 | required: true 162 | scm: 163 | type: str 164 | pattern: /^\w$/ # word 165 | load_path: 166 | type: seq 167 | sequence: 168 | - type: str 169 | pattern: /\S/ # path 170 | default: [lib] 171 | install_message: 172 | type: str 173 | extra: 174 | type: map 175 | 176 | -------------------------------------------------------------------------------- /lib/indexer/attributes.rb: -------------------------------------------------------------------------------- 1 | module Indexer 2 | 3 | # Tracks supported attributes. 4 | def self.attributes 5 | @attributes ||= [] 6 | end 7 | 8 | # The Attributes module defines all of the accepted metadata fields. 9 | module Attributes 10 | 11 | # Define attribute, plus track it. 12 | def self.attr_accessor(name) 13 | Indexer.attributes << name.to_sym 14 | 15 | class_eval %{ 16 | def #{name} 17 | @data[:#{name}] 18 | end 19 | def #{name}=(val) 20 | @data[:#{name}] = val 21 | end 22 | } 23 | end 24 | 25 | # 26 | def attributes 27 | Indexer.attributes 28 | end 29 | 30 | # The revision of ruby meta specification. 31 | attr_accessor :revision 32 | 33 | #def revision 34 | # REVISION 35 | #end 36 | 37 | # The type of ruby meta specification. 38 | attr_accessor :type 39 | 40 | # Files from which to import metadata. 41 | attr_accessor :sources 42 | 43 | # The name of the project 44 | attr_accessor :name 45 | 46 | # The version of the project 47 | attr_accessor :version 48 | 49 | # The nick name for the particular version, e.g. "Lucid Lynx". 50 | attr_accessor :codename 51 | 52 | # The date of this version. 53 | attr_accessor :date 54 | 55 | # The project title 56 | attr_accessor :title 57 | 58 | # The project summary 59 | attr_accessor :summary 60 | 61 | # The project description 62 | attr_accessor :description 63 | 64 | # The suite to which the project belongs. 65 | attr_accessor :suite 66 | 67 | # The copyrights and licenses of the project. 68 | attr_accessor :copyrights 69 | 70 | # The authors of the project 71 | # The first author should be the primary contact. 72 | attr_accessor :authors 73 | 74 | # The organizations involved with the project. 75 | attr_accessor :organizations 76 | 77 | # The resource locators for the project. 78 | attr_accessor :resources 79 | 80 | # The repository URLs for the project. 81 | attr_accessor :repositories 82 | 83 | # TODO: Might webcvs simply be taken from a repository? 84 | # Or perhaps from a specifically labeled resource? 85 | 86 | # URI for linking to source code. 87 | attr_accessor :webcvs 88 | 89 | # Map of path sets which can be used to identify paths within the project. 90 | # The actual path keys largely depend on the project type, but in general 91 | # should reflect the FHS, 92 | # 93 | # For example, the `lib` path key is used by Ruby projects to designate 94 | # which project paths to search within when requiring files. 95 | attr_accessor :paths 96 | 97 | # List of language engine/version family supported. 98 | attr_accessor :engines 99 | 100 | # List of platforms supported. 101 | attr_accessor :platforms 102 | 103 | # The names of the executable scripts 104 | # NOTE: Do not need, executable should alwasy by in bin/, right? 105 | #attr_accessor :executables 106 | 107 | # The packages this package requires to function. 108 | attr_accessor :requirements #:dependencies 109 | 110 | # A list of packages that provide more or less the same functionality. 111 | # A good example is for a markdown library. 112 | # 113 | # alternatives: 114 | # - rdiscount 115 | # - redcarpet 116 | # - BlueCloth 117 | # 118 | attr_accessor :alternatives 119 | 120 | # The packages with which this project cannot function. 121 | attr_accessor :conflicts 122 | 123 | # 124 | # NOTE: This is a Debian concept. Is it useful? 125 | #attr_accessor :provides 126 | 127 | # Categories can be used to help clarify the purpose of 128 | # a project, e.g. `testing` or `rest`. There are no standard 129 | # categories, just use common sense. Categories must be single-line 130 | # strings. When comparisons are made they will be downcased. 131 | attr_accessor :categories 132 | 133 | # The version of Ruby required by the project 134 | # NOTE: is it possible to to makes this a part of ordinary requirements? 135 | #attr_accessor :required_ruby_version 136 | 137 | # The post-installation message. 138 | attr_accessor :install_message 139 | 140 | # The date the project was started. 141 | attr_accessor :created 142 | 143 | # The toplevel namespace of API, e.g. `module Foo` or `class Bar`. 144 | # NOTE: how to best handle this? 145 | attr_accessor :namespace 146 | 147 | # The names of any user-defined fields. 148 | attr_accessor :customs 149 | 150 | protected 151 | 152 | # 153 | # Initializes the {Metadata} attributes. 154 | # 155 | # @todo Is it okay to default type to `ruby`? 156 | # 157 | def initialize_attributes 158 | @data = { 159 | :revision => REVISION, 160 | :type => 'ruby', 161 | :sources => [], 162 | :authors => [], 163 | :organizations => [], 164 | :copyrights => [], 165 | :alternatives => [], 166 | :requirements => [], 167 | :conflicts => [], 168 | :repositories => [], 169 | :resources => [], 170 | :categories => [], 171 | :customs => [], 172 | :paths => {'lib' => ['lib']} 173 | } 174 | end 175 | 176 | end 177 | 178 | end 179 | -------------------------------------------------------------------------------- /work/reference/pom_classes/properties.rb: -------------------------------------------------------------------------------- 1 | require 'pom/profile/property' 2 | 3 | module POM 4 | 5 | # Project's packaging name. It can default to title downcased, 6 | # if not supplied. 7 | property :name do 8 | parse do |value| 9 | value.to_s.downcase.sub(/[^A-Za-z0-9_-]+/, '-') 10 | end 11 | end 12 | 13 | # Version number. 14 | property :version do 15 | parse do |value| 16 | VersionNumber.new(value) 17 | end 18 | validate do |value| 19 | true # TODO 20 | end 21 | end 22 | 23 | # Date this metadata was generated. 24 | property :date do 25 | parse do |val| 26 | case val 27 | when Time, Date, DateTime 28 | val 29 | else 30 | Time.parse(val) if val 31 | end 32 | end 33 | end 34 | 35 | # Title of package (this defaults to project name capitalized). 36 | property :title do 37 | parse do |value| 38 | value.to_s #.capitalize #titlecase 39 | end 40 | #default do 41 | # name.to_s.capitalize #titlecase 42 | #end 43 | end 44 | 45 | # A one-line brief description. 46 | property :summary do 47 | default do 48 | d = description.to_s.strip 49 | i = d.index(/(\.|$)/) 50 | i = 69 if i > 69 51 | d[0..i] 52 | end 53 | end 54 | 55 | # Detailed description. 56 | property :description 57 | 58 | # Colorful nick name for the particular version, e.g. "Lucid Lynx". 59 | property :codename do 60 | aliases :code 61 | end 62 | 63 | # Namespace for this program. This is only needed if it is not the default 64 | # of the +name+ capitalized and/or the toplevel namespace is not a module. 65 | # For example, +activerecord+ uses +ActiveRecord+ for it's namespace, 66 | # not Activerecord. 67 | property :namespace do 68 | parse do |ns| 69 | case ns 70 | when /^class/, /^module/ 71 | ns.strip 72 | else 73 | "module #{ns.strip}" 74 | end 75 | end 76 | end 77 | 78 | # Internal load paths. 79 | property :loadpath do 80 | aliases :path, :require_paths 81 | parse do |path| 82 | case path 83 | when NilClass 84 | ['lib'] 85 | when String 86 | path.split(/[,:;\ ]/) 87 | else 88 | path.to_a 89 | end 90 | end 91 | default ['lib'] 92 | end 93 | 94 | # Access to manifest list or file name. 95 | property :manifest do 96 | parse do |file| 97 | file.to_s 98 | end 99 | end 100 | 101 | # The SCM which the project is currently utilizing. 102 | property :scm 103 | 104 | # List of requirements. 105 | # 106 | # Rather have a separate property to restrict the Ruby 107 | # engines that can be used, use the `engine` group. 108 | property :requires do 109 | aliases :requirements, :dependencies, :gems 110 | parse do |list| 111 | PackageList.new(list) 112 | end 113 | default { PackageList.new } 114 | end 115 | 116 | # List of packages with which this project cannot function. 117 | property :conflicts do 118 | parse do |list| 119 | PackageList.new(list) 120 | end 121 | default { PackageList.new } 122 | end 123 | 124 | # List of packages that this package can replace (approx. same API). 125 | property :replaces do 126 | parse do |list| 127 | PackageList.new(list) 128 | end 129 | default { PackageList.new } 130 | end 131 | 132 | # The runtime engines and versions tested for the project. An entry 133 | # in the field does not indicate an particluar engine/platform 134 | # will not work, but only indicates which have been verified. 135 | # 136 | # A valid entry of +engine_check+ comes fomr `ruby -v`, e.g. 137 | # 138 | # ruby 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux] 139 | # 140 | property :engine_check do 141 | parse do |list| 142 | list.to_list 143 | end 144 | default [] 145 | end 146 | 147 | # The post-installation message. 148 | property :message 149 | 150 | # Name of the user-account or master-project to which this project 151 | # belongs. The suite name defaults to the project name if no entry 152 | # is given. This is also aliased as #collection. 153 | property :suite do 154 | aliases :collection 155 | end 156 | 157 | # Organization. 158 | property :organization do 159 | aliases :company 160 | end 161 | 162 | # Official contact for this project. This is typically 163 | # a name and email address. 164 | property :contact 165 | 166 | # The date the project was started. 167 | property :created 168 | 169 | # Copyright notice. Eg. "Copyright (c) 2009 Thomas Sawyer" 170 | property :copyright do 171 | default do 172 | "Copyright #{Time.now.strftime('%Y')} #{authors.first}" 173 | end 174 | end 175 | 176 | # List of license, e.g. 'Apache 2.0'. 177 | property :licenses do 178 | parse do |value| 179 | [value].flatten 180 | end 181 | default [] 182 | end 183 | 184 | # List of authors. 185 | property :authors do 186 | parse do |value| 187 | [value].flatten 188 | end 189 | default [] 190 | end 191 | 192 | # List of maintainers. 193 | property :maintainers do 194 | parse do |value| 195 | [value].flatten 196 | end 197 | default [] 198 | end 199 | 200 | # Table of project URLs encapsulated in a Resources object. 201 | property :resources do 202 | parse do |value| 203 | Resources.new(value || {}) 204 | end 205 | default do 206 | Resources.new 207 | end 208 | end 209 | 210 | # Returns a Hash of +Type+ => +URI+ for SCM repository. 211 | property :repositories do 212 | default {{}} # Repositories.new 213 | end 214 | 215 | end 216 | 217 | -------------------------------------------------------------------------------- /work/deprecated/resources/lib/indexer/r2013/resources.rb: -------------------------------------------------------------------------------- 1 | module Indexer; module V0 2 | 3 | # The Resources class models a table of project releated URIs. 4 | # Each entry has a name and URI. The class is Enumerable so 5 | # each entry can be iterated over, much like a Hash. 6 | # 7 | # The class also recognizes common entry names and aliases, 8 | # which can be accessed via method calls. 9 | # 10 | class Resources < Model 11 | include Enumerable 12 | 13 | # TODO: Consider if this should instead be an associative array 14 | # of [type, uri]. Could there not be more than one URL for 15 | # a given type? 16 | 17 | # TODO: Resource aliases are probably the trickiest part of this 18 | # specification. It's hard to judge what exactly they should be. 19 | 20 | ## Valid URL regular expression. 21 | #URL = /^(\w+)\:\/\/\S+$/ 22 | 23 | @key_aliases = {} 24 | 25 | # 26 | def self.key_aliases 27 | @key_aliases 28 | end 29 | 30 | # 31 | def self.attr_accessor *aliases 32 | code = [] 33 | aliases.each do |method| 34 | key_aliases[method.to_sym] = aliases 35 | code << "def #{method}" 36 | code << " self['#{name}']" 37 | code << "end" 38 | code << "def #{method}=(val)" 39 | code << " self['#{name}'] = val" 40 | code << "end" 41 | end 42 | module_eval code.join("\n") 43 | end 44 | 45 | # New Resources object. The initializer can 46 | # take a hash of name to URL. 47 | def initialize(data={}) 48 | @table = {} 49 | 50 | data = {} if data.nil? 51 | 52 | data.each do |key, uri| 53 | self[key] = uri 54 | end 55 | end 56 | 57 | # 58 | #def key_index(key) 59 | # key = key.to_sym 60 | # self.class.key_aliases[key] || key 61 | #end 62 | 63 | # 64 | def key_aliases 65 | self.class.key_aliases 66 | end 67 | 68 | # Get a resource URI. 69 | # 70 | def [](key) 71 | #@table[key_index(key)] 72 | @table[find_key(key)] 73 | end 74 | 75 | # Add a resource. 76 | # 77 | # This is rather inefficient, but it does the job right. 78 | # If there is an optimized way to go about it, hey, let us know. 79 | # 80 | def []=(key, uri) 81 | unless Valid.url?(uri) or Valid.irc?(uri) 82 | raise ValidationError, "Not a valid URI - `#{uri}' for `#{key}'" 83 | end 84 | #@table[key_index(key)] = uri 85 | select_keys(key).each do |k| 86 | @table.delete(k) 87 | end 88 | @table[key.to_sym] = uri 89 | end 90 | 91 | # Offical project website. 92 | attr_accessor :homepage, :home 93 | 94 | # Location of development site. 95 | attr_accessor :development, :work, :dev 96 | 97 | # Package distribution service webpage. 98 | attr_accessor :gem, :distro, :distributor 99 | 100 | # Location to downloadable package(s). 101 | attr_accessor :download 102 | 103 | # Browse source code. 104 | attr_accessor :code, :source, :source_code 105 | 106 | # User discussion forum. 107 | attr_accessor :forum 108 | 109 | # Location of support forum. 110 | attr_accessor :support 111 | 112 | # Mailing list email or web address to online version. 113 | attr_accessor :mail, :email, :mailinglist 114 | 115 | # Location of issue tracker. 116 | attr_accessor :bugs, :issues 117 | 118 | # Location of documentation. 119 | attr_accessor :docs, :documentation, :doc 120 | 121 | # Location of API reference documentation. 122 | attr_accessor :api, :reference, :system_guide 123 | 124 | # Location of wiki. 125 | attr_accessor :wiki, :user_guide 126 | 127 | # Resource to project blog. 128 | attr_accessor :blog, :weblog 129 | 130 | # IRC channel 131 | attr_accessor :irc, :chat 132 | 133 | # Convert to Hash by duplicating the underlying 134 | # hash table. 135 | def to_hash 136 | @table.dup 137 | end 138 | 139 | # Convert table to hash with string keys. 140 | def to_h 141 | h = {} 142 | @table.each do |k,v| 143 | h[k.to_s] = v 144 | end 145 | return h 146 | end 147 | 148 | # 149 | def empty? 150 | @table.empty? 151 | end 152 | 153 | # Iterate over each enty. 154 | def each(&block) 155 | @table.each(&block) 156 | end 157 | 158 | # The size of the table. 159 | def size 160 | @table.size 161 | end 162 | 163 | # 164 | def to_a 165 | @table.map{ |k,v| [k,v] } 166 | end 167 | 168 | # 169 | def inspect 170 | @table.inspect 171 | end 172 | 173 | # 174 | def merge!(res) 175 | res.each do |key, uri| 176 | self[key] = uri 177 | end 178 | end 179 | 180 | # Locate an entry with a matching key. 181 | def find_key(key) 182 | names = (key_aliases[key] || []) + [key] 183 | @table.keys.each do |k| 184 | return k if k.to_s.downcase =~ /(#{names.join('|').downcase})/ 185 | end 186 | key 187 | end 188 | 189 | # 190 | def select_keys(key) 191 | names = (key_aliases[key] || []) + [key] 192 | @table.keys.select do |k| 193 | k.to_s.downcase =~ /(#{names.join('|').downcase})/ 194 | end 195 | end 196 | 197 | # If a method is missing and it is a setter method 198 | # (ending in '=') then a new entry by that name 199 | # will be added to the table. If a plain method 200 | # then the name will be looked for in the table. 201 | def method_missing(sym, *args) 202 | meth = sym.to_s 203 | name = meth.chomp('=') 204 | case meth 205 | when /=$/ 206 | self[name] = args.first 207 | else 208 | super(sym, *args) if block_given? or args.size > 0 209 | self[name] 210 | end 211 | end 212 | 213 | end 214 | 215 | end; end 216 | --------------------------------------------------------------------------------