├── var ├── name ├── title ├── version ├── created ├── contact ├── summary ├── authors ├── copyrights ├── resources ├── description └── requirements ├── Gemfile ├── .ruby ├── .yardopts ├── etc └── test.rb ├── MANIFEST ├── .travis.yml ├── .gitignore ├── Assembly ├── HISTORY.md ├── .index ├── LICENSE.txt ├── test └── test_iteration.rb ├── README.md ├── lib └── iteration.rb └── .gemspec /var/name: -------------------------------------------------------------------------------- 1 | iteration 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | Iteration 2 | -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 1.1.1 2 | -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2007-07-18 2 | -------------------------------------------------------------------------------- /var/contact: -------------------------------------------------------------------------------- 1 | Trans 2 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Universal Iteration Object 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Trans 3 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | [git] 2 | git@github.com:rubyworks/iteration.git 3 | 4 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) 2007 Rubyworks (BSD-2-Clause) 3 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://github.com/rubyworks/iteration 3 | 4 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --protected 2 | --private 3 | --readme README.rdoc 4 | lib 5 | - 6 | [A-Z]*.* 7 | -------------------------------------------------------------------------------- /etc/test.rb: -------------------------------------------------------------------------------- 1 | Test.run do |r| 2 | r.loadpath << 'lib' 3 | r.files << 'test/test_*.rb' 4 | end 5 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast bin lib test [A-Z]*.* 2 | lib/iteration.rb 3 | test/test_iteration.rb 4 | LICENSE.txt 5 | HISTORY.md 6 | README.md 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | script: "bundle exec rubytest test/" 4 | rvm: 5 | - 1.9.3 6 | - rbx-19mode 7 | - jruby-19mode 8 | 9 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | Iteration is class that encapsulate an step in an each loop. 2 | It can be used to query infromation about an iteration easily. 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.lock 3 | .ergo/digest 4 | .yardoc/ 5 | doc/ 6 | log/ 7 | man/man1/*.1 8 | man/man1/*.html 9 | pkg/ 10 | tmp/ 11 | web/ 12 | work/sandbox 13 | 14 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - detroit (build) 3 | - ergo (build) 4 | - rubytest (test) 5 | - rubytest-cli (test) 6 | - microtest (test) 7 | - ae (test) 8 | 9 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | gem: 3 | active: true 4 | 5 | github: 6 | gh_pages: web 7 | 8 | dnote: 9 | title: Source Notes 10 | output: log/notes.html 11 | 12 | #locat: 13 | # output : ~ 14 | # active : true 15 | 16 | vclog: 17 | output: 18 | - log/history.html 19 | - log/changes.html 20 | 21 | email: 22 | mailto: 23 | - ruby-talk@ruby-lang.org 24 | - rubyworks-mailinglist@googlegroups.com 25 | 26 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # HISTORY 2 | 3 | ## 1.1.1 / 2013-03-10 4 | 5 | This release fixes a bug in #each_with_iteration. When no 6 | block was given, the enumerator returned was for `#each_iteration` 7 | instead of `#each_with_iteration`. 8 | 9 | Changes: 10 | 11 | * Return corrert enumerator for #each_with_iteration. 12 | 13 | 14 | ## 1.1.0 / 2012-12-31 15 | 16 | This release move the Iteration class to the toplevel 17 | namespace. 18 | 19 | Changes: 20 | 21 | * Move Iteration to toplevel. 22 | * Modernize build configuration. 23 | 24 | 25 | ## 1.0.0 / 2009-07-18 26 | 27 | This is the initial release of Iteration. 28 | 29 | Changes: 30 | 31 | * Happy Birthday! 32 | 33 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - var 6 | authors: 7 | - name: Trans 8 | email: transfire@gmail.com 9 | organizations: [] 10 | requirements: 11 | - groups: 12 | - build 13 | development: true 14 | name: detroit 15 | - groups: 16 | - build 17 | development: true 18 | name: ergo 19 | - groups: 20 | - test 21 | development: true 22 | name: rubytest 23 | - groups: 24 | - test 25 | development: true 26 | name: rubytest-cli 27 | - groups: 28 | - test 29 | development: true 30 | name: microtest 31 | - groups: 32 | - test 33 | development: true 34 | name: ae 35 | conflicts: [] 36 | alternatives: [] 37 | resources: 38 | - type: home 39 | uri: http://github.com/rubyworks/iteration 40 | label: Homepage 41 | repositories: [] 42 | categories: [] 43 | copyrights: 44 | - holder: Rubyworks 45 | year: '2007' 46 | license: BSD-2-Clause 47 | customs: [] 48 | paths: 49 | lib: 50 | - lib 51 | created: '2007-07-18' 52 | summary: Universal Iteration Object 53 | title: Iteration 54 | version: 1.1.1 55 | name: iteration 56 | description: ! 'Iteration is class that encapsulate an step in an each loop. 57 | 58 | It can be used to query infromation about an iteration easily.' 59 | date: '2013-03-08' 60 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (BSD-2-Clause) 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 15 | AND 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 | 24 | -------------------------------------------------------------------------------- /test/test_iteration.rb: -------------------------------------------------------------------------------- 1 | require 'microtest' 2 | require 'ae/legacy' 3 | 4 | require 'iteration' 5 | 6 | class TestArray < MicroTest::TestCase 7 | include AE::Legacy::Assertions 8 | 9 | def test_each_iteration 10 | a = [1,2,3] 11 | r = [ [1, 0, true , false, [] , [2,3]], 12 | [2, 1, false, false, [1] , [3] ], 13 | [3, 2, false, true, [1,2], [] ] ] 14 | i = 0 15 | 16 | a.each_iteration do |it| 17 | value, index, first, last, prior, after = *r[i] 18 | assert_equal(value, it.value) 19 | assert_equal(index, it.index) 20 | assert_equal(first, it.first?) 21 | assert_equal(last, it.last?) 22 | assert_equal(prior, it.prior) 23 | assert_equal(after, it.after) 24 | i+=1 25 | end 26 | end 27 | 28 | end 29 | 30 | 31 | class TestEnumerator < MicroTest::TestCase 32 | include AE::Legacy::Assertions 33 | 34 | def test_iteration 35 | e = [1,2,3].to_enum(:each) 36 | r = [ [1, 0, true , false, [] , [2,3]], 37 | [2, 1, false, false, [1] , [3] ], 38 | [3, 2, false, true, [1,2], [] ] ] 39 | i = 0 40 | 41 | e.iteration do |it| 42 | value, index, first, last, prior, after = *r[i] 43 | assert_equal(value, it.value) 44 | assert_equal(index, it.index) 45 | assert_equal(first, it.first?) 46 | #assert_equal(last, it.last?) # TODO: when enumerators can report underlying object. 47 | assert_equal(prior, it.prior) 48 | #assert_equal(after, it.after) 49 | i+=1 50 | end 51 | end 52 | 53 | end 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Iteration 2 | 3 | [Website](http://rubyworks.github.com/iteration) / 4 | [Documentation](http://rubydoc.info/gems/iteration) / 5 | [Report Issue](http://github.com/rubyworks/iteration/issues) / 6 | [Development](http://github.com/rubyworks/iteration) 7 | 8 | [![Build Status](https://secure.travis-ci.org/rubyworks/iteration.png)](http://travis-ci.org/rubyworks/iteration) 9 | [![Gem Version](https://badge.fury.io/rb/iteration.png)](http://badge.fury.io/rb/iteration) 10 | 11 | 12 | ## DESCRIPTION 13 | 14 | Have you ever wanted to know if an iteration was the last, 15 | or the first, or what iteration results came before the 16 | current? Well, now you can! Iteration is a class that encapsulates 17 | a step in an each loop. It can be used to query infromation about 18 | an iteration easily. 19 | 20 | 21 | ## FEATURES 22 | 23 | * Query sate of each iteration. 24 | * Supports look-ahead features on Arrays. 25 | 26 | 27 | ## SYNOPSIS 28 | 29 | Iterate over each element of array using an iteration object. 30 | 31 | [1,2,3].each_iteration do |it| 32 | p it.index 33 | p it.value 34 | p it.first? 35 | p it.last? 36 | p it.prior 37 | p it.after 38 | end 39 | 40 | on each successive iteration produces: 41 | 42 | 0 1 2 43 | 1 2 3 44 | true false false 45 | false false true 46 | [] [1] [1,2] 47 | [2,3] [3] [] 48 | 49 | 50 | ## HOW TO INSTALL 51 | 52 | To install with RubyGems simply open a console and type: 53 | 54 | gem install iteration 55 | 56 | Local installation requires Setup.rb (gem install setup), 57 | then download the tarball package and type: 58 | 59 | tar -xvzf iteration-1.0.0.tgz 60 | cd iteration-1.0.0.tgz 61 | sudo setup.rb all 62 | 63 | Windows users use 'ruby setup.rb all'. 64 | 65 | 66 | ## COPYRIGHTS 67 | 68 | Copyright (c) 2009 Rubyworks 69 | 70 | This program is ditributed unser the terms of the *BSD-2-Clause* license. 71 | 72 | See LICENSE.txt file for details. 73 | 74 | -------------------------------------------------------------------------------- /lib/iteration.rb: -------------------------------------------------------------------------------- 1 | if RUBY_VERSION < "1.9" 2 | require 'enumerator' 3 | Enumerator = Enumerable::Enumerator 4 | end 5 | 6 | # Iteration encapsulates a step in an each loop. 7 | # 8 | class Iteration 9 | 10 | attr_reader :enum, :index, :value, :prior 11 | 12 | def initialize(enum) 13 | @enum = enum 14 | @index = 0 15 | @value = nil 16 | @prior = [] 17 | end 18 | 19 | def first? 20 | index == 0 21 | end 22 | 23 | def last? 24 | index+1 == enum.size 25 | end 26 | 27 | def after 28 | enum.slice(index+1..-1) 29 | end 30 | 31 | #private 32 | 33 | # TODO: For Ruby 1.9 make private and use fcall ? 34 | # 35 | def __step__(value, &block) 36 | @value = value 37 | block.call 38 | @index += 1 39 | @prior << value 40 | end 41 | 42 | #def next_iteration 43 | # @index += 1 44 | # @prior << value 45 | # @after.shift if enum.respond_to?(:shift) 46 | #end 47 | 48 | end 49 | 50 | class Enumerator 51 | 52 | # TODO: How to access the underlying object of enumeration? 53 | # We need it to provide #size and #slice if possible. 54 | # 55 | 56 | # 57 | def iteration #:yield: 58 | it = Iteration.new(self) 59 | each do |e| 60 | it.__step__(e){ yield(it) } 61 | end 62 | end 63 | 64 | def with_iteration(&block) #:yield: 65 | it = Iteration.new(self) 66 | each do |e| 67 | it.__step__(e){ yield(e,it) } 68 | end 69 | end 70 | 71 | end 72 | 73 | class Array 74 | 75 | # Iterate over each element of array using an iteration object. 76 | # 77 | # [1,2,3].each_iteration do |it| 78 | # p it.index 79 | # p it.value 80 | # p it.first? 81 | # p it.last? 82 | # p it.prior 83 | # p it.after 84 | # end 85 | # 86 | # On each successive iteration this produces: 87 | # 88 | # 0 1 2 89 | # 1 2 3 90 | # true false false 91 | # false false true 92 | # [] [1] [1,2] 93 | # [2,3] [3] [] 94 | # 95 | # @return [Enumerator] if no block is given, otherwise nothing. 96 | def each_iteration(&block) 97 | if block_given? 98 | it = Iteration.new(self) 99 | each do |e| 100 | it.__step__(e){ yield(it) } 101 | end 102 | else 103 | Enumerator.new(self, :each_iteration) 104 | end 105 | end 106 | 107 | # Same as #each_iteration, but provides both the iterated 108 | # element and the iteration. 109 | # 110 | # @return [Enumerator] if no block is given, otherwise nothing. 111 | def each_with_iteration(&block) 112 | if block_given? 113 | it = Iteration.new(self) 114 | each do |e| 115 | it.__step__(e){ yield(e, it) } 116 | end 117 | else 118 | Enumerator.new(self, :each_with_iteration) 119 | end 120 | end 121 | 122 | end 123 | -------------------------------------------------------------------------------- /.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 | paths = metadata['paths'] || {} 159 | paths['load'] || ['lib'] 160 | end 161 | 162 | # 163 | # Convert to gemnspec. 164 | # 165 | def to_gemspec 166 | if has_root? 167 | Gem::Specification.new do |gemspec| 168 | to_gemspec_data(gemspec) 169 | to_gemspec_paths(gemspec) 170 | end 171 | else 172 | Gem::Specification.new do |gemspec| 173 | to_gemspec_data(gemspec) 174 | to_gemspec_paths(gemspec) 175 | end 176 | end 177 | end 178 | 179 | # 180 | # Convert pure data settings. 181 | # 182 | def to_gemspec_data(gemspec) 183 | gemspec.name = name 184 | gemspec.version = metadata['version'] 185 | gemspec.summary = metadata['summary'] 186 | gemspec.description = metadata['description'] 187 | 188 | metadata['authors'].each do |author| 189 | gemspec.authors << author['name'] 190 | 191 | if author.has_key?('email') 192 | if gemspec.email 193 | gemspec.email << author['email'] 194 | else 195 | gemspec.email = [author['email']] 196 | end 197 | end 198 | end 199 | 200 | gemspec.licenses = licenses 201 | 202 | requirements = metadata['requirements'] || [] 203 | requirements.each do |req| 204 | next if req['optional'] 205 | next if req['external'] 206 | 207 | name = req['name'] 208 | groups = req['groups'] || [] 209 | 210 | version = gemify_version(req['version']) 211 | 212 | if groups.empty? or groups.include?('runtime') 213 | # populate runtime dependencies 214 | if gemspec.respond_to?(:add_runtime_dependency) 215 | gemspec.add_runtime_dependency(name,*version) 216 | else 217 | gemspec.add_dependency(name,*version) 218 | end 219 | else 220 | # populate development dependencies 221 | if gemspec.respond_to?(:add_development_dependency) 222 | gemspec.add_development_dependency(name,*version) 223 | else 224 | gemspec.add_dependency(name,*version) 225 | end 226 | end 227 | end 228 | 229 | # convert external dependencies into gemspec requirements 230 | requirements.each do |req| 231 | next unless req['external'] 232 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 233 | end 234 | 235 | gemspec.homepage = homepage 236 | gemspec.require_paths = require_paths 237 | gemspec.post_install_message = metadata['install_message'] 238 | end 239 | 240 | # 241 | # Set gemspec settings that require a root directory path. 242 | # 243 | def to_gemspec_paths(gemspec) 244 | gemspec.files = files 245 | gemspec.extensions = extensions 246 | gemspec.executables = executables 247 | 248 | if Gem::VERSION < '1.7.' 249 | gemspec.default_executable = gemspec.executables.first 250 | end 251 | 252 | gemspec.test_files = glob_files(patterns[:test]) 253 | 254 | unless gemspec.files.include?('.document') 255 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 256 | end 257 | end 258 | 259 | # 260 | # Return a copy of this file. This is used to generate a local 261 | # .gemspec file that can automatically read the index file. 262 | # 263 | def self.source_code 264 | File.read(__FILE__) 265 | end 266 | 267 | private 268 | 269 | def find_root 270 | root_files = patterns[:root] 271 | if Dir.glob(root_files).first 272 | Pathname.new(Dir.pwd) 273 | elsif Dir.glob("../#{root_files}").first 274 | Pathname.new(Dir.pwd).parent 275 | else 276 | #raise "Can't find root of project containing `#{root_files}'." 277 | warn "Can't find root of project containing `#{root_files}'." 278 | nil 279 | end 280 | end 281 | 282 | def glob(pattern) 283 | if File.directory?(pattern) 284 | Dir.glob(File.join(pattern, '**', '*')) 285 | else 286 | Dir.glob(pattern) 287 | end 288 | end 289 | 290 | def gemify_version(version) 291 | case version 292 | when /^(.*?)\+$/ 293 | ">= #{$1}" 294 | when /^(.*?)\-$/ 295 | "< #{$1}" 296 | when /^(.*?)\~$/ 297 | "~> #{$1}" 298 | else 299 | version 300 | end 301 | end 302 | 303 | end 304 | 305 | end 306 | 307 | Indexer::GemspecExporter.gemspec --------------------------------------------------------------------------------