├── var ├── name ├── title ├── version ├── created ├── license ├── organization ├── summary ├── authors ├── copyrights ├── repositories ├── requirements ├── resources └── description ├── lib ├── rspecial.yml ├── assay │ └── rspec.rb ├── rspecial.rb └── rspecial │ ├── should.rb │ ├── operatics.rb │ ├── expect.rb │ ├── have.rb │ └── matchers.rb ├── Gemfile ├── .ruby ├── demo ├── applique │ ├── setup.rb │ └── helper.rb ├── 04_expect.md ├── 01_should.md ├── 00_introduction.md ├── 03_have.md └── 02_matchers.md ├── .gitignore ├── .travis.yml ├── Config.rb ├── MANIFEST ├── Assembly ├── HISTORY.md ├── LICENSE.txt ├── .index ├── README.md ├── work └── have.rb └── .gemspec /var/name: -------------------------------------------------------------------------------- 1 | rspecial 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | RSpecial 2 | -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 0.3.0 2 | -------------------------------------------------------------------------------- /lib/rspecial.yml: -------------------------------------------------------------------------------- 1 | ../.index -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2012-01-18 2 | -------------------------------------------------------------------------------- /var/license: -------------------------------------------------------------------------------- 1 | BSD-2-Clause 2 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | Rubyworks -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/assay/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'rspecial' 2 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Brass-balled RSpec Expectations 2 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Thomas Sawyer 3 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-linux] 2 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - Copyright (c) 2012 Rubyworks (BSD-2-Clause) 3 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git@github.com:rubyworks/rspecial.git 3 | -------------------------------------------------------------------------------- /demo/applique/setup.rb: -------------------------------------------------------------------------------- 1 | require 'rspecial' 2 | include ::RSpecial::Matchers 3 | 4 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - assay 3 | - detroit (build) 4 | - qed (test) 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | log 4 | pkg 5 | ri 6 | tmp 7 | site 8 | web 9 | work/trash 10 | DEMOS.md 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | script: "bundle exec qed -Ilib demo/" 3 | rvm: 4 | - 1.9.2 5 | - 1.9.3 6 | - rbx-19mode 7 | - jruby-19mode 8 | 9 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/rspecial 3 | docs: http://rubydoc.info/gems/rspecial 4 | code: http://github.com/rubyworks/rspecial 5 | mail: http://groups.google.com/groups/rubyworks-mailinglist 6 | chat: http://chat.us.feenode.net/rubyworks 7 | 8 | -------------------------------------------------------------------------------- /Config.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Test coverage report. 4 | profile :coverage do 5 | 6 | # Setup QED. 7 | config :qed do 8 | require 'simplecov' 9 | SimpleCov.start do 10 | coverage_dir 'log/coverage' 11 | end 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | RSpecial defines a set of RSpec-compatible matcher methods which 2 | are BRASS compliant by using Assay as a backend. This allows developers 3 | to switch to BRASS compliant test frameworks without having to change 4 | a slew of previously written RSpec-based specifications. 5 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast -x *.lock .index .ruby .yardopts bin data lib man share spec test *.md *.rdoc *.txt 2 | .index 3 | .ruby 4 | lib/assay/rspec.rb 5 | lib/rspecial/expect.rb 6 | lib/rspecial/have.rb 7 | lib/rspecial/matchers.rb 8 | lib/rspecial/operatics.rb 9 | lib/rspecial/should.rb 10 | lib/rspecial.rb 11 | lib/rspecial.yml 12 | HISTORY.md 13 | README.md 14 | DEMOS.md 15 | LICENSE.txt 16 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | 5 | gem: 6 | active: true 7 | 8 | dnote: 9 | title: Source Notes 10 | output: log/notes.html 11 | 12 | vclog: 13 | output: 14 | - log/history.html 15 | - log/changes.html 16 | 17 | email: 18 | mailto: 19 | - ruby-talk@ruby-lang.org 20 | - rubyworks-mailinglist@googlegroups.com 21 | 22 | qed: 23 | files: demo/ 24 | 25 | qedoc: 26 | files: demo/ 27 | output: 28 | - DEMOS.md 29 | - web/demos.html 30 | 31 | -------------------------------------------------------------------------------- /demo/04_expect.md: -------------------------------------------------------------------------------- 1 | ## RSpec Expect 2 | 3 | ### expect 4 | 5 | Expect is RSpecs new and approved assertion notation. It works 6 | very much like `#should` but it isn't an Object extension, which 7 | is considered a "*better practice*". 8 | 9 | expect(10).to eq(10) 10 | 11 | ### operators 12 | 13 | It can also delegate operators, so the above example can also be 14 | written. 15 | 16 | expect(10).to == 10 17 | 18 | assert_raises EqualAssay do 19 | expect(10).to == 20 20 | end 21 | 22 | -------------------------------------------------------------------------------- /demo/01_should.md: -------------------------------------------------------------------------------- 1 | ## RSpec Should Method 2 | 3 | ### should 4 | 5 | Becuase the `#should` method used `=~` as the assertion operator, 6 | you can do something a bit unexpected, such as, 7 | 8 | 'a'.should /a/ 9 | 10 | ### should= 11 | 12 | As a shortcut one can also use the `#should=` method instead of 13 | the usual two-step `should ==` call. 14 | 15 | 10.should = 10 16 | 17 | assert_raises ::EqualAssay do 18 | 10.should = 20 19 | end 20 | 21 | The same is true for `#should_not`. 22 | 23 | 10.should_not = 20 24 | 25 | -------------------------------------------------------------------------------- /lib/rspecial.rb: -------------------------------------------------------------------------------- 1 | # RSpecial (http://rubyworks.github.com/rspecial) 2 | # Copyright (c) 2012 Rubyworks. 3 | # License (spdx) BSD-2-Clause. 4 | 5 | require 'assay' 6 | require 'rspecial/matchers' 7 | require 'rspecial/have' 8 | require 'rspecial/should' 9 | require 'rspecial/expect' 10 | require 'rspecial/operatics' 11 | 12 | # This Test::Matchers module holds all matcher methods, which can be mixed into 13 | # one's testing scope (e.g. World). 14 | # 15 | module Test 16 | module Matchers 17 | include RSpecial::Matchers 18 | end 19 | end 20 | 21 | -------------------------------------------------------------------------------- /demo/00_introduction.md: -------------------------------------------------------------------------------- 1 | # RSpecial 2 | 3 | ## Setup 4 | 5 | First we need to require the library. 6 | 7 | require 'rspecial' 8 | 9 | This will load Assay and the RSpec extension, which will add `#should` 10 | and `#should_not` to `BasicObject` class, and create a module called 11 | `RSpecial::Matchers`. This moudle is included into `Assay::Matchers`. 12 | To make use of it, we simply need to include either of these modules into 13 | our test scope. 14 | 15 | include RSpecial::Matchers 16 | 17 | With that in place, we are ready to use the matchers. 18 | 19 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # HISTORY 2 | 3 | ## 0.3.0 | 2012-03-16 4 | 5 | Renamed project from *Assay RSpec* to *RSpecial*, and added 6 | RSpec's new expect notation. 7 | 8 | Changes: 9 | 10 | * Rename project to respecial. 11 | * Add RSpec's new expect notation. 12 | 13 | 14 | ## 0.2.0 | 2012-02-26 15 | 16 | This release adds support for #have matchers. 17 | 18 | Changes: 19 | 20 | * Add support for have matchers. 21 | 22 | 23 | ## 0.1.0 | 2012-01-26 24 | 25 | This is the initial release of Assay RSpec. Currently the library covers 26 | every RSpec matcher except the `change` and `have` matchers. 27 | 28 | Changes: 29 | 30 | * Happy Birthday! 31 | 32 | -------------------------------------------------------------------------------- /demo/applique/helper.rb: -------------------------------------------------------------------------------- 1 | # Very simple helper assertion system, so we can test 2 | # Assay without name clashes. 3 | 4 | def assert(truth, msg=nil, trace=nil) 5 | if truth 6 | increment_counts(:pass) 7 | else 8 | increment_counts(:fail) 9 | raise Assertion, msg || "assert failed", trace || caller 10 | end 11 | end 12 | 13 | def refute(truth) 14 | assert(!truth, "refute failed", caller) 15 | end 16 | 17 | def assert_raises(error) 18 | counts = $ASSERTION_COUNTS.dup 19 | begin 20 | yield 21 | $ASSERTION_COUNTS = counts 22 | increment_counts(:fail) 23 | raise Assertion, "#{error} not raised.", caller 24 | rescue error 25 | $ASSERTION_COUNTS = counts 26 | increment_counts(:pass) 27 | end 28 | end 29 | 30 | def increment_counts(which) 31 | case which 32 | when :pass 33 | $ASSERTION_COUNTS[:pass] += 1 34 | when :fail 35 | $ASSERTION_COUNTS[:fail] += 1 36 | end 37 | $ASSERTION_COUNTS[:total] += 1 38 | end 39 | 40 | -------------------------------------------------------------------------------- /lib/rspecial/should.rb: -------------------------------------------------------------------------------- 1 | module RSpecial 2 | 3 | # The Should module is included into BasicObject to provide 4 | # the needed assertion interface that RSpec utilizes. Namely, the 5 | # `should` and `should_not` methods. 6 | # 7 | module Should 8 | 9 | # 10 | # Use `should` nomenclature for assertions. 11 | # 12 | # 10.should be_kind_of(Integer) 13 | # 14 | def should(matcher) 15 | matcher =~ self 16 | end 17 | 18 | # 19 | # Also, `should_not` nomenclature for assertions. 20 | # 21 | # 10.should_not be_kind_of?(Integer) 22 | # 23 | def should_not(matcher) 24 | matcher !~ self 25 | end 26 | 27 | # 28 | # 29 | # 30 | def should=(value) 31 | EqualAssay.assert!(self, value) 32 | end 33 | 34 | # 35 | # 36 | # 37 | def should_not=(value) 38 | EqualAssay.refute!(self, value) 39 | end 40 | 41 | end 42 | 43 | end 44 | 45 | 46 | class BasicObject # Object ? 47 | include ::RSpecial::Should 48 | end 49 | 50 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD-2-Clause License 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 14 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 16 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /demo/03_have.md: -------------------------------------------------------------------------------- 1 | ### Have 2 | 3 | The `#have` matchers make it easy to set expectations about the size 4 | of a collection. In general the term `items` is used if the target 5 | object is a collection. 6 | 7 | a = [1, 2, 3] 8 | 9 | a.should have(3).items 10 | a.should_not have(2).items 11 | a.should_not have(4).items 12 | 13 | a.should have_exactly(3).items 14 | a.should_not have_exactly(2).items 15 | a.should_not have_exactly(4).items 16 | 17 | a.should have_at_least(2).items 18 | a.should have_at_most(4).items 19 | 20 | But a method that returns a collection can be used instead. 21 | 22 | class String 23 | def words 24 | split(' ') 25 | end 26 | end 27 | 28 | s = "a sentence with some words" 29 | 30 | s.should have(5).words 31 | s.should_not have(4).words 32 | s.should_not have(6).words 33 | 34 | s.should have_exactly(5).words 35 | s.should_not have_exactly(4).words 36 | s.should_not have_exactly(6).words 37 | 38 | s.should have_at_least(4).words 39 | s.should have_at_most(6).words 40 | 41 | Besides `#have` there is also `#have_at_least` and `#have_at_most`. 42 | 43 | a = [1, 2, 3] 44 | 45 | a.should have_at_least(2).items 46 | a.should have_at_most(4).items 47 | 48 | a.should have_at_least(3).items 49 | a.should have_at_most(3).items 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/rspecial/operatics.rb: -------------------------------------------------------------------------------- 1 | module RSpecial 2 | 3 | # Operatics delegates operator based assertions for execpt.rb. 4 | # 5 | class Operatics < Object 6 | instance_methods.each{ |m| undef_method m if m.to_s =~ /^\W/ } 7 | 8 | # 9 | def initialize(target, negate=false) 10 | @target = target 11 | @negate = negate 12 | end 13 | 14 | private 15 | 16 | # 17 | def method_missing(op, *a, &b) 18 | super(op, *a, &b) if op.to_s =~ /^\w/ 19 | 20 | if @negate 21 | refute!(op, *a, &b) 22 | else 23 | assert!(op, *a, &b) 24 | end 25 | end 26 | 27 | # 28 | def assert!(op, *a, &b) 29 | if assay = Assertion.by_operator(op) 30 | return assay.assert!(@target, *a, &b) 31 | else 32 | assert! @target.send(op, *a, &b) 33 | end 34 | end 35 | 36 | # 37 | def refute!(op, *a, &b) 38 | if assay = Assertion.by_operator(op) 39 | return assay.refute!(@target, *a, &b) 40 | else 41 | refute! @target.send(op, *a, &b) 42 | end 43 | end 44 | 45 | # 46 | def generic?(op) 47 | @target.method(op).owner == ::Kernel 48 | end if ::Method.method_defined?(:owner) 49 | 50 | # 51 | def generic?(op) 52 | @target.method(op).to_s.include?('(Kernel)') 53 | end unless ::Method.method_defined?(:owner) 54 | 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | type: ruby 3 | revision: 2013 4 | sources: 5 | - var 6 | authors: 7 | - name: Thomas Sawyer 8 | email: transfire@gmail.com 9 | organizations: [] 10 | requirements: 11 | - name: assay 12 | - groups: 13 | - build 14 | development: true 15 | name: detroit 16 | - groups: 17 | - test 18 | development: true 19 | name: qed 20 | conflicts: [] 21 | alternatives: [] 22 | resources: 23 | - type: home 24 | uri: http://rubyworks.github.com/rspecial 25 | label: Homepage 26 | - type: docs 27 | uri: http://rubydoc.info/gems/rspecial 28 | label: Documentation 29 | - type: code 30 | uri: http://github.com/rubyworks/rspecial 31 | label: Source Code 32 | - type: mail 33 | uri: http://groups.google.com/groups/rubyworks-mailinglist 34 | label: Mailing List 35 | - type: chat 36 | uri: http://chat.us.feenode.net/rubyworks 37 | label: IRC Channel 38 | repositories: 39 | - name: upstream 40 | scm: git 41 | uri: git@github.com:rubyworks/rspecial.git 42 | categories: [] 43 | paths: 44 | load: 45 | - lib 46 | copyrights: 47 | - holder: Rubyworks 48 | year: '2012' 49 | license: BSD-2-Clause 50 | created: '2012-01-18' 51 | summary: Brass-balled RSpec Expectations 52 | title: RSpecial 53 | version: 0.3.0 54 | name: rspecial 55 | description: ! 'RSpecial defines a set of RSpec-compatible matcher methods which 56 | 57 | are BRASS compliant by using Assay as a backend. This allows developers 58 | 59 | to switch to BRASS compliant test frameworks without having to change 60 | 61 | a slew of previously written RSpec-based specifications.' 62 | date: '2012-12-20' 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RSpecial 2 | 3 | [Homepage](http://rubyworks.github.com/rspecial) / 4 | [Report Issue](http://github.com/rubyworks/rspecial/issues) / 5 | [Source Code](http://github.com/rubyworks/rspecial) 6 | ( [![Build Status](https://secure.travis-ci.org/rubyworks/rspecial.png)](http://travis-ci.org/rubyworks/rspecial) ) 7 | 8 | _Well, isn't that special._ 9 | 10 |
11 | 12 | RSpecial is an BRASS-compliant *assertions framework*. It defines 13 | the RSpec's `expect` and `should` handlers and a set of RSpec-compatible matchers 14 | allowing developers to change test frameworks without having to change 15 | a slew of previously defined assertions. 16 | 17 | RSpecial utilizes the [Assay](http://rubyworks.github.com/assay) 18 | assertions meta-framework on the back-end. Assay defines assertions 19 | in the same way that Ruby defines exceptions. An assertion is nothing 20 | more that an extended Exception class. Assay provides a complete set 21 | of these assertion classes for all common assertion needs. 22 | 23 | 24 | ## Installation 25 | 26 | To install with RubyGems simply open a console and type: 27 | 28 | $ gem install rspecial 29 | 30 | Old-school site installations via a tarball can be done with [Ruby Setup](http://rubyworks.github.com/setup) 31 | (`gem install setup`). 32 | 33 | 34 | ## Basic Usage 35 | 36 | To use RSpecial, simply require the `rspecial` script, and include the `RSpecial::Matchers` 37 | or `Test::Matchers` mixin module into your test framework wherever it requires 38 | it (which may be as simple as the top-level namespace). 39 | 40 | require 'rspecial' 41 | 42 | include RSpecial::Matchers 43 | 44 | Or more generically, 45 | 46 | include Test::Matchers 47 | 48 | Now assertions can be made just as if you were using RSpec. 49 | 50 | expect(10).to be_kind_of(Integer) 51 | 52 | and the traditional way 53 | 54 | 10.should be_kind_of(Integer) 55 | 56 | 57 | ## Limitations 58 | 59 | Compatibility with RSpec is not 100%, but it is close. Compatibility will improve 60 | with future releases. 61 | 62 | Also error messages aren't always as nice, nor always customizable, as they are in RSpec. 63 | This too will improve with future releases. 64 | 65 | Please feel _obligated_ to submit a patch if you need a missing a feature ;-) 66 | 67 | 68 | ## Copyrights 69 | 70 | RSpecial is copyright open source software 71 | 72 | Copyright (c) 2012 Rubyworks 73 | 74 | This program is distributed under the terms of the [BSD-2-Clause](http://spdx.org/licenses/BSD-2-Clause) license. 75 | 76 | See LICENSE.txt file for details. 77 | -------------------------------------------------------------------------------- /demo/02_matchers.md: -------------------------------------------------------------------------------- 1 | ## RSpec Matchers 2 | 3 | ### should equal 4 | 5 | 1.should = 1 6 | 7 | assert_raises ::EqualAssay do 8 | 1.should = 2 9 | end 10 | 11 | ### be_true 12 | 13 | true.should be_true 14 | 15 | assert_raises ::TrueAssay do 16 | false.should be_true 17 | end 18 | 19 | ### be_false 20 | 21 | false.should be_false 22 | 23 | assert_raises ::FalseAssay do 24 | true.should be_false 25 | end 26 | 27 | ### be_nil 28 | 29 | nil.should be_nil 30 | 31 | assert_raises ::NilAssay do 32 | true.should be_nil 33 | end 34 | 35 | ### be_empty 36 | 37 | [].should be_empty 38 | 39 | assert_raises ::EmptyAssay do 40 | [1].should be_empty 41 | end 42 | 43 | [1].should_not be_empty 44 | 45 | ### be_close 46 | 47 | 1.should be_close(1.5, 2) 48 | 49 | assert_raises ::WithinAssay do 50 | 1.should be_close(0.5, 2) 51 | end 52 | 53 | 1.should_not be_close(0.5, 2) 54 | 55 | ### match 56 | 57 | "abc".should match(/a/) 58 | 59 | assert_raises ::MatchAssay do 60 | "abc".should match(/x/) 61 | end 62 | 63 | "abc".should_not match(/g/) 64 | 65 | ### eql 66 | 67 | 1.should eql(1) 68 | 69 | assert_raises ::EqualityAssay do 70 | 1.should eql(1.0) 71 | end 72 | 73 | 1.should_not eql(1.0) 74 | 75 | ### equal 76 | 77 | :a.should equal(:a) 78 | 79 | assert_raises ::IdentityAssay do 80 | "a".should equal("a") 81 | end 82 | 83 | :a.should_not equal('a') 84 | 85 | ### be_instance_of 86 | 87 | 1.should be_instance_of(Fixnum) 88 | 89 | assert_raises ::InstanceAssay do 90 | 1.should be_instance_of(String) 91 | end 92 | 93 | 1.should_not be_instance_of(String) 94 | 95 | ### be_kind_of 96 | 97 | 1.should be_kind_of(Integer) 98 | 99 | assert_raises ::KindAssay do 100 | 1.should be_kind_of(String) 101 | end 102 | 103 | 1.should_not be_kind_of(String) 104 | 105 | ### raise_error 106 | 107 | procedure = lambda{ raise ::ArgumentError } 108 | 109 | procedure.should raise_error(::ArgumentError) 110 | 111 | assert_raises ::RaiseAssay do 112 | procedure.should raise_error(::TypeError) 113 | end 114 | 115 | procedure.should_not raise_error(::TypeError) 116 | 117 | ### respond_to 118 | 119 | "string".should respond_to(:upcase) 120 | 121 | assert_raises ::RespondAssay do 122 | "string".should respond_to(:not_a_method) 123 | end 124 | 125 | "string".should_not respond_to(:not_a_method) 126 | 127 | ### satisfy 128 | 129 | 5.should satisfy{ |x| x > 3 } 130 | 131 | assert_raises ::ExecutionAssay do 132 | 5.should satisfy{ |x| x < 3 } 133 | end 134 | 135 | 5.should_not satisfy{ |x| x < 3 } 136 | 137 | ### throw_symbol 138 | 139 | procedure = lambda{ throw :foo } 140 | 141 | procedure.should throw_symbol(:foo) 142 | 143 | assert_raises ::ThrowAssay do 144 | procedure.should throw_symbol(:bar) 145 | end 146 | 147 | procedure.should_not throw_symbol(:bar) 148 | 149 | ## Supplemental Matchers 150 | 151 | ### equate_to 152 | 153 | This is not strictly an RSpec matcher, but we have thrown it in for good measure. 154 | It is equivalent to using the `#should=` method. 155 | 156 | 10.should equate_to(10) 157 | 158 | assert_raises ::EqualityAssay do 159 | 10.should equate_to(10.0) 160 | end 161 | 162 | 10.should_not equate_to(10.0) 163 | 164 | ### be_like 165 | 166 | The `#be_like` matcher is not strictly an RSpec matcher, but we have thrown it 167 | in for good measure. 168 | 169 | /a/.should be_like('a') 170 | 171 | assert_raises ::LikeAssay do 172 | /a/.should be_like('b') 173 | end 174 | 175 | /a/.should_not be_like('b') 176 | 177 | -------------------------------------------------------------------------------- /work/have.rb: -------------------------------------------------------------------------------- 1 | module Assay 2 | 3 | module RSpec 4 | 5 | # 6 | # TODO: Note this is the code from RSpec for `#have`. It needs to rewritten 7 | # into a form usable by Assay. 8 | # 9 | class Have 10 | 11 | def initialize(expected, relativity=:exactly) 12 | @expected = case expected 13 | when :no then 0 14 | when String then expected.to_i 15 | else expected 16 | end 17 | @relativity = relativity 18 | @actual = @collection_name = @plural_collection_name = nil 19 | end 20 | 21 | def relativities 22 | @relativities ||= { 23 | :exactly => "", 24 | :at_least => "at least ", 25 | :at_most => "at most " 26 | } 27 | end 28 | 29 | def matches?(collection_or_owner) 30 | collection = determine_collection(collection_or_owner) 31 | query_method = determine_query_method(collection) 32 | raise not_a_collection unless query_method 33 | @actual = collection.send(query_method) 34 | case @relativity 35 | when :at_least then @actual >= @expected 36 | when :at_most then @actual <= @expected 37 | else @actual == @expected 38 | end 39 | end 40 | 41 | alias_method :pass?, :matches? 42 | alias_method :===, :matches? 43 | alias_method :=~, :matches? 44 | 45 | 46 | def determine_collection(collection_or_owner) 47 | if collection_or_owner.respond_to?(@collection_name) 48 | collection_or_owner.send(@collection_name, *@args, &@block) 49 | elsif (@plural_collection_name && collection_or_owner.respond_to?(@plural_collection_name)) 50 | collection_or_owner.send(@plural_collection_name, *@args, &@block) 51 | elsif determine_query_method(collection_or_owner) 52 | collection_or_owner 53 | else 54 | collection_or_owner.send(@collection_name, *@args, &@block) 55 | end 56 | end 57 | 58 | def determine_query_method(collection) 59 | [:size, :length, :count].detect {|m| collection.respond_to?(m)} 60 | end 61 | 62 | def not_a_collection 63 | "expected #{@collection_name} to be a collection but it does not respond to #length, #size or #count" 64 | end 65 | 66 | def failure_message_for_should 67 | "expected #{relative_expectation} #{@collection_name}, got #{@actual}" 68 | end 69 | 70 | def failure_message_for_should_not 71 | if @relativity == :exactly 72 | return "expected target not to have #{@expected} #{@collection_name}, got #{@actual}" 73 | elsif @relativity == :at_most 74 | return <<-EOF 75 | Isn't life confusing enough? 76 | Instead of having to figure out the meaning of this: 77 | should_not have_at_most(#{@expected}).#{@collection_name} 78 | We recommend that you use this instead: 79 | should have_at_least(#{@expected + 1}).#{@collection_name} 80 | EOF 81 | elsif @relativity == :at_least 82 | return <<-EOF 83 | Isn't life confusing enough? 84 | Instead of having to figure out the meaning of this: 85 | should_not have_at_least(#{@expected}).#{@collection_name} 86 | We recommend that you use this instead: 87 | should have_at_most(#{@expected - 1}).#{@collection_name} 88 | EOF 89 | end 90 | end 91 | 92 | def description 93 | "have #{relative_expectation} #{@collection_name}" 94 | end 95 | 96 | def respond_to?(m) 97 | @expected.respond_to?(m) || super 98 | end 99 | 100 | private 101 | 102 | def method_missing(method, *args, &block) 103 | @collection_name = method 104 | if inflector = (defined?(ActiveSupport::Inflector) && ActiveSupport::Inflector.respond_to?(:pluralize) ? ActiveSupport::Inflector : (defined?(Inflector) ? Inflector : nil)) 105 | @plural_collection_name = inflector.pluralize(method.to_s) 106 | end 107 | @args = args 108 | @block = block 109 | self 110 | end 111 | 112 | def relative_expectation 113 | "#{relativities[@relativity]}#{@expected}" 114 | end 115 | 116 | end 117 | 118 | end 119 | 120 | end 121 | -------------------------------------------------------------------------------- /lib/rspecial/expect.rb: -------------------------------------------------------------------------------- 1 | module RSpecial 2 | 3 | # Wraps the target of an expectation. 4 | # 5 | # @example 6 | # expect(something) # => Expect 7 | # 8 | # # used with `to` 9 | # expect(actual).to eq(3) 10 | # 11 | # # with `to_not` 12 | # expect(actual).to_not eq(3) 13 | # 14 | class Expect 15 | 16 | # @api private 17 | def initialize(target) 18 | @target = target 19 | end 20 | 21 | # Runs the given expectation, passing if `matcher` returns true. 22 | # 23 | # @example 24 | # expect(value).to eq(5) 25 | # expect { perform }.to raise_error 26 | # 27 | # @param [Matcher] 28 | # matcher 29 | # 30 | # @param [String] message optional message to display when the expectation fails 31 | # 32 | # @return [Boolean] true if the expectation succeeds (else raises) 33 | # 34 | # @see RSpec::Matchers 35 | # 36 | def to(matcher=nil, message=nil, &block) 37 | #prevent_operator_matchers(:to, matcher) 38 | handle_positive_matcher(@target, matcher, message, &block) 39 | end 40 | 41 | # Runs the given expectation, passing if `matcher` returns false. 42 | # 43 | # @example 44 | # expect(value).to_not eq(5) 45 | # expect(value).not_to eq(5) 46 | # 47 | # @param [Matcher] 48 | # matcher 49 | # 50 | # @param [String] message optional message to display when the expectation fails 51 | # 52 | # @return [Boolean] false if the negative expectation succeeds (else raises) 53 | # 54 | # @see RSpec::Matchers 55 | # 56 | def to_not(matcher=nil, message=nil, &block) 57 | #prevent_operator_matchers(:to_not, matcher) 58 | handle_negative_matcher(@target, matcher, message, &block) 59 | end 60 | 61 | alias :not_to :to_not 62 | 63 | private 64 | 65 | #def prevent_operator_matchers(verb, matcher) 66 | # return if matcher 67 | # 68 | # raise ArgumentError, "The expect syntax does not support operator matchers, " + 69 | # "so you must pass a matcher to `##{verb}`." 70 | #end 71 | 72 | # 73 | # @todo how to customize the message? 74 | # 75 | def handle_positive_matcher(actual, matcher, message=nil, &block) 76 | check_message(message) 77 | 78 | #::RSpec::Matchers.last_should = :should 79 | #::RSpec::Matchers.last_matcher = matcher 80 | 81 | if block 82 | actual = block.call(actual) 83 | end 84 | 85 | #return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) if matcher.nil? 86 | 87 | return Operatics.new(actual) if matcher.nil? 88 | 89 | matcher =~ actual 90 | 91 | #match = matcher.matches?(actual, &block) 92 | #return match if match 93 | 94 | #message ||= matcher.respond_to?(:failure_message_for_should) ? 95 | # matcher.failure_message_for_should : 96 | # matcher.failure_message 97 | 98 | #if matcher.respond_to?(:diffable?) && matcher.diffable? 99 | # ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual 100 | #else 101 | # ::RSpec::Expectations.fail_with message 102 | #end 103 | end 104 | 105 | # 106 | # @todo how to customize the message? 107 | # 108 | def handle_negative_matcher(actual, matcher, message=nil, &block) 109 | check_message(message) 110 | 111 | #::RSpec::Matchers.last_should = :should_not 112 | #::RSpec::Matchers.last_matcher = matcher 113 | 114 | if block 115 | actual = block.call(actual) 116 | end 117 | 118 | #return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) if matcher.nil? 119 | 120 | return Operatics.new(actual, true) if matcher.nil? 121 | 122 | matcher !~ actual 123 | 124 | #match = matcher.respond_to?(:does_not_match?) ? 125 | # !matcher.does_not_match?(actual, &block) : 126 | # matcher.matches?(actual, &block) 127 | #return match unless match 128 | 129 | #message ||= matcher.respond_to?(:failure_message_for_should_not) ? 130 | # matcher.failure_message_for_should_not : 131 | # matcher.negative_failure_message 132 | 133 | #if matcher.respond_to?(:diffable?) && matcher.diffable? 134 | # ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual 135 | #else 136 | # ::RSpec::Expectations.fail_with message 137 | #end 138 | end 139 | 140 | def message_must_be_string(msg) 141 | "WARNING: ignoring the provided expectation message argument " + 142 | "(#{msg.inspect}) since it is not a string." 143 | end 144 | 145 | def check_message(msg) 146 | ::Kernel.warn message_must_be_string(msg) unless msg.nil? || msg.is_a?(String) 147 | end 148 | 149 | end 150 | 151 | end 152 | -------------------------------------------------------------------------------- /lib/rspecial/have.rb: -------------------------------------------------------------------------------- 1 | module RSpecial 2 | 3 | # Delegate to Have Assays. 4 | # 5 | class Have 6 | 7 | attr :expected 8 | 9 | attr :collection_name 10 | 11 | attr :relation 12 | 13 | # 14 | # Initialize new Have delegator. 15 | # 16 | def initialize(expected, relation=:exactly) 17 | @expected = case expected 18 | when :no then 0 19 | when String then expected.to_i 20 | else expected 21 | end 22 | @relation = relation 23 | @collection_name = nil 24 | end 25 | 26 | # 27 | # @param [#size,#length,#count] collection_or_owner 28 | # 29 | def collection(collection_or_owner) 30 | if collection_or_owner.respond_to?(@collection_name) 31 | collection_or_owner.__send__(@collection_name, *@args, &@block) 32 | elsif query_method(collection_or_owner) 33 | collection_or_owner 34 | else 35 | collection_or_owner.__send__(@collection_name, *@args, &@block) 36 | end 37 | end 38 | 39 | # 40 | # 41 | # 42 | def query_method(collection) 43 | [:size, :length, :count].detect {|m| collection.respond_to?(m)} 44 | end 45 | 46 | # 47 | # 48 | # 49 | def method_missing(method, *args, &block) 50 | @collection_name = method 51 | @args = args 52 | @block = block 53 | 54 | case @relation 55 | when :at_least 56 | RSpecial::HaveAtLeastAssay.assertor(self) 57 | when :at_most 58 | RSpecial::HaveAtMostAssay.assertor(self) 59 | else 60 | RSpecial::HaveExactlyAssay.assertor(self) 61 | end 62 | end 63 | 64 | # 65 | # 66 | module Helpers 67 | 68 | # 69 | # 70 | # 71 | def collection_set(collection_or_owner, have) 72 | collection = have.collection(collection_or_owner) 73 | query_method = query_method(collection) 74 | 75 | raise not_a_collection(have) unless query_method 76 | 77 | actual = collection.__send__(query_method) 78 | 79 | return collection, query_method, actual 80 | end 81 | 82 | # 83 | # Return the actual size of the collection. 84 | # 85 | def collection_actual(collection_or_owner, have) 86 | collection_set(collection_or_owner, have).last 87 | end 88 | 89 | # 90 | # Which size method to use: `size`, `length` or `count`. 91 | # 92 | def query_method(collection) 93 | [:size, :length, :count].detect {|m| collection.respond_to?(m)} 94 | end 95 | 96 | # 97 | # Message to use when collection doesn't respond to `size`, `length` or `count`. 98 | # 99 | def not_a_collection(have) 100 | "expected #{have.collection_name} to be a collection but it does not respond to #length, #size or #count" 101 | end 102 | 103 | # 104 | # TODO: use when verbose error message mode 105 | # 106 | def self.assert_description(collection_or_owner, have) 107 | collection, method, actual = collection_set(collection_or_owner, have) 108 | "expected #{have.relation} #{have.expected} #{have.collection_name}, got #{actual}" 109 | end 110 | 111 | # 112 | # 113 | # 114 | def self.refute_descrptipon(collection_or_owner, have) 115 | collection, method, actual = collection_set(collection_or_owner, have) 116 | "expected not to have #{have.relation} #{have.expected} #{have.collection_name}, got #{actual}" 117 | end 118 | 119 | end 120 | 121 | end 122 | 123 | # 124 | # 125 | class HaveExactlyAssay < EqualAssay 126 | 127 | extend Have::Helpers 128 | 129 | register :have 130 | 131 | # 132 | # 133 | # 134 | def self.pass?(collection_or_owner, have) 135 | actual = collection_actual(collection_or_owner, have) 136 | super(actual, have.expected) 137 | end 138 | 139 | # 140 | # Error message for have equal assertion. 141 | # 142 | def self.assert_message(collection_or_owner, have) 143 | actual = collection_actual(collection_or_owner, have) 144 | super(actual, have.expected) 145 | end 146 | 147 | end 148 | 149 | # 150 | # 151 | class HaveAtLeastAssay < MoreEqualAssay 152 | 153 | extend Have::Helpers 154 | 155 | register :have_at_least 156 | 157 | # 158 | # 159 | # 160 | def self.pass?(collection_or_owner, have) 161 | actual = collection_actual(collection_or_owner, have) 162 | super(actual, have.expected) 163 | end 164 | 165 | # 166 | # 167 | # 168 | def self.assert_message(collection_or_owner, have) 169 | actual = collection_actual(collection_or_owner, have) 170 | super(actual, have.expected) 171 | end 172 | 173 | end 174 | 175 | # 176 | # 177 | class HaveAtMostAssay < LessEqualAssay 178 | 179 | extend Have::Helpers 180 | 181 | register :have_at_most 182 | 183 | # 184 | # 185 | # 186 | def self.pass?(collection_or_owner, have) 187 | actual = collection_actual(collection_or_owner, have) 188 | super(actual, have.expected) 189 | end 190 | 191 | # 192 | # 193 | # 194 | def self.assert_message(collection_or_owner, have) 195 | actual = collection_actual(collection_or_owner, have) 196 | super(actual, have.expected) 197 | end 198 | 199 | end 200 | 201 | end 202 | -------------------------------------------------------------------------------- /lib/rspecial/matchers.rb: -------------------------------------------------------------------------------- 1 | module RSpecial 2 | 3 | # This module provides matchers for RSpec-compatiblity. 4 | # 5 | # The set is not fully compataible, but provides most RSpec matchers. 6 | # The most notable exlusion for the moment is the `have` matchers. 7 | # 8 | # Compatability will improve with time. Feel _obligated_ to submit a 9 | # patch if you really need it. ;) 10 | # 11 | # @see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers 12 | # 13 | module Matchers 14 | 15 | #def self.included(base) 16 | # @_once ||= require_relative('should').nil? 17 | #end 18 | 19 | #def self.extended(base) 20 | # @_once ||= require_relative('should').nil? 21 | #end 22 | 23 | # Passes if the expected and actual are alike. 24 | # 25 | # object.should be_like(criterion) 26 | # 27 | # There is no equivalant for this in RSpec, we simply add it 28 | # here to cover all Assays available. 29 | # 30 | # @raise LikeAssay 31 | # 32 | def be_like(criterion) 33 | LikeAssay.assertor(criterion) 34 | end 35 | 36 | # Passes if expected and actual are nto equal within delta tolerance. 37 | # 38 | # value.should be_close(delta, criterion) 39 | # 40 | # @raise WithinAssay 41 | # 42 | def be_close(delta, criterion) 43 | WithinAssay.assertor(criterion, delta) 44 | end 45 | 46 | # Passes if object is empty. 47 | # 48 | # object.should be_empty 49 | # 50 | # @raise EmptyAssay 51 | # 52 | def be_empty 53 | EmptyAssay.assertor 54 | end 55 | 56 | # Passes if +expected+ == +actual+. 57 | # 58 | # 'MY STRING'.should equate_to('my string'.upcase) 59 | # 'MY STRING'.should_not equate_to('another string') 60 | # 61 | # This matcher is not supported by RSpec, but is added so that the 62 | # EqualityAssay has an explict matcher available. 63 | # 64 | # @raise EqualityAssay 65 | # 66 | def equate_to(exp) 67 | EqualityAssay.assertor(exp) 68 | end 69 | 70 | # 71 | # Passes if block is satisfied given target object as argument. 72 | # 73 | # 5.should satisfy{ |n| n > 3} 74 | # 75 | # @raise ExecutionAssay 76 | # 77 | def satisfy(&block) 78 | ExecutionAssay.assertor(&block) 79 | end 80 | 81 | # 82 | # Passed if object is +false+. 83 | # 84 | # value.should be_false 85 | # 86 | # @raise FalseAssay 87 | # 88 | def be_false 89 | FalseAssay.assertor 90 | end 91 | 92 | # 93 | # Passes if actual is the same exact object as expected. 94 | # 95 | # object.should be_identical_to(object) 96 | # 97 | # @raise IdentityAssay 98 | # 99 | def equal(obj) 100 | IdentityAssay.assertor(obj) 101 | end 102 | 103 | alias :be_identical_to :equal 104 | 105 | # 106 | # Passes if `actual == expected`. 107 | # 108 | # object.should eq(object) 109 | # 110 | # @raise EqualAssay 111 | # 112 | def eq(obj) 113 | EqualAssay.assertor(obj) 114 | end 115 | 116 | # 117 | # Passes if object is an instance of class. 118 | # 119 | # object.should be_instance_of(class) 120 | # 121 | # @raise InstanceAssay 122 | # 123 | def be_instance_of(cls) 124 | InstanceAssay.assertor(cls) 125 | end 126 | 127 | alias :be_an_instance_of :be_instance_of 128 | 129 | # 130 | # Pass if object is a kind of class. 131 | # 132 | # object.should be_kind_of(class) 133 | # 134 | # @raise KindAssay 135 | # 136 | def be_kind_of(cls) 137 | KindAssay.assertor(cls) 138 | end 139 | 140 | alias :be_a_kind_of :be_kind_of 141 | alias :be_a :be_kind_of 142 | alias :be_an :be_kind_of 143 | 144 | # 145 | # Pass if object matches pattern using `#=~` method. 146 | # 147 | # object.should match(regexp) 148 | # 149 | # @raise MatchAssay 150 | # 151 | def match(regexp) 152 | MatchAssay.assertor(regexp) 153 | end 154 | 155 | # 156 | # Pass if object is +nil+. 157 | # 158 | # value.should be_nil 159 | # 160 | # @raise NilAssay 161 | # 162 | def be_nil 163 | NilAssay.assertor 164 | end 165 | 166 | # 167 | # Pass if an exception is raised. 168 | # 169 | # lambda { do_something_risky }.should raise_error 170 | # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError) 171 | # 172 | # @raise RaiseAssay 173 | # 174 | # @todo Support for message matching ? 175 | # 176 | def raise_error(exception=Exception) 177 | RaiseAssay.assertor(exception) 178 | end 179 | 180 | # 181 | # Pass if object responds to message. 182 | # 183 | # object.should respond_to(:method_name) 184 | # 185 | # @raise RespondAssay 186 | # 187 | def respond_to(method) 188 | RespondAssay.assertor(method) 189 | end 190 | 191 | # 192 | # Pass if two objects are equal using `#eql?` method. 193 | # 194 | # object1.should eql(object2) 195 | # 196 | # @raise EqualityAssay 197 | # 198 | def eql(exp) 199 | EqualityAssay.assertor(exp) 200 | end 201 | 202 | # 203 | # Pass if procedure throws a specified symbol. 204 | # 205 | # lambda { do_something_risky }.should throw_symbol 206 | # lambda { do_something_risky }.should throw_symbol(:that_was_risky) 207 | # 208 | # @raise ThrowAssay 209 | # 210 | # @todo Support for throw argument. (Does RSpec even support this?) 211 | # 212 | def throw_symbol(sym=nil) #, arg=nil) 213 | ThrowAssay.assertor(sym) 214 | end 215 | 216 | # 217 | # Passed if object is +true+. 218 | # 219 | # object.should be_true 220 | # 221 | # @raise TrueAssay 222 | # 223 | def be_true 224 | TrueAssay.assertor 225 | end 226 | 227 | # 228 | # 229 | # 230 | def have(n) 231 | Have.new(n) 232 | end 233 | 234 | alias :have_exactly :have 235 | 236 | # 237 | # 238 | # 239 | def have_at_least(n) 240 | Have.new(n, :at_least) 241 | end 242 | 243 | # 244 | # 245 | # 246 | def have_at_most(n) 247 | Have.new(n, :at_most) 248 | end 249 | 250 | # 251 | # 252 | def expect(target) 253 | Expect.new(target) 254 | end 255 | 256 | end 257 | 258 | end 259 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | require 'pathname' 5 | 6 | module Indexer 7 | 8 | # Convert index data into a gemspec. 9 | # 10 | # Notes: 11 | # * Assumes all executables are in bin/. 12 | # * Does not yet handle default_executable setting. 13 | # * Does not yet handle platform setting. 14 | # * Does not yet handle required_ruby_version. 15 | # * Support for rdoc entries is weak. 16 | # 17 | class GemspecExporter 18 | 19 | # File globs to include in package --unless a manifest file exists. 20 | FILES = ".index .yardopts alt bin data demo ext features lib man spec test try* [A-Z]*.*" unless defined?(FILES) 21 | 22 | # File globs to omit from FILES. 23 | OMIT = "Config.rb" unless defined?(OMIT) 24 | 25 | # Standard file patterns. 26 | PATTERNS = { 27 | :root => '{.index,Gemfile}', 28 | :bin => 'bin/*', 29 | :lib => 'lib/{**/}*', #.rb', 30 | :ext => 'ext/{**/}extconf.rb', 31 | :doc => '*.{txt,rdoc,md,markdown,tt,textile}', 32 | :test => '{test,spec}/{**/}*.rb' 33 | } unless defined?(PATTERNS) 34 | 35 | # For which revision of indexer spec is this converter intended? 36 | REVISION = 2013 unless defined?(REVISION) 37 | 38 | # 39 | def self.gemspec 40 | new.to_gemspec 41 | end 42 | 43 | # 44 | attr :metadata 45 | 46 | # 47 | def initialize(metadata=nil) 48 | @root_check = false 49 | 50 | if metadata 51 | root_dir = metadata.delete(:root) 52 | if root_dir 53 | @root = root_dir 54 | @root_check = true 55 | end 56 | metadata = nil if metadata.empty? 57 | end 58 | 59 | @metadata = metadata || YAML.load_file(root + '.index') 60 | 61 | if @metadata['revision'].to_i != REVISION 62 | warn "This gemspec exporter was not designed for this revision of index metadata." 63 | end 64 | end 65 | 66 | # 67 | def has_root? 68 | root ? true : false 69 | end 70 | 71 | # 72 | def root 73 | return @root if @root || @root_check 74 | @root_check = true 75 | @root = find_root 76 | end 77 | 78 | # 79 | def manifest 80 | return nil unless root 81 | @manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first 82 | end 83 | 84 | # 85 | def scm 86 | return nil unless root 87 | @scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym 88 | end 89 | 90 | # 91 | def files 92 | return [] unless root 93 | @files ||= \ 94 | if manifest 95 | File.readlines(manifest). 96 | map{ |line| line.strip }. 97 | reject{ |line| line.empty? || line[0,1] == '#' } 98 | else 99 | list = [] 100 | Dir.chdir(root) do 101 | FILES.split(/\s+/).each do |pattern| 102 | list.concat(glob(pattern)) 103 | end 104 | OMIT.split(/\s+/).each do |pattern| 105 | list = list - glob(pattern) 106 | end 107 | end 108 | list 109 | end.select{ |path| File.file?(path) }.uniq 110 | end 111 | 112 | # 113 | def glob_files(pattern) 114 | return [] unless root 115 | Dir.chdir(root) do 116 | Dir.glob(pattern).select do |path| 117 | File.file?(path) && files.include?(path) 118 | end 119 | end 120 | end 121 | 122 | def patterns 123 | PATTERNS 124 | end 125 | 126 | def executables 127 | @executables ||= \ 128 | glob_files(patterns[:bin]).map do |path| 129 | File.basename(path) 130 | end 131 | end 132 | 133 | def extensions 134 | @extensions ||= \ 135 | glob_files(patterns[:ext]).map do |path| 136 | File.basename(path) 137 | end 138 | end 139 | 140 | def name 141 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 142 | end 143 | 144 | def homepage 145 | page = ( 146 | metadata['resources'].find{ |r| r['type'] =~ /^home/i } || 147 | metadata['resources'].find{ |r| r['name'] =~ /^home/i } || 148 | metadata['resources'].find{ |r| r['name'] =~ /^web/i } 149 | ) 150 | page ? page['uri'] : false 151 | end 152 | 153 | def licenses 154 | metadata['copyrights'].map{ |c| c['license'] }.compact 155 | end 156 | 157 | def require_paths 158 | metadata['load_path'] || ['lib'] 159 | end 160 | 161 | # 162 | # Convert to gemnspec. 163 | # 164 | def to_gemspec 165 | if has_root? 166 | Gem::Specification.new do |gemspec| 167 | to_gemspec_data(gemspec) 168 | to_gemspec_paths(gemspec) 169 | end 170 | else 171 | Gem::Specification.new do |gemspec| 172 | to_gemspec_data(gemspec) 173 | to_gemspec_paths(gemspec) 174 | end 175 | end 176 | end 177 | 178 | # 179 | # Convert pure data settings. 180 | # 181 | def to_gemspec_data(gemspec) 182 | gemspec.name = name 183 | gemspec.version = metadata['version'] 184 | gemspec.summary = metadata['summary'] 185 | gemspec.description = metadata['description'] 186 | 187 | metadata['authors'].each do |author| 188 | gemspec.authors << author['name'] 189 | 190 | if author.has_key?('email') 191 | if gemspec.email 192 | gemspec.email << author['email'] 193 | else 194 | gemspec.email = [author['email']] 195 | end 196 | end 197 | end 198 | 199 | gemspec.licenses = licenses 200 | 201 | requirements = metadata['requirements'] || [] 202 | requirements.each do |req| 203 | next if req['optional'] 204 | next if req['external'] 205 | 206 | name = req['name'] 207 | groups = req['groups'] || [] 208 | 209 | version = gemify_version(req['version']) 210 | 211 | if groups.empty? or groups.include?('runtime') 212 | # populate runtime dependencies 213 | if gemspec.respond_to?(:add_runtime_dependency) 214 | gemspec.add_runtime_dependency(name,*version) 215 | else 216 | gemspec.add_dependency(name,*version) 217 | end 218 | else 219 | # populate development dependencies 220 | if gemspec.respond_to?(:add_development_dependency) 221 | gemspec.add_development_dependency(name,*version) 222 | else 223 | gemspec.add_dependency(name,*version) 224 | end 225 | end 226 | end 227 | 228 | # convert external dependencies into gemspec requirements 229 | requirements.each do |req| 230 | next unless req['external'] 231 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 232 | end 233 | 234 | gemspec.homepage = homepage 235 | gemspec.require_paths = require_paths 236 | gemspec.post_install_message = metadata['install_message'] 237 | end 238 | 239 | # 240 | # Set gemspec settings that require a root directory path. 241 | # 242 | def to_gemspec_paths(gemspec) 243 | gemspec.files = files 244 | gemspec.extensions = extensions 245 | gemspec.executables = executables 246 | 247 | if Gem::VERSION < '1.7.' 248 | gemspec.default_executable = gemspec.executables.first 249 | end 250 | 251 | gemspec.test_files = glob_files(patterns[:test]) 252 | 253 | unless gemspec.files.include?('.document') 254 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 255 | end 256 | end 257 | 258 | # 259 | # Return a copy of this file. This is used to generate a local 260 | # .gemspec file that can automatically read the index file. 261 | # 262 | def self.source_code 263 | File.read(__FILE__) 264 | end 265 | 266 | private 267 | 268 | def find_root 269 | root_files = patterns[:root] 270 | if Dir.glob(root_files).first 271 | Pathname.new(Dir.pwd) 272 | elsif Dir.glob("../#{root_files}").first 273 | Pathname.new(Dir.pwd).parent 274 | else 275 | #raise "Can't find root of project containing `#{root_files}'." 276 | warn "Can't find root of project containing `#{root_files}'." 277 | nil 278 | end 279 | end 280 | 281 | def glob(pattern) 282 | if File.directory?(pattern) 283 | Dir.glob(File.join(pattern, '**', '*')) 284 | else 285 | Dir.glob(pattern) 286 | end 287 | end 288 | 289 | def gemify_version(version) 290 | case version 291 | when /^(.*?)\+$/ 292 | ">= #{$1}" 293 | when /^(.*?)\-$/ 294 | "< #{$1}" 295 | when /^(.*?)\~$/ 296 | "~> #{$1}" 297 | else 298 | version 299 | end 300 | end 301 | 302 | end 303 | 304 | end 305 | 306 | Indexer::GemspecExporter.gemspec --------------------------------------------------------------------------------