├── index ├── name ├── version ├── created ├── title ├── organizations ├── paths ├── summary ├── authors ├── copyrights ├── repositories ├── requirements ├── description └── resources ├── .gitignore ├── demo └── 01_proutils.md ├── .travis.yml ├── .yardopts ├── lib ├── proutils │ ├── core_ext.rb │ ├── core_ext │ │ ├── to_yamlfrag.rb │ │ ├── to_actual_filename.rb │ │ ├── to_list.rb │ │ ├── filetest.rb │ │ ├── facets.rb │ │ └── to_console.rb │ ├── info_utils.rb │ ├── stdio_utils.rb │ ├── shell_utils.rb │ ├── path_utils.rb │ ├── file_utils.rb │ ├── config_utils.rb │ └── email_utils.rb └── proutils.rb ├── HISTORY.md ├── Gemfile ├── MANIFEST ├── Indexfile ├── LICENSE.txt ├── .index ├── README.md └── .gemspec /index/name: -------------------------------------------------------------------------------- 1 | proutils 2 | -------------------------------------------------------------------------------- /index/version: -------------------------------------------------------------------------------- 1 | 0.4.0 2 | -------------------------------------------------------------------------------- /index/created: -------------------------------------------------------------------------------- 1 | 2014-01-14 2 | -------------------------------------------------------------------------------- /index/title: -------------------------------------------------------------------------------- 1 | ProUtils 2 | -------------------------------------------------------------------------------- /index/organizations: -------------------------------------------------------------------------------- 1 | --- 2 | - Rubyworks 3 | -------------------------------------------------------------------------------- /index/paths: -------------------------------------------------------------------------------- 1 | --- 2 | load: 3 | - lib 4 | -------------------------------------------------------------------------------- /index/summary: -------------------------------------------------------------------------------- 1 | Project utility methods 2 | -------------------------------------------------------------------------------- /index/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Trans 3 | -------------------------------------------------------------------------------- /index/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - 2014 Rubyworks (BSD-2-Clause) 3 | -------------------------------------------------------------------------------- /index/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/proutils.git 3 | -------------------------------------------------------------------------------- /index/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - facets 3 | #- detroit (build) 4 | #- ergo (build) 5 | - qed (test) 6 | #- lemon (test) 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.lock 3 | .reap/digest 4 | .rdoc 5 | .yardoc 6 | log/ 7 | pkg/ 8 | tmp/ 9 | doc/ 10 | web/ 11 | DEMO.md 12 | -------------------------------------------------------------------------------- /demo/01_proutils.md: -------------------------------------------------------------------------------- 1 | # ProUtils 2 | 3 | Lets start out just by trying to load up the entire library. 4 | 5 | require 'proutils' 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | script: "bundle exec qed" 4 | rvm: 5 | - 1.9.3 6 | - 2.0.0 7 | #- 2.1.0 8 | - rbx 9 | # rbx-2 10 | - jruby-19mode 11 | 12 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title "ProUtils" 2 | --output-dir doc 3 | --plugin tomdoc 4 | --readme README.md 5 | --exclude lib/protuils.yml 6 | --protected 7 | --private 8 | lib 9 | - 10 | *.md 11 | *.txt 12 | 13 | -------------------------------------------------------------------------------- /index/description: -------------------------------------------------------------------------------- 1 | ProUtils provides a large set of utitlity methods useful 2 | for project development requirements. The methods are 3 | divided up into a set of topic modules to help maximize 4 | reusability. 5 | -------------------------------------------------------------------------------- /index/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/proutils 3 | docs: http://rubydoc.info/gems/proutils/frames 4 | code: http://github.com/rubyworks/proutils 5 | bugs: http://github.com/rubyworks/proutils/issues 6 | mail: http://groups.google.com/group/rubyworks-mailinglist 7 | -------------------------------------------------------------------------------- /lib/proutils/core_ext.rb: -------------------------------------------------------------------------------- 1 | require_relative 'core_ext/facets' 2 | require_relative 'core_ext/filetest' 3 | require_relative 'core_ext/to_actual_filename' 4 | require_relative 'core_ext/to_console' 5 | require_relative 'core_ext/to_list' 6 | require_relative 'core_ext/to_yamlfrag' 7 | 8 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 0.4.0 | 2014-01-15 4 | 5 | This is the first release of ProUtils (albeit it is actuality a completely 6 | new take on a much older project, which accounts for the 0.4 initial version). 7 | 8 | Changes: 9 | 10 | * Happy reincarnation day! 11 | 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | #gemspec 3 | 4 | gem "facets" 5 | 6 | #group :build do 7 | # gem "detroit" 8 | # gem "ergo" 9 | #end 10 | 11 | group :test do 12 | gem "qed" 13 | end 14 | 15 | # TODO: FIX Indexer `platforms` 16 | platforms :rbx do 17 | gem 'rubysl', '~> 2.0' 18 | gem 'psych' 19 | end 20 | 21 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/to_yamlfrag.rb: -------------------------------------------------------------------------------- 1 | class Object 2 | 3 | # Same as `#to_yaml` but removes the initial `---` line. 4 | # 5 | # @return [String] 6 | def to_yamlfrag 7 | str = to_yaml 8 | if /\A--- !/ =~ str 9 | str = str.sub(/\A--- !.*?$/,'') #.lstrip 10 | else 11 | str = str.sub(/\A---\s*/,'') 12 | end 13 | str.chomp("\n...\n") 14 | end 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /lib/proutils.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'rbconfig' 3 | require 'ansi/core' 4 | 5 | require_relative 'proutils/core_ext' 6 | require_relative 'proutils/config_utils' 7 | require_relative 'proutils/email_utils' 8 | require_relative 'proutils/file_utils' 9 | require_relative 'proutils/info_utils' 10 | require_relative 'proutils/path_utils' 11 | require_relative 'proutils/stdio_utils' 12 | require_relative 'proutils/shell_utils' 13 | 14 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/to_actual_filename.rb: -------------------------------------------------------------------------------- 1 | class String 2 | 3 | # Find actual filename (casefolding) and returns it. 4 | # Returns nil if no file is found. 5 | # 6 | # Returns [String] 7 | 8 | def to_actual_filename 9 | Dir.glob(self, File::FNM_CASEFOLD).first 10 | end 11 | 12 | # Find actual filename (casefolding) and replace string with it. 13 | # If file not found, string remains the same and method returns nil. 14 | # 15 | # Returns [String] 16 | 17 | def to_actual_filename! 18 | filename = to_actual_filename 19 | replace(filename) if filename 20 | end 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .index .yardopts bin lib man demo spec test *.md *.txt 2 | .index 3 | .yardopts 4 | lib/proutils/config_utils.rb 5 | lib/proutils/core_ext/facets.rb 6 | lib/proutils/core_ext/filetest.rb 7 | lib/proutils/core_ext/to_actual_filename.rb 8 | lib/proutils/core_ext/to_console.rb 9 | lib/proutils/core_ext/to_list.rb 10 | lib/proutils/core_ext/to_yamlfrag.rb 11 | lib/proutils/core_ext.rb 12 | lib/proutils/email_utils.rb 13 | lib/proutils/file_utils.rb 14 | lib/proutils/info_utils.rb 15 | lib/proutils/path_utils.rb 16 | lib/proutils/shell_utils.rb 17 | lib/proutils/stdio_utils.rb 18 | lib/proutils.rb 19 | demo/01_proutils.md 20 | README.md 21 | HISTORY.md 22 | LICENSE.txt 23 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/to_list.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | # Helper method for cleaning list options. This simply returns 4 | # the array making sure there are no nil elements. 5 | # 6 | # @return [Array] 7 | def to_list 8 | compact 9 | end 10 | 11 | end 12 | 13 | class NilClass 14 | 15 | # Helper method for cleaning list options. This returns an 16 | # empty array. 17 | # 18 | # @return [Array] 19 | def to_list 20 | [] 21 | end 22 | 23 | end 24 | 25 | class String 26 | 27 | # Helper method for cleaning list options. This will split the 28 | # a string on `:`, `;` or `,`. --if it is a string, rather than 29 | # an array. And it will make sure there are no nil elements. 30 | # 31 | # @return [Array] 32 | def to_list 33 | split(/[:;,\n]/) 34 | end 35 | 36 | end 37 | 38 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/filetest.rb: -------------------------------------------------------------------------------- 1 | module FileTest 2 | 3 | module_function 4 | 5 | # Return a cached list of the PATH environment variable. 6 | # This is a support method used by #bin? 7 | # 8 | # Returns [Array] 9 | 10 | def command_paths 11 | @command_paths ||= ( 12 | list = [] 13 | if path = ENV['PATH'] 14 | list.concat path.split(/[:;]/) 15 | end 16 | list 17 | ) 18 | end 19 | 20 | # Is a file a bin/ executable? 21 | # 22 | # TODO: Make more robust. Probably needs to be fixed for Windows. 23 | # What's the right way to do this? 24 | # 25 | # Returns the file name or false. [String,False] 26 | 27 | def bin?(fname) 28 | is_bin = command_paths.any? do |dir| 29 | file = File.join(dir, fname) 30 | FileTest.exist?(file) 31 | end 32 | #is_bin ? file : false 33 | is_bin ? fname : false 34 | end 35 | 36 | end 37 | 38 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/facets.rb: -------------------------------------------------------------------------------- 1 | # I know some people will be deterred by the dependency on Facets b/c they 2 | # see it as a "heavy" dependency. But really that is far from true, consider 3 | # the fact that the following libs are all that it uses. We're talking maybe 4 | # two dozen small, but very helpful, methods. 5 | 6 | #require 'facets/boolean' 7 | 8 | require 'facets/filetest' 9 | require 'facets/fileutils' 10 | require 'facets/pathname' 11 | 12 | require 'facets/array/not_empty' 13 | 14 | # DEPRECATE: when new #glob is working. 15 | require 'facets/dir/multiglob' 16 | 17 | require 'facets/kernel/yes' # pulls in #ask and #no? too. 18 | require 'facets/kernel/silence' # FIXME ??? 19 | require 'facets/kernel/disable_warnings' 20 | 21 | require 'facets/module/basename' 22 | require 'facets/module/alias_accessor' 23 | 24 | require 'facets/string/unfold' 25 | 26 | 27 | class String 28 | # DEPRECATE 29 | alias unfold_paragraphs unfold 30 | end 31 | 32 | -------------------------------------------------------------------------------- /lib/proutils/info_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | ## 4 | # Methods for accessing system information. 5 | # 6 | module InfoUtils 7 | 8 | # Access RBConfig::CONFIG as an Struct. 9 | # 10 | # TODO: Currently this returns an OpenStruct, but should be 11 | # a regular Struct instead? 12 | # 13 | def rbconfig 14 | @_rbconfig ||= ( 15 | c = ::RbConfig::CONFIG.rekey{ |k| k.to_s.downcase } 16 | OpenStruct.new(c) 17 | ) 18 | end 19 | 20 | # The Ruby command, i.e. the path the the Ruby executable. 21 | def ruby_command 22 | @_ruby_command ||= ( 23 | bindir = ::RbConfig::CONFIG['bindir'] 24 | rubyname = ::RbConfig::CONFIG['ruby_install_name'] 25 | File.join(bindir, rubyname).sub(/.*\s.*/m, '"\&"') 26 | ) 27 | end 28 | 29 | # Current platform. 30 | def current_platform 31 | require 'facets/platform' 32 | Platform.local.to_s 33 | end 34 | 35 | # Make the instance level available at the class level too. 36 | extend self 37 | 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /Indexfile: -------------------------------------------------------------------------------- 1 | 2 | title "ProUtils" 3 | 4 | name "proutils" 5 | 6 | version "0.4.0" 7 | 8 | summary "Project utility methods" 9 | 10 | author "Trans " 11 | 12 | description "ProUtils provides a large set of utitlity methods useful " \ 13 | "for project development requirements. The methods are " \ 14 | "divided up into a set of topic modules to help maximize " \ 15 | "reusability." 16 | 17 | organization "Rubyworks" 18 | 19 | repositories upstream: "git://github.com/rubyworks/proutils.git" 20 | 21 | resources home: "http://rubyworks.github.com/proutils", 22 | docs: "http://rubydoc.info/gems/proutils/frames", 23 | code: "http://github.com/rubyworks/proutils", 24 | bugs: "http://github.com/rubyworks/proutils/issues", 25 | mail: "http://groups.google.com/group/rubyworks-mailinglist" 26 | 27 | created "2014-01-14" 28 | 29 | copyright "2014 Rubyworks (BSD-2-Clause)" 30 | 31 | paths load: "lib" 32 | 33 | -------------------------------------------------------------------------------- /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 | * Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY Thomas Sawyer ``AS IS'' AND ANY EXPRESS 14 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 16 | NO EVENT SHALL Thomas Sawyer OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 17 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 | BUT 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 | (SPDX: BSD-2-Clause) 25 | 26 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - Indexfile 6 | - Gemfile 7 | authors: 8 | - name: Trans 9 | email: transfire@gmail.com 10 | organizations: 11 | - name: Rubyworks 12 | requirements: 13 | - version: '>= 0' 14 | name: facets 15 | - groups: 16 | - test 17 | version: '>= 0' 18 | name: qed 19 | - engines: 20 | - name: rbx 21 | version: 0+ 22 | version: ~> 2.0 23 | name: rubysl 24 | - engines: 25 | - name: rbx 26 | version: 0+ 27 | version: '>= 0' 28 | name: psych 29 | conflicts: [] 30 | alternatives: [] 31 | resources: 32 | - type: home 33 | uri: http://rubyworks.github.com/proutils 34 | label: Homepage 35 | - type: docs 36 | uri: http://rubydoc.info/gems/proutils/frames 37 | label: Documentation 38 | - type: code 39 | uri: http://github.com/rubyworks/proutils 40 | label: Source Code 41 | - type: bugs 42 | uri: http://github.com/rubyworks/proutils/issues 43 | label: Issue Tracker 44 | - type: mail 45 | uri: http://groups.google.com/group/rubyworks-mailinglist 46 | label: Mailing List 47 | repositories: 48 | - name: upstream 49 | scm: git 50 | uri: git://github.com/rubyworks/proutils.git 51 | categories: [] 52 | copyrights: 53 | - holder: Rubyworks 54 | year: '2014' 55 | license: BSD-2-Clause 56 | customs: [] 57 | paths: 58 | load: 59 | - lib 60 | title: ProUtils 61 | name: proutils 62 | version: 0.4.0 63 | summary: Project utility methods 64 | description: ProUtils provides a large set of utitlity methods useful for project 65 | development requirements. The methods are divided up into a set of topic modules 66 | to help maximize reusability. 67 | created: '2014-01-14' 68 | date: '2014-01-20' 69 | -------------------------------------------------------------------------------- /lib/proutils/stdio_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | ## 4 | # Standard IO utility methods. 5 | # 6 | module StdioUtils 7 | 8 | include ConfigUtils 9 | 10 | # 11 | def stdin 12 | $stdin 13 | end 14 | 15 | # 16 | def stdout 17 | # return dev/null if silent? 18 | $stdout 19 | end 20 | 21 | # 22 | def stderr 23 | $stderr 24 | end 25 | 26 | # 27 | def print(str=nil) 28 | return if silent? 29 | stdout.print(str.to_s) 30 | end 31 | 32 | # 33 | def puts(str=nil) 34 | return if silent? 35 | stdout.puts(str.to_s) 36 | end 37 | 38 | # 39 | def warn(message) 40 | #return if silent? 41 | stderr.puts "WARNING ".ansi(:yellow) + message.to_s 42 | end 43 | 44 | # 45 | def status(message) 46 | return if silent? 47 | stdout.puts "#{message}".ansi(:bold) 48 | end 49 | 50 | # Same as status. 51 | # 52 | # @deprecated 53 | # Doubley redundant with #status and #puts. 54 | alias report status 55 | 56 | # Internal trace report. Only output if in trace mode. 57 | def trace(message) 58 | #return if silent? 59 | if trace? 60 | stderr.print "(TRIAL RUN) " if trial? 61 | stderr.puts message 62 | end 63 | end 64 | 65 | # Convenient method to get simple console reply. 66 | def ask(question) 67 | stdout.print "#{question} " 68 | stdout.flush 69 | input = stdin.gets #until inp = stdin.gets ; sleep 1 ; end 70 | input.strip 71 | end 72 | 73 | # TODO: Until we have better support for getting input across 74 | # platforms, we are using #ask for passwords too. 75 | def password(prompt=nil) 76 | prompt ||= "Enter Password: " 77 | ask(prompt) 78 | end 79 | 80 | # Make the instance level available at the class level too. 81 | extend self 82 | 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /lib/proutils/shell_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | ## 4 | # ShellUtils provides the whole slew of FileUtils, 5 | # FileTest and File class methods in a single module 6 | # and modifies methods according to noop? and verbose? 7 | # options. 8 | # 9 | module ShellUtils 10 | 11 | include ConfigUtils 12 | 13 | # Shell delegator. 14 | def shell 15 | @_shell ||= Shell.new 16 | end 17 | 18 | # Shell out to ruby. 19 | def ruby(*argv) 20 | shell.out(InfoUtils.ruby_command, *argv) 21 | end 22 | 23 | # Shortcut to `shell.out` popularized by Rake. 24 | def sh(*argv) 25 | shell.out(*argv) 26 | end 27 | 28 | # Deprecated: Delegate to Ratch::Shell instance. 29 | #def shell(path=Dir.pwd) 30 | # @shell ||= {} 31 | # @shell[path] ||= ( 32 | # mode = { 33 | # :noop => trial?, 34 | # :verbose => trace? || (trial? && !quiet?), 35 | # :quiet => quiet? 36 | # } 37 | # Ratch::Shell.new(path, mode) 38 | # ) 39 | #end 40 | 41 | # Make the instance level available at the class level too. 42 | extend self 43 | 44 | end 45 | 46 | ## 47 | # Shell class acts as a delegator for shelling out to the command 48 | # line. 49 | # 50 | # shell = Shell.new 51 | # shell.rdoc "-a lib/**/*.rb" 52 | # 53 | class Shell 54 | 55 | include ConfigUtils 56 | include StdioUtils 57 | 58 | # 59 | def initialize 60 | end 61 | 62 | # Shell out. 63 | # 64 | # Returns success of executation. [Boolean] 65 | def out(*argv) 66 | cmd = argv.map{ |a| a.to_s }.join(' ') 67 | 68 | trace(cmd) 69 | return true if noop? 70 | 71 | success = nil 72 | if silent? 73 | silently{ success = system(cmd) } 74 | else 75 | success = system(cmd) 76 | end 77 | success 78 | end 79 | 80 | # Shell out to ruby. 81 | # 82 | # Returns success of executation. [Boolean] 83 | def ruby(*argv) 84 | out(InfoUtils.ruby_command, *argv) 85 | end 86 | 87 | # 88 | def method_missing(c, *a, &b) 89 | super(c, *a, &b) if b 90 | out(c, *a) 91 | end 92 | 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProUtils 2 | 3 | [Website](http://rubyworks.github.io/proutils) / 4 | [Report Issue](http://github.com/rubyworks/proutils/issues) / 5 | [Development](http://github.com/rubyworks/proutils) 6 | 7 | [![Build Status](http://travis-ci.org/rubyworks/proutils.png)](http://travis-ci.org/rubyworks/proutils) 8 | [![Gem Version](https://badge.fury.io/rb/proutils.png)](http://badge.fury.io/rb/proutils)     9 | [![Flattr Me](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/324911/Rubyworks-Ruby-Development-Fund) 10 | 11 | 12 | ## About 13 | 14 | ProUtils is a set of utility methods design primarily for project 15 | development. 16 | 17 | 18 | ## Usage 19 | 20 | ProUtils provides its utility methods in a handful of independent modules. 21 | 22 | * `ConfigUtils` - Used by all modules to handle configuration of utility methods. 23 | * `EmailUtils` - Provides utility methods to make it easy to send emails. 24 | * `InfoUtils` - Provides utility methods for accessing system information. 25 | * `PathUtils` - Convenience methods for working with file system paths. 26 | * `ShellUtils` - Utility methods for shelling out to the command line. 27 | * `StdioUtils` - Methods for read and writing to standard IO. 28 | * `FileUtils` - An expanded version of Ruby's FileUtils module. 29 | 30 | There will likely be a few more modules added in the future and some could 31 | be reformulated a bit, as this project is still in an fairly early stage 32 | of development. 33 | 34 | Also included are a few supporting classes that can be used independently 35 | as well. 36 | 37 | * `Path` - An improved subclass of `Pathname` that takes configuration into account. 38 | * `Shell` - A delegator for shelling out to the command line via method calls. 39 | 40 | Lastly, there are two dozen or so core extensions cherry picked from Ruby Facets 41 | to facilitate the utility methods. 42 | 43 | 44 | ## Tips 45 | 46 | If you don't want to fill up a class with a ton of FileUtils methods, 47 | but still want relatively easy access to those methods then define 48 | a file system delegator. 49 | 50 | def fs 51 | ProUtils::FileUtils 52 | end 53 | 54 | Then you can just call against `fs` as needed, e.g. 55 | 56 | fs.write("hello.txt", "Hello World!") 57 | 58 | 59 | ## Copyrights 60 | 61 | Copyright (c) 2014 Rubyworks 62 | 63 | 64 | -------------------------------------------------------------------------------- /lib/proutils/core_ext/to_console.rb: -------------------------------------------------------------------------------- 1 | # TODO: Improve the naming scheme of these methods. 2 | # TODO: Replace these with facets/shellwords ? 3 | 4 | class Array 5 | # Convert an array into commandline parameters. 6 | # The array is accepted in the format of Ruby 7 | # method arguments --ie. [arg1, arg2, ..., hash] 8 | # 9 | # Returns [String] 10 | 11 | def to_console 12 | #flags = (Hash===last ? pop : {}) 13 | #flags = flags.to_console 14 | #flags + ' ' + join(" ") 15 | to_argv.join(' ') 16 | end 17 | 18 | # TODO: DEPRECATE 19 | alias_method :to_params, :to_console 20 | 21 | # Convert to argument vector. If the last element in the 22 | # array is a Hash then call #to_argv on it and merge results. 23 | # 24 | # Returns [Array] 25 | def to_argv 26 | flags = (Hash===last ? pop : {}) 27 | flags = flags.to_argv 28 | flags + self 29 | end 30 | 31 | # def to_console 32 | # flags = (Hash===last ? pop : {}) 33 | # flags = flags.collect do |f,v| 34 | # m = f.to_s.size == 1 ? '-' : '--' 35 | # case v 36 | # when Array 37 | # v.collect{ |e| "#{m}#{f} '#{e}'" }.join(' ') 38 | # when true 39 | # "#{m}#{f}" 40 | # when false, nil 41 | # '' 42 | # else 43 | # "#{m}#{f} '#{v}'" 44 | # end 45 | # end 46 | # return (flags + self).join(" ") 47 | # end 48 | 49 | end 50 | 51 | class Hash 52 | 53 | # Convert a Hash into command line arguments. 54 | # The array is accepted in the format of Ruby 55 | # method arguments --ie. [arg1, arg2, ..., hash] 56 | # 57 | # Returns [String] 58 | def to_console 59 | to_argv.join(' ') 60 | end 61 | 62 | # Convert a Hash into command line parameters. 63 | # The array is accepted in the format of Ruby 64 | # method arguments --ie. [arg1, arg2, ..., hash] 65 | # 66 | # Returns [Array] 67 | def to_argv 68 | flags = [] 69 | each do |f,v| 70 | m = f.to_s.size == 1 ? '-' : '--' 71 | case v 72 | when Array 73 | v.each{ |e| flags << "#{m}#{f}='#{e}'" } 74 | when true 75 | flags << "#{m}#{f}" 76 | when false, nil 77 | # nothing 78 | else 79 | flags << "#{m}#{f}='#{v}'" 80 | end 81 | end 82 | flags 83 | end 84 | 85 | # Turn a hash into arguments. 86 | # 87 | # h = { :list => [1,2], :base => "HI" } 88 | # h.argumentize #=> [ [], { :list => [1,2], :base => "HI" } ] 89 | # h.argumentize(:list) #=> [ [1,2], { :base => "HI" } ] 90 | # 91 | # Returns [Array] 92 | def argumentize(args_field=nil) 93 | config = dup 94 | if args_field 95 | args = [config.delete(args_field)].flatten.compact 96 | else 97 | args = [] 98 | end 99 | args << config 100 | return args 101 | end 102 | 103 | # Alias for #argumentize. 104 | alias_method :command_vector, :argumentize 105 | 106 | end 107 | -------------------------------------------------------------------------------- /lib/proutils/path_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | ## 4 | # Project path utilities. 5 | # 6 | module PathUtils 7 | 8 | include ConfigUtils 9 | 10 | # Shortcut for create a `Path` instance. 11 | def path(pathname) 12 | Path.new(pathname) 13 | end 14 | 15 | # Alias for #path. 16 | alias :file :path 17 | 18 | # Project root directory. If not already set, this method will try 19 | # to find the project root using SCM markers. If not found, it 20 | # assumes the current working directory must be the project root. 21 | # 22 | # Returns [Pathname] 23 | def root 24 | config.root ||= ( 25 | find_root || Path.new(Dir.pwd) 26 | ) 27 | end 28 | 29 | # Find project root directory looking for SCM markers. 30 | # 31 | # Returns [Pathname] 32 | def find_root 33 | path = nil 34 | home = File.expand_path('~') 35 | 36 | while dir != home && dir != '/' 37 | if Dir[root_pattern].first 38 | path = dir 39 | break 40 | end 41 | dir = File.dirname(dir) 42 | end 43 | 44 | path ? Path.new(path, config) : nil 45 | end 46 | 47 | # Apply a naming policy to a file name and extension. 48 | # 49 | # Returns the file name. [String] 50 | def apply_file_name_policy(name, ext) 51 | filename_policy.each do |policy| 52 | case policy.to_s 53 | when /^low/, /^down/ 54 | name = name.downcase 55 | when /^up/ 56 | name = name.upcase 57 | when /^cap/ 58 | name = name.capitalize 59 | when /^ext/ 60 | name = name + ".#{ext}" 61 | end 62 | end 63 | name 64 | end 65 | 66 | # Make the instance level available at the class level too. 67 | extend self 68 | 69 | end 70 | 71 | ## 72 | # The Path class provides all the things you can do with a single path. 73 | # It is essentially the same as Ruby Pathname class, except if supports 74 | # the `trace` and `noop` options. 75 | # 76 | # TODO: Add options to #open and #sysopen based on writable permission. 77 | # 78 | class Path < ::Pathname 79 | #include ConfigUtils 80 | #include StdioUtils 81 | 82 | # Pathname methods that can modify the file system. 83 | DRYRUN_METHODS = [ 84 | :chmod, :lchmod, :chown, :lchown, :make_link, :rename, 85 | :make_symlink, :truncate, :utime, :mkdir, :mkpath, 86 | :rmdir, :rmtree, :unlink, :delete 87 | ] 88 | 89 | DRYRUN_METHODS.each do |methname| 90 | module_eval %{ 91 | def #{methname}(*args) 92 | if $TRACE 93 | $stderr.print "(TRIAL RUN) " if $NOOP 94 | $stderr.puts "path #{methname} \#{self} \#{args.join(' ')}" 95 | end 96 | super(*args) unless $NOOP 97 | end 98 | } 99 | end 100 | 101 | # Write to file. 102 | def write(*args) 103 | $stderr.print "(TRIAL RUN) " if $NOOP 104 | $stderr.puts "write #{path}" if $TRACE 105 | super(*args) unless $NOOP 106 | end 107 | 108 | # Append to file. 109 | def append(text) 110 | $stderr.print "(TRIAL RUN) " if $NOOP 111 | $stderr.puts "path append #{path}" if trace? 112 | File.open(path, 'a'){ |f| f << text } unless noop? 113 | end 114 | 115 | # Does path have the same content as another path? 116 | # 117 | # TODO: Better name? 118 | def same?(other) 119 | # TODO: Raname FileTest.indentical? 120 | FileTest.identical?(path, other) 121 | end 122 | 123 | end 124 | 125 | end 126 | -------------------------------------------------------------------------------- /lib/proutils/file_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | module FileUtils 4 | 5 | include ConfigUtils 6 | 7 | # -- Dir Methods ---------------------------------------------------------- 8 | 9 | # 10 | def glob(*args) 11 | Dir.glob(*args) 12 | end 13 | 14 | # TODO: Ultimately merge #glob and #multiglob. 15 | def multiglob(*args, &blk) 16 | Dir.multiglob(*args, &blk) 17 | end 18 | 19 | # 20 | def multiglob_r(*args, &blk) 21 | Dir.multiglob_r(*args, &blk) 22 | end 23 | 24 | # -- File Methods --------------------------------------------------------- 25 | 26 | # Read file. 27 | def read(path) 28 | File.read(path) 29 | end 30 | 31 | # Write file. 32 | def write(path, text) 33 | stderr.puts "write #{path}" if trace? 34 | File.open(path, 'w'){ |f| f << text } unless noop? 35 | end 36 | 37 | # Append to file. 38 | def append(path, text) 39 | stderr.puts "append #{path}" if trace? 40 | File.open(path, 'a'){ |f| f << text } unless noop? 41 | end 42 | 43 | # 44 | def atime(*args) ; File.ctime(*args) ; end 45 | def ctime(*args) ; File.ctime(*args) ; end 46 | def mtime(*args) ; File.mtime(*args) ; end 47 | 48 | def utime(*args) 49 | File.utime(*args) unless noop? 50 | end 51 | 52 | # -- FileTest Methods ----------------------------------------------------- 53 | 54 | # 55 | def size(path) ; FileTest.size(path) ; end 56 | def size?(path) ; FileTest.size?(path) ; end 57 | def directory?(path) ; FileTest.directory?(path) ; end 58 | def symlink?(path) ; FileTest.symlink?(path) ; end 59 | def readable?(path) ; FileTest.readable?(path) ; end 60 | def chardev?(path) ; FileTest.chardev?(path) ; end 61 | def exist?(path) ; FileTest.exist?(path) ; end 62 | def exists?(path) ; FileTest.exists?(path) ; end 63 | def zero?(path) ; FileTest.zero?(path) ; end 64 | def pipe?(path) ; FileTest.pipe?(path) ; end 65 | def file?(path) ; FileTest.file?(path) ; end 66 | def sticky?(path) ; FileTest.sticky?(path) ; end 67 | def blockdev?(path) ; FileTest.blockdev?(path) ; end 68 | def grpowned?(path) ; FileTest.grpowned?(path) ; end 69 | def setgid?(path) ; FileTest.setgid?(path) ; end 70 | def setuid?(path) ; FileTest.setuid?(path) ; end 71 | def socket?(path) ; FileTest.socket?(path) ; end 72 | def owned?(path) ; FileTest.owned?(path) ; end 73 | def writable?(path) ; FileTest.writable?(path) ; end 74 | def executable?(path) ; FileTest.executable?(path) ; end 75 | 76 | def safe?(path) ; FileTest.safe?(path) ; end 77 | 78 | def relative?(path) ; FileTest.relative?(path) ; end 79 | def absolute?(path) ; FileTest.absolute?(path) ; end 80 | 81 | def writable_real?(path) ; FileTest.writable_real?(path) ; end 82 | def executable_real?(path) ; FileTest.executable_real?(path) ; end 83 | def readable_real?(path) ; FileTest.readable_real?(path) ; end 84 | 85 | # TODO: better name for this? 86 | def same?(path, other) 87 | FileTest.identical?(path, other) 88 | end 89 | 90 | # -- FileUtils Methods ---------------------------------------------------- 91 | 92 | # Returns Ruby's FileUtils module based on mode. 93 | def fileutils 94 | if dryrun? 95 | ::FileUtils::DryRun 96 | elsif noop? or trial? 97 | ::FileUtils::Noop 98 | elsif trace? 99 | ::FileUtils::Verbose 100 | else 101 | ::FileUtils 102 | end 103 | end 104 | 105 | # Fallback to filutils. 106 | # 107 | # TODO: delegate this better? 108 | def method_missing(s, *a, &b) 109 | if fileutils.respond_to?(s) 110 | fileutils.send(s, *a, &b) 111 | else 112 | super(s, *a, &b) 113 | end 114 | end 115 | 116 | # Make the instance level available at the class level too. 117 | extend self 118 | 119 | end 120 | 121 | end 122 | -------------------------------------------------------------------------------- /lib/proutils/config_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | require 'pathname' 4 | 5 | ## 6 | # ConfigUtils module provides an interface to configuration. 7 | # 8 | # TODO: Should these all just be globals instead? Then we don't 9 | # have to fuss with passing them around. If they need to be 10 | # changed for specific section of code then we can create a 11 | # wrapper, e.g. 12 | # 13 | # configure(options) do 14 | # # make calls 15 | # end 16 | # 17 | # But that is not thread safe :(. Can we fix with Mutex locks? 18 | # If it wasn't for the threading question this would probably 19 | # be a done deal. 20 | # 21 | module ConfigUtils 22 | 23 | # List of attributes. 24 | ATTRIBUTES = [ 25 | :silent, :quiet, :trial, :noop, :verbose, :debug, :trace, 26 | :root_pattern, :file_name_policy, :stdin, :stdout, :stderr 27 | ] 28 | 29 | # FIXME: How to handle exactly? 30 | def configure(options, &block) 31 | if block 32 | save = options.keys.map do |k| 33 | [k, send(k)] 34 | end 35 | else 36 | options.each do |name, value| 37 | send("#{name}=", value) if ATTRIBUTES.include?(name.to_sym) 38 | end 39 | end 40 | end 41 | 42 | # When silent the output to $stdout is supressed. 43 | # 44 | # Returns [Boolean] 45 | def silent? 46 | $SILENT 47 | end 48 | 49 | # Set `$SILENT` to `true` or `false`. 50 | def silent=(boolean) 51 | $SILENT = !!boolean 52 | end 53 | 54 | # Alias for `#silent?`. 55 | alias quiet? silent? 56 | 57 | # Alias for `#silent=`. 58 | alias quiet= silent= 59 | 60 | # Trace flag instructs code to show progress when significant actions 61 | # take place. The output should go to `$stderr`. The {StdioUtils#trace} 62 | # method is provided to simplify this. 63 | def trace? 64 | $TRACE 65 | end 66 | 67 | # Set `$TRACE` to `true` or `false`. 68 | def trace=(boolean) 69 | $TRACE = !!boolean 70 | end 71 | 72 | # Debug mode? 73 | # 74 | # Returns [Boolean] 75 | def debug? 76 | $DEBUG 77 | end 78 | 79 | # Set `$DEBUG` to `true` or `false`. 80 | def debug=(boolean) 81 | $DEBUG = !!boolean 82 | end 83 | 84 | # Warning level. 85 | # 86 | # Returns [nil,Integer] 87 | def warn? 88 | $WARN 89 | end 90 | 91 | # Set the warning level. It can be `false` (or `nil`) to mean 92 | # no warnings or any positive integer to indicate a level of warnings 93 | # The higher the level the more types of warnings one will see. 94 | # 95 | # Unfortunately, Ruby is a bit limited in levels, and it only 96 | # natively support two levels handled as the `false` and `true` 97 | # values of the $VERBOSE global variable. 98 | # 99 | def warn=(level) 100 | $WARN = ( 101 | case level 102 | when Integer 103 | level = level.abs 104 | level == 0 ? nil : level 105 | when true 106 | 2 107 | else 108 | nil 109 | end 110 | ) 111 | 112 | # Ruby's handling of warning levels. 113 | $VERBOSE = ( 114 | case $WARN 115 | when nil, false then nil 116 | when 1 then false 117 | else true 118 | end 119 | ) 120 | end 121 | 122 | # 123 | def noop? 124 | $NOOP 125 | end 126 | 127 | # 128 | def noop=(boolean) 129 | $NOOP = !!boolean 130 | end 131 | 132 | # A `dryrun` is the same a `trace` and `noop` togehter. 133 | def dryrun? 134 | trace? && noop? 135 | end 136 | 137 | # 138 | def dryrun=(boolean) 139 | self.trace = boolean 140 | self.noop = boolean 141 | end 142 | 143 | # Alias for `dryrun`. 144 | alias trial? dryrun? 145 | alias trial= dryrun= 146 | 147 | # Access to `$stdin`. 148 | # 149 | # Returns [IO] 150 | def stdin 151 | $stdin 152 | end 153 | 154 | # 155 | def stdin=(io) 156 | $stdin = io 157 | end 158 | 159 | # Access to `$stdout`. 160 | # 161 | # Returns [IO] 162 | def stdout 163 | $stdout 164 | end 165 | 166 | # 167 | def stdout=(io) 168 | $stdout = io 169 | end 170 | 171 | # Access to `$stderr`. 172 | # 173 | # Returns [IO] 174 | def stderr 175 | $stdrr 176 | end 177 | 178 | # 179 | def stderr=(io) 180 | $stderr = io 181 | end 182 | 183 | # Default project root pattern. 184 | $ROOT_PATTERN = "{.index,.git,.hg,.svn,_darcs}" 185 | 186 | # Glob for finding root of a project. 187 | # 188 | # TODO: Get the from an environment variable first? 189 | # 190 | # Returns glob string. [String] 191 | def root_pattern 192 | $ROOT_PATTERN 193 | end 194 | 195 | # The project root pattern glob. 196 | # 197 | # glob - [String] Pattern used by `Dir.glob()` to locate a 198 | # project's root directory. 199 | # 200 | def root_pattern=(glob) 201 | $ROOT_PATTERN = glob 202 | end 203 | 204 | # Probably can do without the filename policy. But if some tool 205 | # saves to a file, and the default naming scheme is to the users 206 | # liking then they should have the option of chaning the name 207 | # of file explicitly. Being able to adjust the "style" of name 208 | # os useful then. 209 | 210 | # Deprecated: Default file name policy. 211 | $FILENAME_POLICY = [:down, :ext] 212 | 213 | # Deprecated: A file name policy detrmines how files that 214 | # have flexiable naming options are to be named exactly. 215 | # For instance, a file may optionally be capitialized 216 | # or not. A file name policy of `[:cap]` can then be used 217 | # to ... this is stupid. 218 | # 219 | # Returns [Array] 220 | def filename_policy 221 | $FILENAME_POLICY 222 | end 223 | 224 | # Deprecated: Set file name policy. 225 | def filename_policy=(entry) 226 | $FILENAME_POLICY ||= ( 227 | Array(entry).map{ |s| s.to_s } 228 | ) 229 | end 230 | 231 | # Force opertations that otherwise would have been skipped 232 | # or raised an error for catuionary reasons. 233 | # 234 | # NOTE: There is a reluctance to provide a global for the 235 | # force setting. Maybe force should be on a per use basis 236 | # and not a global config at all? 237 | # 238 | # Returns [Boolean] 239 | def force? 240 | $FORCE 241 | end 242 | 243 | # Set force setting `true` or `false`. 244 | def force=(boolean) 245 | $FORCE = !!boolean 246 | end 247 | 248 | end 249 | 250 | end 251 | 252 | =begin 253 | # This allows config to be used in some places where a hash would be used. 254 | def [](name) 255 | return nil unless ATTRIBUTES.include?(name.to_sym) 256 | if respond_to?(name) 257 | send(name) 258 | elsif respond_to?("#{name}?") 259 | send("#{name}?") 260 | else 261 | nil 262 | end 263 | end 264 | 265 | # 266 | def to_h 267 | h = {} 268 | ATTRIBUTES.each do |a| 269 | h[a] = send(a) 270 | end 271 | h 272 | end 273 | =end 274 | 275 | -------------------------------------------------------------------------------- /.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 | # TODO: how best to handle; why doesn't ruygems have unified gems? 208 | # NOTE: Must change environment variable RUBY_ENGINE to build gem for 209 | # different platform. 210 | if engines = req['engines'] 211 | next if engines.none? do |engine| 212 | engine['name'] == (ENV['RUBY_ENGINE'] || RUBY_ENGINE) 213 | # TODO: version comparison (ugh!) 214 | end 215 | end 216 | 217 | name = req['name'] 218 | groups = req['groups'] || [] 219 | 220 | version = gemify_version(req['version']) 221 | 222 | if groups.empty? or groups.include?('runtime') 223 | # populate runtime dependencies 224 | if gemspec.respond_to?(:add_runtime_dependency) 225 | gemspec.add_runtime_dependency(name,*version) 226 | else 227 | gemspec.add_dependency(name,*version) 228 | end 229 | else 230 | # populate development dependencies 231 | if gemspec.respond_to?(:add_development_dependency) 232 | gemspec.add_development_dependency(name,*version) 233 | else 234 | gemspec.add_dependency(name,*version) 235 | end 236 | end 237 | end 238 | 239 | # convert external dependencies into gemspec requirements 240 | requirements.each do |req| 241 | next unless req['external'] 242 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 243 | end 244 | 245 | gemspec.homepage = homepage 246 | gemspec.require_paths = require_paths 247 | gemspec.post_install_message = metadata['install_message'] 248 | end 249 | 250 | # 251 | # Set gemspec settings that require a root directory path. 252 | # 253 | def to_gemspec_paths(gemspec) 254 | gemspec.files = files 255 | gemspec.extensions = extensions 256 | gemspec.executables = executables 257 | 258 | if Gem::VERSION < '1.7.' 259 | gemspec.default_executable = gemspec.executables.first 260 | end 261 | 262 | gemspec.test_files = glob_files(patterns[:test]) 263 | 264 | unless gemspec.files.include?('.document') 265 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 266 | end 267 | end 268 | 269 | # 270 | # Return a copy of this file. This is used to generate a local 271 | # .gemspec file that can automatically read the index file. 272 | # 273 | def self.source_code 274 | File.read(__FILE__) 275 | end 276 | 277 | private 278 | 279 | def find_root 280 | root_files = patterns[:root] 281 | if Dir.glob(root_files).first 282 | Pathname.new(Dir.pwd) 283 | elsif Dir.glob("../#{root_files}").first 284 | Pathname.new(Dir.pwd).parent 285 | else 286 | #raise "Can't find root of project containing `#{root_files}'." 287 | warn "Can't find root of project containing `#{root_files}'." 288 | nil 289 | end 290 | end 291 | 292 | def glob(pattern) 293 | if File.directory?(pattern) 294 | Dir.glob(File.join(pattern, '**', '*')) 295 | else 296 | Dir.glob(pattern) 297 | end 298 | end 299 | 300 | def gemify_version(version) 301 | case version 302 | when /^(.*?)\+$/ 303 | ">= #{$1}" 304 | when /^(.*?)\-$/ 305 | "< #{$1}" 306 | when /^(.*?)\~$/ 307 | "~> #{$1}" 308 | else 309 | version 310 | end 311 | end 312 | 313 | end 314 | 315 | end 316 | 317 | Indexer::GemspecExporter.gemspec 318 | -------------------------------------------------------------------------------- /lib/proutils/email_utils.rb: -------------------------------------------------------------------------------- 1 | module ProUtils 2 | 3 | ## 4 | # The Email utility module provides an easy to use `email` method. 5 | # 6 | # TODO: Integrate with Config. 7 | # 8 | module EmailUtils 9 | 10 | # Email function to easily send out an email. 11 | # 12 | # Settings: 13 | # 14 | # subject Subject of email message. 15 | # from Message FROM address [email]. 16 | # to Email address to send announcemnt. 17 | # server Email server to route message. 18 | # port Email server's port. 19 | # domain Email server's domain name. 20 | # account Email account name if needed. 21 | # password Password for login.. 22 | # login Login type: plain, cram_md5 or login [plain]. 23 | # secure Uses TLS security, true or false? [false] 24 | # message Mesage to send -or- 25 | # file File that contains message. 26 | # 27 | # TODO: Add trace and noop support. 28 | # 29 | # Returns success status. [Boolean] 30 | def email(options) 31 | #options[:file] = localize(options[:file]) if options[:file] 32 | emailer = Emailer.new(options.rekey) 33 | success = emailer.email 34 | if Exception === success 35 | puts "Email failed: #{success.message}." 36 | false 37 | else 38 | puts "Email sent successfully to #{success.join(';')}." 39 | true 40 | end 41 | end 42 | 43 | # Make the instance level available at the class level too. 44 | extend self 45 | 46 | end 47 | 48 | # Emailer class makes it easy send out an email. 49 | # 50 | # Settings: 51 | # 52 | # subject Subject of email message. 53 | # from Message FROM address [email]. 54 | # to Email address to send announcemnt. 55 | # server Email server to route message. 56 | # port Email server's port. 57 | # port_secure Email server's port. 58 | # domain Email server's domain name. 59 | # account Email account name if needed. 60 | # password Password for login.. 61 | # login Login type: plain, cram_md5 or login [plain]. 62 | # secure Uses TLS security, true or false? [false] 63 | # message Mesage to send -or- 64 | # file File that contains message. 65 | # 66 | class Emailer 67 | 68 | class << self 69 | # Used for caching password between usages. 70 | attr_accessor :password 71 | 72 | # 73 | def environment_options 74 | options = {} 75 | options[:server] = ENV['EMAIL_SERVER'] 76 | options[:from] = ENV['EMAIL_FROM'] 77 | options[:account] = ENV['EMAIL_ACCOUNT'] || ENV['EMAIL_FROM'] 78 | options[:password] = ENV['EMAIL_PASSWORD'] 79 | options[:port] = ENV['EMAIL_PORT'] 80 | options[:domain] = ENV['EMAIL_DOMAIN'] 81 | options[:login] = ENV['EMAIL_LOGIN'] 82 | options[:secure] = ENV['EMAIL_SECURE'] 83 | options 84 | end 85 | 86 | def new_with_environment(options={}) 87 | environment_options.merge(options.rekey) 88 | new(options) 89 | end 90 | end 91 | 92 | attr_accessor :server 93 | attr_accessor :port 94 | attr_accessor :account 95 | attr_accessor :passwd 96 | attr_accessor :login 97 | attr_accessor :secure 98 | attr_accessor :domain 99 | attr_accessor :from 100 | attr_accessor :mailto 101 | attr_accessor :subject 102 | attr_accessor :message 103 | 104 | # 105 | 106 | # 107 | def initialize(options={}) 108 | require_smtp 109 | 110 | options = options.rekey 111 | 112 | if not options[:server] 113 | options = self.class.environment_options.merge(options) 114 | end 115 | 116 | @mailto = options[:to] || options[:mailto] 117 | 118 | @from = options[:from] 119 | @message = options[:message] 120 | @subject = options[:subject] 121 | @server = options[:server] 122 | @account = options[:account] 123 | @passwd = options[:password] 124 | @login = options[:login] 125 | @secure = options[:secure] #.to_b 126 | @domain = options[:domain] 127 | @port = options[:port] 128 | 129 | @port ||= secure ? 465 : 25 130 | @port = @port.to_i 131 | 132 | @account ||= @from 133 | 134 | @login ||= :plain 135 | @login = @login.to_sym 136 | 137 | @passwd ||= self.class.password 138 | 139 | @domain ||= @server 140 | 141 | # save the password for later use 142 | self.class.password = @passwd 143 | end 144 | 145 | # 146 | 147 | def email(options={}) 148 | options.rekey 149 | 150 | message = options[:message] || self.message 151 | subject = options[:subject] || self.subject 152 | from = options[:from] || self.from 153 | mailto = options[:mailto] || options[:to] || self.mailto 154 | 155 | raise ArgumentError, "missing email field -- server" unless server 156 | raise ArgumentError, "missing email field -- account" unless account 157 | 158 | raise ArgumentError, "missing email field -- from" unless from 159 | raise ArgumentError, "missing email field -- mailto" unless mailto 160 | raise ArgumentError, "missing email field -- subject" unless subject 161 | 162 | passwd ||= password("#{account} password: ") 163 | 164 | mailto = [mailto].flatten.compact 165 | 166 | msg = "" 167 | msg << "From: #{from}\n" 168 | msg << "To: #{mailto.join(';')}\n" 169 | msg << "Subject: #{subject}\n" 170 | msg << "" 171 | msg << message 172 | 173 | #p server, port, domain, account, passwd, login, secure if verbose? 174 | 175 | begin 176 | smtp = Net::SMTP.new(server, port) 177 | 178 | if smtp.respond_to?(:enable_starttls_auto) 179 | smtp.enable_starttls_auto 180 | elsif secure #&& smtp.respond_to?(:enable_tls) 181 | smtp.enable_tls 182 | end 183 | 184 | smtp.start(domain, account, passwd, login) do |smtp| 185 | smtp.send_message(msg, from, mailto) 186 | end 187 | 188 | return mailto 189 | rescue Exception => e 190 | return e 191 | end 192 | end 193 | 194 | # Ask for a password. 195 | # 196 | # FIXME: Does not hide password. 197 | 198 | def password(msg=nil) 199 | msg ||= "Enter Password: " 200 | inp = '' 201 | 202 | $stdout << msg 203 | 204 | inp = STDIN.gets.chomp 205 | 206 | #begin 207 | # system "stty -echo" 208 | # inp = gets.chomp 209 | #ensure 210 | # system "stty echo" 211 | #end 212 | 213 | return inp 214 | end 215 | 216 | # 217 | def require_smtp 218 | begin 219 | require 'net/smtp_tls' 220 | rescue LoadError 221 | require 'net/smtp' 222 | end 223 | end 224 | 225 | end 226 | 227 | end 228 | 229 | 230 | 231 | =begin 232 | # Email function to easily send out an email. 233 | # 234 | # Settings: 235 | # 236 | # subject Subject of email message. 237 | # from Message FROM address [email]. 238 | # to Email address to send announcemnt. 239 | # server Email server to route message. 240 | # port Email server's port. 241 | # domain Email server's domain name. 242 | # account Email account name if needed. 243 | # password Password for login.. 244 | # login Login type: plain, cram_md5 or login [plain]. 245 | # secure Uses TLS security, true or false? [false] 246 | # message Mesage to send -or- 247 | # file File that contains message. 248 | # 249 | def email(message, settings) 250 | settings ||= {} 251 | settings.rekey! 252 | 253 | server = settings[:server] 254 | account = settings[:account] || ENV['EMAIL_ACCOUNT'] 255 | passwd = settings[:password] || ENV['EMAIL_PASSWORD'] 256 | login = settings[:login].to_sym 257 | subject = settings[:subject] 258 | mail_to = settings[:to] || settings[:mail_to] 259 | mail_from = settings[:from] || settings[:mail_from] 260 | secure = settings[:secure] 261 | domain = settings[:domain] || server 262 | 263 | port ||= (secure ? 465 : 25) 264 | account ||= mail_from 265 | login ||= :plain 266 | 267 | #mail_to = nil if mail_to.empty? 268 | 269 | raise ArgumentError, "missing email field -- server" unless server 270 | raise ArgumentError, "missing email field -- account" unless account 271 | raise ArgumentError, "missing email field -- subject" unless subject 272 | raise ArgumentError, "missing email field -- to" unless mail_to 273 | raise ArgumentError, "missing email field -- from" unless mail_from 274 | 275 | passwd ||= password(account) 276 | 277 | mail_to = [mail_to].flatten.compact 278 | 279 | msg = "" 280 | msg << "From: #{mail_from}\n" 281 | msg << "To: #{mail_to.join(';')}\n" 282 | msg << "Subject: #{subject}\n" 283 | msg << "" 284 | msg << message 285 | 286 | begin 287 | Net::SMTP.enable_tls if Net::SMTP.respond_to?(:enable_tls) and secure 288 | Net::SMTP.start(server, port, domain, account, passwd, login) do |s| 289 | s.send_message( msg, mail_from, mail_to ) 290 | end 291 | puts "Email sent successfully to #{mail_to.join(';')}." 292 | return true 293 | rescue => e 294 | if trace? 295 | raise e 296 | else 297 | abort "Email delivery failed." 298 | end 299 | end 300 | end 301 | =end 302 | 303 | --------------------------------------------------------------------------------