schacon-github 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +18 -0
- data/Manifest +19 -0
- data/README +164 -0
- data/bin/gh +5 -0
- data/bin/github +5 -0
- data/commands/commands.rb +242 -0
- data/commands/helpers.rb +370 -0
- data/github-gem.gemspec +26 -0
- data/lib/github.rb +146 -0
- data/lib/github/command.rb +99 -0
- data/lib/github/extensions.rb +28 -0
- data/lib/github/helper.rb +4 -0
- data/spec/command_spec.rb +82 -0
- data/spec/extensions_spec.rb +36 -0
- data/spec/github_spec.rb +65 -0
- data/spec/helper_spec.rb +280 -0
- data/spec/spec_helper.rb +138 -0
- data/spec/ui_spec.rb +604 -0
- data/spec/windoze_spec.rb +36 -0
- metadata +92 -0
    
        data/commands/helpers.rb
    ADDED
    
    | @@ -0,0 +1,370 @@ | |
| 1 | 
            +
            helper :user_and_repo_from do |url|
         | 
| 2 | 
            +
              case url
         | 
| 3 | 
            +
              when %r|^git://github\.com/([^/]+/[^/]+)$|: $1.split('/')
         | 
| 4 | 
            +
              when %r|^(?:ssh://)?(?:git@)?github\.com:([^/]+/[^/]+)$|: $1.split('/')
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
            end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            helper :user_and_repo_for do |remote|
         | 
| 9 | 
            +
              user_and_repo_from(url_for(remote))
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            helper :user_for do |remote|
         | 
| 13 | 
            +
              user_and_repo_for(remote).try.first
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            helper :repo_for do |remote|
         | 
| 17 | 
            +
              user_and_repo_for(remote).try.last
         | 
| 18 | 
            +
            end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            helper :project do
         | 
| 21 | 
            +
              repo = repo_for(:origin)
         | 
| 22 | 
            +
              if repo.nil?
         | 
| 23 | 
            +
                if url_for(:origin) == ""
         | 
| 24 | 
            +
                  STDERR.puts "Error: missing remote 'origin'"
         | 
| 25 | 
            +
                else
         | 
| 26 | 
            +
                  STDERR.puts "Error: remote 'origin' is not a github URL"
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                exit 1
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
              repo.chomp('.git')
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            helper :url_for do |remote|
         | 
| 34 | 
            +
              `git config --get remote.#{remote}.url`.chomp
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            helper :local_heads do
         | 
| 38 | 
            +
              `git show-ref --heads --hash`.split("\n")
         | 
| 39 | 
            +
            end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            helper :has_commit? do |sha|
         | 
| 42 | 
            +
              `git show #{sha} >/dev/null 2>/dev/null`
         | 
| 43 | 
            +
              $?.exitstatus == 0
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            helper :resolve_commits do |treeish|
         | 
| 47 | 
            +
              if treeish
         | 
| 48 | 
            +
                if treeish.match(/\.\./)
         | 
| 49 | 
            +
                  commits = `git rev-list #{treeish}`.split("\n")
         | 
| 50 | 
            +
                else
         | 
| 51 | 
            +
                  commits = `git rev-parse #{treeish}`.split("\n")
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              else
         | 
| 54 | 
            +
                # standard in
         | 
| 55 | 
            +
                puts 'reading from stdin...'
         | 
| 56 | 
            +
                commits = $stdin.read.split("\n")
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
              commits.select { |a| a.size == 40 } # only the shas, not the ^SHAs
         | 
| 59 | 
            +
            end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            helper :ignore_file_path do
         | 
| 62 | 
            +
              dir = `git rev-parse --git-dir`.chomp
         | 
| 63 | 
            +
              File.join(dir, 'ignore-shas')
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            helper :ignore_sha_array do
         | 
| 67 | 
            +
              File.open( ignore_file_path ) { |yf| YAML::load( yf ) } rescue {}
         | 
| 68 | 
            +
            end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            helper :remove_ignored do |array, ignore_array|
         | 
| 71 | 
            +
              array.reject { |id| ignore_array[id] }
         | 
| 72 | 
            +
            end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            helper :ignore_shas do |shas|
         | 
| 75 | 
            +
              ignores = ignore_sha_array
         | 
| 76 | 
            +
              shas.each do |sha|
         | 
| 77 | 
            +
                puts 'ignoring ' + sha
         | 
| 78 | 
            +
                ignores[sha] = true
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
              File.open( ignore_file_path, 'w' ) do |out|
         | 
| 81 | 
            +
                YAML.dump( ignores, out )
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
            end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            helper :get_commits do |rev_array|
         | 
| 86 | 
            +
              list = rev_array.select { |a| has_commit?(a) }.join(' ')
         | 
| 87 | 
            +
              `git log --pretty=format:"%H::%ae::%s::%ar::%ad" --no-merges #{list}`.split("\n").map { |a| a.split('::') }
         | 
| 88 | 
            +
            end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            helper :get_cherry do |branch|
         | 
| 91 | 
            +
              `git cherry HEAD #{branch} | git name-rev --stdin`.split("\n").map { |a| a.split(' ') }
         | 
| 92 | 
            +
            end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            helper :get_common do |branch|
         | 
| 95 | 
            +
              `git rev-list ..#{branch} --boundary | tail -1 | git name-rev --stdin`.split(' ')[1] rescue 'unknown'
         | 
| 96 | 
            +
            end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            helper :print_commits do |our_commits, options|
         | 
| 99 | 
            +
              ignores = ignore_sha_array
         | 
| 100 | 
            +
             | 
| 101 | 
            +
              case options[:sort]
         | 
| 102 | 
            +
              when 'branch'
         | 
| 103 | 
            +
                our_commits.sort! { |a, b| a[0][2] <=> b[0][2] }
         | 
| 104 | 
            +
              when 'author'
         | 
| 105 | 
            +
                our_commits.sort! { |a, b| a[1][1] <=> b[1][1] }
         | 
| 106 | 
            +
              else
         | 
| 107 | 
            +
                our_commits.sort! { |a, b| Date.parse(a[1][4]) <=> Date.parse(b[1][4]) } rescue 'cant parse dates'
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              shown_commits = {}
         | 
| 111 | 
            +
              before = Date.parse(options[:before]) if options[:before] rescue puts 'cant parse before date'
         | 
| 112 | 
            +
              after = Date.parse(options[:after]) if options[:after] rescue puts 'cant parse after date'
         | 
| 113 | 
            +
              our_commits.each do |cherry, commit|
         | 
| 114 | 
            +
                status, sha, ref_name = cherry
         | 
| 115 | 
            +
                next if shown_commits[sha] || ignores[sha]
         | 
| 116 | 
            +
                next if options[:project] && !ref_name.match(Regexp.new(options[:project]))
         | 
| 117 | 
            +
                ref_name = ref_name.gsub('remotes/', '')
         | 
| 118 | 
            +
                if status == '+' && commit
         | 
| 119 | 
            +
                  next if options[:author] && !commit[1].match(Regexp.new(options[:author]))
         | 
| 120 | 
            +
                  next if options[:before] && before && (before < Date.parse(commit[4]))  rescue false
         | 
| 121 | 
            +
                  next if options[:after] && after && (after > Date.parse(commit[4])) rescue false
         | 
| 122 | 
            +
                  applies = applies_cleanly(sha)
         | 
| 123 | 
            +
                  next if options[:applies] && !applies
         | 
| 124 | 
            +
                  next if options[:noapply] && applies
         | 
| 125 | 
            +
                  if options[:shas]
         | 
| 126 | 
            +
                    puts sha
         | 
| 127 | 
            +
                  else
         | 
| 128 | 
            +
                    common = options[:common] ? get_common(sha) : ''
         | 
| 129 | 
            +
                    puts [sha[0,6], ref_name.ljust(25), commit[1][0,20].ljust(21),
         | 
| 130 | 
            +
                        commit[2][0, 36].ljust(38), commit[3][0,15], common].join(" ")
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
                shown_commits[sha] = true
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
            helper :applies_cleanly do |sha|
         | 
| 138 | 
            +
              `git diff ...#{sha} | git apply --check >/dev/null 2>/dev/null`
         | 
| 139 | 
            +
              $?.exitstatus == 0
         | 
| 140 | 
            +
            end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            helper :remotes do
         | 
| 143 | 
            +
              regexp = '^remote\.(.+)\.url$'
         | 
| 144 | 
            +
              `git config --get-regexp '#{regexp}'`.split("\n").inject({}) do |memo, line|
         | 
| 145 | 
            +
                name_string, url = line.split(/ /, 2)
         | 
| 146 | 
            +
                m, name = *name_string.match(/#{regexp}/)
         | 
| 147 | 
            +
                memo[name.to_sym] = url
         | 
| 148 | 
            +
                memo
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
            end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            helper :remote_branches_for do |user|
         | 
| 153 | 
            +
              `git ls-remote -h #{user} 2> /dev/null`.split(/\n/).inject({}) do |memo, line|
         | 
| 154 | 
            +
                hash, head = line.split(/\t/, 2)
         | 
| 155 | 
            +
                head = head[%r{refs/heads/(.+)$},1] unless head.nil?
         | 
| 156 | 
            +
                memo[head] = hash unless head.nil?
         | 
| 157 | 
            +
                memo
         | 
| 158 | 
            +
              end if !(user.nil? || user.strip.empty?)
         | 
| 159 | 
            +
            end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            helper :remote_branch? do |user, branch|
         | 
| 162 | 
            +
              remote_branches_for(user).key?(branch)
         | 
| 163 | 
            +
            end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            helper :branch_dirty? do
         | 
| 166 | 
            +
              # see if there are any cached or tracked files that have been modified
         | 
| 167 | 
            +
              # originally, we were going to use git-ls-files but that could only
         | 
| 168 | 
            +
              # report modified track files...not files that have been staged
         | 
| 169 | 
            +
              # for committal
         | 
| 170 | 
            +
              !(system("git diff --quiet 2>/dev/null") or !system("git diff --cached --quiet 2>/dev/null"))
         | 
| 171 | 
            +
            end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            helper :tracking do
         | 
| 174 | 
            +
              remotes.inject({}) do |memo, (name, url)|
         | 
| 175 | 
            +
                if ur = user_and_repo_from(url)
         | 
| 176 | 
            +
                  memo[name] = ur.first
         | 
| 177 | 
            +
                else
         | 
| 178 | 
            +
                  memo[name] = url
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
                memo
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
            end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
            helper :tracking? do |user|
         | 
| 185 | 
            +
              tracking.values.include?(user)
         | 
| 186 | 
            +
            end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            helper :owner do
         | 
| 189 | 
            +
              user_for(:origin)
         | 
| 190 | 
            +
            end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            helper :current_branch do
         | 
| 193 | 
            +
              `git rev-parse --symbolic-full-name HEAD`.chomp.sub(/^refs\/heads\//, '')
         | 
| 194 | 
            +
            end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
            helper :user_and_branch do
         | 
| 197 | 
            +
              raw_branch = current_branch
         | 
| 198 | 
            +
              user, branch = raw_branch.split(/\//, 2)
         | 
| 199 | 
            +
              if branch
         | 
| 200 | 
            +
                [user, branch]
         | 
| 201 | 
            +
              else
         | 
| 202 | 
            +
                [owner, user]
         | 
| 203 | 
            +
              end
         | 
| 204 | 
            +
            end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            helper :branch_user do
         | 
| 207 | 
            +
              user_and_branch.first
         | 
| 208 | 
            +
            end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
            helper :branch_name do
         | 
| 211 | 
            +
              user_and_branch.last
         | 
| 212 | 
            +
            end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
            helper :public_url_for_user_and_repo do |user, repo|
         | 
| 215 | 
            +
              "git://github.com/#{user}/#{repo}.git"
         | 
| 216 | 
            +
            end
         | 
| 217 | 
            +
             | 
| 218 | 
            +
            helper :private_url_for_user_and_repo do |user, repo|
         | 
| 219 | 
            +
              "git@github.com:#{user}/#{repo}.git"
         | 
| 220 | 
            +
            end
         | 
| 221 | 
            +
             | 
| 222 | 
            +
            helper :public_url_for do |user|
         | 
| 223 | 
            +
              public_url_for_user_and_repo user, project
         | 
| 224 | 
            +
            end
         | 
| 225 | 
            +
             | 
| 226 | 
            +
            helper :private_url_for do |user|
         | 
| 227 | 
            +
              private_url_for_user_and_repo user, project
         | 
| 228 | 
            +
            end
         | 
| 229 | 
            +
             | 
| 230 | 
            +
            helper :homepage_for do |user, branch|
         | 
| 231 | 
            +
              "https://github.com/#{user}/#{project}/tree/#{branch}"
         | 
| 232 | 
            +
            end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            helper :network_page_for do |user|
         | 
| 235 | 
            +
              "https://github.com/#{user}/#{project}/network"
         | 
| 236 | 
            +
            end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
            helper :network_meta_for do |user|
         | 
| 239 | 
            +
              "http://github.com/#{user}/#{project}/network_meta"
         | 
| 240 | 
            +
            end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
             | 
| 243 | 
            +
            helper :has_launchy? do |blk|
         | 
| 244 | 
            +
              begin
         | 
| 245 | 
            +
                gem 'launchy'
         | 
| 246 | 
            +
                require 'launchy'
         | 
| 247 | 
            +
                blk.call
         | 
| 248 | 
            +
              rescue Gem::LoadError
         | 
| 249 | 
            +
                STDERR.puts "Sorry, you need to install launchy: `gem install launchy`"
         | 
| 250 | 
            +
              end
         | 
| 251 | 
            +
            end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
            helper :open do |url|
         | 
| 254 | 
            +
              has_launchy? proc {
         | 
| 255 | 
            +
                Launchy::Browser.new.visit url
         | 
| 256 | 
            +
              }
         | 
| 257 | 
            +
            end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
            helper :print_network_help do
         | 
| 260 | 
            +
              puts "
         | 
| 261 | 
            +
            You have to provide a command :
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                web [user]     - opens your web browser to the network graph page for this
         | 
| 264 | 
            +
                                 project, or for the graph page for [user] if provided
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                list           - shows the projects in your network that have commits
         | 
| 267 | 
            +
                                 that you have not pulled in yet, and branch names
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                fetch          - adds all projects in your network as remotes and fetches
         | 
| 270 | 
            +
                                 any objects from them that you don't have yet
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                commits        - will show you a list of all commits in your network that
         | 
| 273 | 
            +
                                 you have not ignored or have not merged or cherry-picked.
         | 
| 274 | 
            +
                                 This will automatically fetch objects you don't have yet.
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                  --project (user/branch)  - only show projects that match string
         | 
| 277 | 
            +
                  --author (email)         - only show projects that match string
         | 
| 278 | 
            +
                  --after (date)           - only show commits after date
         | 
| 279 | 
            +
                  --before (date)          - only show commits before date
         | 
| 280 | 
            +
                  --shas                   - only print shas (can pipe through 'github ignore')
         | 
| 281 | 
            +
                  --applies                - filter to patches that still apply cleanly
         | 
| 282 | 
            +
                  --sort                   - how to sort the commits (date, branch, author)
         | 
| 283 | 
            +
            "
         | 
| 284 | 
            +
            end
         | 
| 285 | 
            +
             | 
| 286 | 
            +
            helper :print_network_cherry_help do
         | 
| 287 | 
            +
              $stderr.puts "
         | 
| 288 | 
            +
            =========================================================================================
         | 
| 289 | 
            +
            These are all the commits that other people have pushed that you have not
         | 
| 290 | 
            +
            applied or ignored yet (see 'github ignore'). Some things you might want to do:
         | 
| 291 | 
            +
             | 
| 292 | 
            +
            * You can run 'github fetch user/branch' (sans '~N') to pull into a local branch for testing
         | 
| 293 | 
            +
            * You can run 'git cherry-pick [SHA]' to apply a single patch
         | 
| 294 | 
            +
            * You can run 'git merge user/branch' to merge a commit and all the '~N' variants.
         | 
| 295 | 
            +
            * You can ignore all of a projects commits with 'github ignore ..user/branch'
         | 
| 296 | 
            +
            =========================================================================================
         | 
| 297 | 
            +
             | 
| 298 | 
            +
            "
         | 
| 299 | 
            +
            end
         | 
| 300 | 
            +
             | 
| 301 | 
            +
            helper :argv do
         | 
| 302 | 
            +
              GitHub.original_args
         | 
| 303 | 
            +
            end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
             | 
| 306 | 
            +
            helper :get_network_data do |user, options|
         | 
| 307 | 
            +
              if options[:cache] && has_cache?
         | 
| 308 | 
            +
                return get_cache
         | 
| 309 | 
            +
              end
         | 
| 310 | 
            +
              if cache_network_data(options)
         | 
| 311 | 
            +
                return cache_data(user)
         | 
| 312 | 
            +
              else
         | 
| 313 | 
            +
                return get_cache
         | 
| 314 | 
            +
              end
         | 
| 315 | 
            +
            end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
            helper :cache_commits do |commits|
         | 
| 318 | 
            +
              File.open( commits_cache_path, 'w' ) do |out|
         | 
| 319 | 
            +
                out.write(commits.to_yaml)
         | 
| 320 | 
            +
              end
         | 
| 321 | 
            +
            end
         | 
| 322 | 
            +
             | 
| 323 | 
            +
            helper :commits_cache do
         | 
| 324 | 
            +
              YAML.load(File.open(commits_cache_path))
         | 
| 325 | 
            +
            end
         | 
| 326 | 
            +
             | 
| 327 | 
            +
            helper :cache_commits_data do |options|
         | 
| 328 | 
            +
              cache_expired? || options[:nocache] || !has_commits_cache?
         | 
| 329 | 
            +
            end
         | 
| 330 | 
            +
             | 
| 331 | 
            +
            helper :cache_network_data do |options|
         | 
| 332 | 
            +
              cache_expired? || options[:nocache] || !has_cache?
         | 
| 333 | 
            +
            end
         | 
| 334 | 
            +
             | 
| 335 | 
            +
            helper :network_cache_path do
         | 
| 336 | 
            +
              dir = `git rev-parse --git-dir`.chomp
         | 
| 337 | 
            +
              File.join(dir, 'network-cache')
         | 
| 338 | 
            +
            end
         | 
| 339 | 
            +
             | 
| 340 | 
            +
            helper :commits_cache_path do
         | 
| 341 | 
            +
              dir = `git rev-parse --git-dir`.chomp
         | 
| 342 | 
            +
              File.join(dir, 'commits-cache')
         | 
| 343 | 
            +
            end
         | 
| 344 | 
            +
             | 
| 345 | 
            +
            helper :cache_data do |user|
         | 
| 346 | 
            +
              raw_data = Kernel.open(network_meta_for(user)).read
         | 
| 347 | 
            +
              File.open( network_cache_path, 'w' ) do |out|
         | 
| 348 | 
            +
                out.write(raw_data)
         | 
| 349 | 
            +
              end
         | 
| 350 | 
            +
              data = JSON.parse(raw_data)
         | 
| 351 | 
            +
            end
         | 
| 352 | 
            +
             | 
| 353 | 
            +
            helper :cache_expired? do
         | 
| 354 | 
            +
              return true if !has_cache?
         | 
| 355 | 
            +
              age = Time.now - File.stat(network_cache_path).mtime
         | 
| 356 | 
            +
              return true if age > (60 * 60) # 1 hour
         | 
| 357 | 
            +
              false
         | 
| 358 | 
            +
            end
         | 
| 359 | 
            +
             | 
| 360 | 
            +
            helper :has_cache? do
         | 
| 361 | 
            +
              File.file?(network_cache_path)
         | 
| 362 | 
            +
            end
         | 
| 363 | 
            +
             | 
| 364 | 
            +
            helper :has_commits_cache? do
         | 
| 365 | 
            +
              File.file?(commits_cache_path)
         | 
| 366 | 
            +
            end
         | 
| 367 | 
            +
             | 
| 368 | 
            +
            helper :get_cache do
         | 
| 369 | 
            +
              JSON.parse(File.read(network_cache_path))
         | 
| 370 | 
            +
            end
         | 
    
        data/github-gem.gemspec
    ADDED
    
    | @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            Gem::Specification.new do |s|
         | 
| 2 | 
            +
              s.name = %q{github}
         | 
| 3 | 
            +
              s.version = "0.3.0"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              s.specification_version = 2 if s.respond_to? :specification_version=
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 8 | 
            +
              s.authors = ["Chris Wanstrath, Kevin Ballard, Scott Chacon"]
         | 
| 9 | 
            +
              s.date = %q{2008-05-18}
         | 
| 10 | 
            +
              s.default_executable = %q{gh}
         | 
| 11 | 
            +
              s.description = %q{The official `github` command line helper for simplifying your GitHub experience.}
         | 
| 12 | 
            +
              s.email = %q{chris@ozmm.org}
         | 
| 13 | 
            +
              s.executables = ["github", "gh"]
         | 
| 14 | 
            +
              s.extra_rdoc_files = ["bin/github", "bin/gh", "lib/github/extensions.rb", "lib/github/command.rb", "lib/github/helper.rb", "lib/github.rb", "LICENSE", "README"]
         | 
| 15 | 
            +
              s.files = ["bin/github", "commands/commands.rb", "commands/helpers.rb", "lib/github/extensions.rb", "lib/github/command.rb", "lib/github/helper.rb", "lib/github.rb", "LICENSE", "Manifest", "README", "spec/command_spec.rb", "spec/extensions_spec.rb", "spec/github_spec.rb", "spec/helper_spec.rb", "spec/spec_helper.rb", "spec/ui_spec.rb", "spec/windoze_spec.rb", "github-gem.gemspec"]
         | 
| 16 | 
            +
              s.has_rdoc = true
         | 
| 17 | 
            +
              s.homepage = %q{http://github.com/}
         | 
| 18 | 
            +
              s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Github", "--main", "README"]
         | 
| 19 | 
            +
              s.require_paths = ["lib"]
         | 
| 20 | 
            +
              s.rubyforge_project = %q{github}
         | 
| 21 | 
            +
              s.rubygems_version = %q{1.1.1}
         | 
| 22 | 
            +
              s.summary = %q{The official `github` command line helper for simplifying your GitHub experience.}
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              # s.add_dependency(%q<launchy>, [">= 0"])
         | 
| 25 | 
            +
              s.add_dependency('json', [">= 0"])
         | 
| 26 | 
            +
            end
         | 
    
        data/lib/github.rb
    ADDED
    
    | @@ -0,0 +1,146 @@ | |
| 1 | 
            +
            $:.unshift File.dirname(__FILE__)
         | 
| 2 | 
            +
            require 'github/extensions'
         | 
| 3 | 
            +
            require 'github/command'
         | 
| 4 | 
            +
            require 'github/helper'
         | 
| 5 | 
            +
            require 'rubygems'
         | 
| 6 | 
            +
            require 'open-uri'
         | 
| 7 | 
            +
            require 'json'
         | 
| 8 | 
            +
            require 'yaml'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            ##
         | 
| 11 | 
            +
            # Starting simple.
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            # $ github <command> <args>
         | 
| 14 | 
            +
            #
         | 
| 15 | 
            +
            #   GitHub.command <command> do |*args|
         | 
| 16 | 
            +
            #     whatever
         | 
| 17 | 
            +
            #   end
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            # We'll probably want to use the `choice` gem for concise, tasty DSL
         | 
| 20 | 
            +
            # arg parsing action.
         | 
| 21 | 
            +
            #
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module GitHub
         | 
| 24 | 
            +
              extend self
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              BasePath = File.expand_path(File.dirname(__FILE__) + '/..')
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def command(command, &block)
         | 
| 29 | 
            +
                debug "Registered `#{command}`"
         | 
| 30 | 
            +
                descriptions[command] = @next_description if @next_description
         | 
| 31 | 
            +
                @next_description = nil
         | 
| 32 | 
            +
                flag_descriptions[command].update @next_flags if @next_flags
         | 
| 33 | 
            +
                @next_flags = nil
         | 
| 34 | 
            +
                commands[command.to_s] = Command.new(block)
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def desc(str)
         | 
| 38 | 
            +
                @next_description = str
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def flags(hash)
         | 
| 42 | 
            +
                @next_flags ||= {}
         | 
| 43 | 
            +
                @next_flags.update hash
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def helper(command, &block)
         | 
| 47 | 
            +
                debug "Helper'd `#{command}`"
         | 
| 48 | 
            +
                Helper.send :define_method, command, &block
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def activate(args)
         | 
| 52 | 
            +
                @@original_args = args.clone
         | 
| 53 | 
            +
                @options = parse_options(args)
         | 
| 54 | 
            +
                @debug = @options[:debug]
         | 
| 55 | 
            +
                load 'helpers.rb'
         | 
| 56 | 
            +
                load 'commands.rb'
         | 
| 57 | 
            +
                invoke(args.shift, *args)
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def invoke(command, *args)
         | 
| 61 | 
            +
                block = find_command(command)
         | 
| 62 | 
            +
                debug "Invoking `#{command}`"
         | 
| 63 | 
            +
                block.call(*args)
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              def find_command(name)
         | 
| 67 | 
            +
                name = name.to_s
         | 
| 68 | 
            +
                commands[name] || GitCommand.new(name) || commands['default']
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def commands
         | 
| 72 | 
            +
                @commands ||= {}
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def descriptions
         | 
| 76 | 
            +
                @descriptions ||= {}
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def flag_descriptions
         | 
| 80 | 
            +
                @flagdescs ||= Hash.new { |h, k| h[k] = {} }
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              def options
         | 
| 84 | 
            +
                @options
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              def original_args
         | 
| 88 | 
            +
                @@original_args ||= []
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def parse_options(args)
         | 
| 92 | 
            +
                idx = 0
         | 
| 93 | 
            +
                args.clone.inject({}) do |memo, arg|
         | 
| 94 | 
            +
                  case arg
         | 
| 95 | 
            +
                  when /^--(.+?)=(.*)/
         | 
| 96 | 
            +
                    args.delete_at(idx)
         | 
| 97 | 
            +
                    memo.merge($1.to_sym => $2)
         | 
| 98 | 
            +
                  when /^--(.+)/
         | 
| 99 | 
            +
                    args.delete_at(idx)
         | 
| 100 | 
            +
                    memo.merge($1.to_sym => true)
         | 
| 101 | 
            +
                  when "--"
         | 
| 102 | 
            +
                    args.delete_at(idx)
         | 
| 103 | 
            +
                    return memo
         | 
| 104 | 
            +
                  else
         | 
| 105 | 
            +
                    idx += 1
         | 
| 106 | 
            +
                    memo
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              def load(file)
         | 
| 112 | 
            +
                file[0] == ?/ ? path = file : path = BasePath + "/commands/#{file}"
         | 
| 113 | 
            +
                data = File.read(path)
         | 
| 114 | 
            +
                GitHub.module_eval data, path
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              def debug(*messages)
         | 
| 118 | 
            +
                puts *messages.map { |m| "== #{m}" } if debug?
         | 
| 119 | 
            +
              end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
              def debug?
         | 
| 122 | 
            +
                !!@debug
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
            end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            GitHub.command :default do
         | 
| 127 | 
            +
              puts "Usage: github command <space separated arguments>", ''
         | 
| 128 | 
            +
              puts "Available commands:", ''
         | 
| 129 | 
            +
              longest = GitHub.descriptions.map { |d,| d.to_s.size }.max
         | 
| 130 | 
            +
              GitHub.descriptions.each do |command, desc|
         | 
| 131 | 
            +
                cmdstr = "%-#{longest}s" % command
         | 
| 132 | 
            +
                puts "  #{cmdstr} => #{desc}"
         | 
| 133 | 
            +
                flongest = GitHub.flag_descriptions[command].map { |d,| "--#{d}".size }.max
         | 
| 134 | 
            +
                GitHub.flag_descriptions[command].each do |flag, fdesc|
         | 
| 135 | 
            +
                  flagstr = "#{" " * longest}  %-#{flongest}s" % "--#{flag}"
         | 
| 136 | 
            +
                  puts "  #{flagstr}: #{fdesc}"
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
              puts
         | 
| 140 | 
            +
            end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            GitHub.commands[''] = GitHub.commands['default']
         | 
| 143 | 
            +
            GitHub.commands['-h'] = GitHub.commands['default']
         | 
| 144 | 
            +
            GitHub.commands['--help'] = GitHub.commands['default']
         | 
| 145 | 
            +
            GitHub.commands['-help'] = GitHub.commands['default']
         | 
| 146 | 
            +
            GitHub.commands['help'] = GitHub.commands['default']
         |