glom 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +54 -0
- data/Rakefile +1 -0
- data/bin/glom +14 -0
- data/glom.gemspec +23 -0
- data/lib/.DS_Store +0 -0
- data/lib/glom/.DS_Store +0 -0
- data/lib/glom/registries/bower.rb +25 -0
- data/lib/glom/registries/npm.rb +23 -0
- data/lib/glom/registries/rubygems.rb +10 -0
- data/lib/glom/terminal-table/cell.rb +98 -0
- data/lib/glom/terminal-table/core_ext.rb +8 -0
- data/lib/glom/terminal-table/row.rb +53 -0
- data/lib/glom/terminal-table/separator.rb +14 -0
- data/lib/glom/terminal-table/style.rb +65 -0
- data/lib/glom/terminal-table/table.rb +261 -0
- data/lib/glom/terminal-table/table_helper.rb +9 -0
- data/lib/glom/terminal-table/version.rb +5 -0
- data/lib/glom/version.rb +3 -0
- data/lib/glom.rb +81 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bdb09d9de0759354a469a53fcf222c4271de1e61
|
4
|
+
data.tar.gz: f101596b029c3206b0409cd9c7e8e1aab2d9b28c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 326577e121b524aafef48e345ab9f39653155f7190ed1a7633820de3bb593097f0e17cfc6a436b1f1fde4f4ed55b40d034e1a99d3333f823afaa2fb8626f0cc8
|
7
|
+
data.tar.gz: c8cd1a509aa0eabd875f655e6ca14c1ada7a98886d51167da46fd9c6c6a47c0c5927d8214fe42c69efba9f078312b1af1a21f20ef4198a2fc00bc380e5244475
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jackson Gariety
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Glom
|
2
|
+
|
3
|
+
#### Problem:
|
4
|
+
|
5
|
+
_To find a package for templating my new js project, I must open github.com and sift through people's crappy repos._
|
6
|
+
|
7
|
+
#### Solution:
|
8
|
+
|
9
|
+
$ glom 'javascript templating'
|
10
|
+
|
11
|
+
Searching `https://bower-component-list.herokuapp.com` for `templating`...
|
12
|
+
|
13
|
+
Searching `http://jiyinyiyong.github.io/nipster/packages.json` for `templating`...
|
14
|
+
|
15
|
+
+-----------------+-------------------------+---------------+-------+----------------+----------+
|
16
|
+
| Name | Description | Author | Stars | Last Updated | Registry |
|
17
|
+
+-----------------+-------------------------+---------------+-------+----------------+----------+
|
18
|
+
| mustache | Minimal templating | janl | 5457 | 3 months | bower |
|
19
|
+
| | with {{mustaches}} | | | | |
|
20
|
+
| | in JavaScript | | | | |
|
21
|
+
| hogan | A compiler for the | twitter | 2912 | 25 days | bower |
|
22
|
+
| | Mustache templating | | | | |
|
23
|
+
| | language | | | | |
|
24
|
+
+-----------------+-------------------------+---------------+-------+----------------+----------+
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
$ gem install glom
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
Support for APIs can be added easily by adding `[ApiName].rb` to the `lib/glom/registries` directory.
|
33
|
+
|
34
|
+
`[ApiName].rb` should be a module with a `KEYWORDS` array and a `get()` method.
|
35
|
+
|
36
|
+
Example:
|
37
|
+
|
38
|
+
# npm.rb
|
39
|
+
|
40
|
+
module Npm
|
41
|
+
KEYWORDS = ['npm', 'node', 'nodejs', 'js', 'javascript']
|
42
|
+
|
43
|
+
def get
|
44
|
+
# return array of packages here plz
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#### Git for ~~losers~~ dummies
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/glom
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'glom'
|
4
|
+
|
5
|
+
include Glom
|
6
|
+
|
7
|
+
begin
|
8
|
+
detect ARGV[0].clone # Use environment-specific keywords from @string to get an array of @registries to search
|
9
|
+
search # Search for @string in each of the @registries and join the results
|
10
|
+
sort # Sort the JSON by (stars), and maybe ((stars * followers of top contributor) / issues) in the future
|
11
|
+
display # Print [title], [description], [top contributor], [stars], and [install command] into an ASCII table in terminal
|
12
|
+
rescue Gem::SystemExitException => e
|
13
|
+
exit e.exit_code
|
14
|
+
end
|
data/glom.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'glom/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "glom"
|
8
|
+
spec.version = Glom::VERSION
|
9
|
+
spec.authors = ["Jackson Gariety"]
|
10
|
+
spec.email = ["personal@jacksongariety.com"]
|
11
|
+
spec.description = "Intelligent package search, without leaving your shell."
|
12
|
+
spec.summary = "Intelligent package search, without leaving your shell."
|
13
|
+
spec.homepage = "https://github.com/JacksonGariety/glom"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/.DS_Store
ADDED
Binary file
|
data/lib/glom/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'action_view'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
include ActionView::Helpers::DateHelper
|
8
|
+
|
9
|
+
module Glom::Bower
|
10
|
+
KEYWORDS = ['bower', 'front-end', 'frontend', 'js', 'javascript']
|
11
|
+
NAME = 'bower'
|
12
|
+
URL = 'https://bower-component-list.herokuapp.com'
|
13
|
+
|
14
|
+
def standardize(query)
|
15
|
+
json = Glom.get(URL)
|
16
|
+
|
17
|
+
packages = JSON.parse(json).select do |package|
|
18
|
+
package['description'].include? query if package['description'].is_a? String
|
19
|
+
end
|
20
|
+
|
21
|
+
packages.map do |package|
|
22
|
+
[package['name'], package['description'], package['owner'], package['stars'], time_ago_in_words(Time.parse(package['updated'])), NAME]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'action_view'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Glom::Npm
|
8
|
+
KEYWORDS = ['npm', 'node', 'nodejs', 'js', 'javascript']
|
9
|
+
NAME = 'npm'
|
10
|
+
URL = 'http://jiyinyiyong.github.io/nipster/packages.json'
|
11
|
+
|
12
|
+
def standardize(query)
|
13
|
+
json = Glom.get(URL)
|
14
|
+
|
15
|
+
packages = JSON.parse(json)['packages'].select do |package|
|
16
|
+
package[1].downcase.include? query.downcase if package[1].is_a? String and package[3].is_a? String and !package[5].nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
packages.map do |package|
|
20
|
+
[package[0], package[1], package[2], package[5], time_ago_in_words(Time.parse(package[3])), NAME]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
module Terminal
|
3
|
+
class Table
|
4
|
+
class Cell
|
5
|
+
|
6
|
+
##
|
7
|
+
# Cell width.
|
8
|
+
|
9
|
+
attr_reader :width
|
10
|
+
|
11
|
+
##
|
12
|
+
# Cell value.
|
13
|
+
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
##
|
17
|
+
# Column span.
|
18
|
+
|
19
|
+
attr_reader :colspan
|
20
|
+
|
21
|
+
##
|
22
|
+
# Initialize with _options_.
|
23
|
+
|
24
|
+
def initialize options = nil
|
25
|
+
@value, options = options, {} unless Hash === options
|
26
|
+
@value = options.fetch :value, value
|
27
|
+
@alignment = options.fetch :alignment, nil
|
28
|
+
@colspan = options.fetch :colspan, 1
|
29
|
+
@width = options.fetch :width, @value.to_s.size
|
30
|
+
@index = options.fetch :index
|
31
|
+
@table = options.fetch :table
|
32
|
+
end
|
33
|
+
|
34
|
+
def alignment?
|
35
|
+
!@alignment.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def alignment
|
39
|
+
@alignment || @table.style.alignment || :left
|
40
|
+
end
|
41
|
+
|
42
|
+
def alignment=(val)
|
43
|
+
supported = %w(left center right)
|
44
|
+
if supported.include?(val.to_s)
|
45
|
+
@alignment = val
|
46
|
+
else
|
47
|
+
raise "Aligment must be one of: #{supported.join(' ')}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def lines
|
52
|
+
@value.to_s.split(/\n/)
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Render the cell.
|
57
|
+
|
58
|
+
def render(line = 0)
|
59
|
+
left = " " * @table.style.padding_left
|
60
|
+
right = " " * @table.style.padding_right
|
61
|
+
render_width = lines[line].to_s.size - escape(lines[line]).size + width
|
62
|
+
"#{left}#{lines[line]}#{right}".align(alignment, render_width + @table.cell_padding)
|
63
|
+
end
|
64
|
+
alias :to_s :render
|
65
|
+
|
66
|
+
##
|
67
|
+
# Returns the longest line in the cell and
|
68
|
+
# removes all ANSI escape sequences (e.g. color)
|
69
|
+
|
70
|
+
def value_for_column_width_recalc
|
71
|
+
lines.map{ |s| escape(s) }.max_by{ |s| s.size }
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Returns the width of this cell
|
76
|
+
|
77
|
+
def width
|
78
|
+
padding = (colspan - 1) * @table.cell_spacing
|
79
|
+
inner_width = (1..@colspan).to_a.inject(0) do |w, counter|
|
80
|
+
w + @table.column_width(@index + counter - 1)
|
81
|
+
end
|
82
|
+
inner_width + padding
|
83
|
+
end
|
84
|
+
|
85
|
+
def wrap(width)
|
86
|
+
@value.gsub!(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# removes all ANSI escape sequences (e.g. color)
|
91
|
+
def escape(line)
|
92
|
+
line.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
93
|
+
gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
94
|
+
gsub(/(\x03|\x1a)/, '')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Terminal
|
2
|
+
class Table
|
3
|
+
class Row
|
4
|
+
|
5
|
+
##
|
6
|
+
# Row cells
|
7
|
+
|
8
|
+
attr_reader :cells
|
9
|
+
|
10
|
+
attr_reader :table
|
11
|
+
|
12
|
+
##
|
13
|
+
# Initialize with _width_ and _options_.
|
14
|
+
|
15
|
+
def initialize table, array = []
|
16
|
+
@cell_index = 0
|
17
|
+
@table = table
|
18
|
+
@cells = []
|
19
|
+
array.each { |item| self << item }
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_cell item
|
23
|
+
options = item.is_a?(Hash) ? item : {:value => item}
|
24
|
+
cell = Cell.new(options.merge(:index => @cell_index, :table => @table))
|
25
|
+
@cell_index += cell.colspan
|
26
|
+
@cells << cell
|
27
|
+
end
|
28
|
+
alias << add_cell
|
29
|
+
|
30
|
+
def [] index
|
31
|
+
cells[index]
|
32
|
+
end
|
33
|
+
|
34
|
+
def height
|
35
|
+
cells.map { |c| c.lines.count }.max
|
36
|
+
end
|
37
|
+
|
38
|
+
def wrap_cell(index, width)
|
39
|
+
return if @cells.nil? or @cells[index].nil?
|
40
|
+
@cells[index].wrap(width)
|
41
|
+
end
|
42
|
+
|
43
|
+
def render
|
44
|
+
y = @table.style.border_y
|
45
|
+
(0...height).to_a.map do |line|
|
46
|
+
y + cells.map do |cell|
|
47
|
+
cell.render(line)
|
48
|
+
end.join(y) + y
|
49
|
+
end.join("\n")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Terminal
|
2
|
+
class Table
|
3
|
+
class Separator < Row
|
4
|
+
|
5
|
+
def render
|
6
|
+
arr_x = (0...@table.number_of_columns).to_a.map do |i|
|
7
|
+
@table.style.border_x * (@table.column_width(i) + @table.cell_padding)
|
8
|
+
end
|
9
|
+
border_i = @table.style.border_i
|
10
|
+
border_i + arr_x.join(border_i) + border_i
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
module Terminal
|
3
|
+
class Table
|
4
|
+
# A Style object holds all the formatting information for a Table object
|
5
|
+
#
|
6
|
+
# To create a table with a certain style, use either the constructor
|
7
|
+
# option <tt>:style</tt>, the Table#style object or the Table#style= method
|
8
|
+
#
|
9
|
+
# All these examples have the same effect:
|
10
|
+
#
|
11
|
+
# # by constructor
|
12
|
+
# @table = Table.new(:style => {:padding_left => 2, :width => 40})
|
13
|
+
#
|
14
|
+
# # by object
|
15
|
+
# @table.style.padding_left = 2
|
16
|
+
# @table.style.width = 40
|
17
|
+
#
|
18
|
+
# # by method
|
19
|
+
# @table.style = {:padding_left => 2, :width => 40}
|
20
|
+
#
|
21
|
+
# To set a default style for all tables created afterwards use Style.defaults=
|
22
|
+
#
|
23
|
+
# Terminal::Table::Style.defaults = {:width => 80}
|
24
|
+
#
|
25
|
+
class Style
|
26
|
+
@@defaults = {
|
27
|
+
:border_x => "-", :border_y => "|", :border_i => "+",
|
28
|
+
:padding_left => 1, :padding_right => 1,
|
29
|
+
:width => nil, :alignment => nil,
|
30
|
+
:wrap => true, :wrap_minimum_width => 4
|
31
|
+
}
|
32
|
+
|
33
|
+
attr_accessor :border_x
|
34
|
+
attr_accessor :border_y
|
35
|
+
attr_accessor :border_i
|
36
|
+
|
37
|
+
attr_accessor :padding_left
|
38
|
+
attr_accessor :padding_right
|
39
|
+
|
40
|
+
attr_accessor :width
|
41
|
+
attr_accessor :alignment
|
42
|
+
attr_accessor :wrap
|
43
|
+
attr_accessor :wrap_minimum_width
|
44
|
+
|
45
|
+
|
46
|
+
def initialize options = {}
|
47
|
+
apply self.class.defaults.merge(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def apply options
|
51
|
+
options.each { |m, v| __send__ "#{m}=", v }
|
52
|
+
end
|
53
|
+
|
54
|
+
class << self
|
55
|
+
def defaults
|
56
|
+
@@defaults
|
57
|
+
end
|
58
|
+
|
59
|
+
def defaults= options
|
60
|
+
@@defaults = defaults.merge(options)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
|
2
|
+
module Terminal
|
3
|
+
class Table
|
4
|
+
|
5
|
+
attr_reader :title
|
6
|
+
attr_reader :headings
|
7
|
+
|
8
|
+
##
|
9
|
+
# Generates a ASCII table with the given _options_.
|
10
|
+
|
11
|
+
def initialize options = {}, &block
|
12
|
+
@column_widths = []
|
13
|
+
self.style = options.fetch :style, {}
|
14
|
+
self.headings = options.fetch :headings, []
|
15
|
+
self.rows = options.fetch :rows, []
|
16
|
+
self.title = options.fetch :title, nil
|
17
|
+
yield_or_eval(&block) if block
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Align column _n_ to the given _alignment_ of :center, :left, or :right.
|
22
|
+
|
23
|
+
def align_column n, alignment
|
24
|
+
r = rows
|
25
|
+
column(n).each_with_index do |col, i|
|
26
|
+
cell = r[i][n]
|
27
|
+
cell.alignment = alignment unless cell.alignment?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Add a row.
|
33
|
+
|
34
|
+
def add_row array
|
35
|
+
row = array == :separator ? Separator.new(self) : Row.new(self, array)
|
36
|
+
@rows << row
|
37
|
+
recalc_column_widths row
|
38
|
+
end
|
39
|
+
alias :<< :add_row
|
40
|
+
|
41
|
+
##
|
42
|
+
# Add a separator.
|
43
|
+
|
44
|
+
def add_separator
|
45
|
+
self << :separator
|
46
|
+
end
|
47
|
+
|
48
|
+
def cell_spacing
|
49
|
+
cell_padding + style.border_y.length
|
50
|
+
end
|
51
|
+
|
52
|
+
def cell_padding
|
53
|
+
style.padding_left + style.padding_right
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Return column _n_.
|
58
|
+
|
59
|
+
def column n, method = :value, array = rows
|
60
|
+
array.map { |row|
|
61
|
+
cell = row[n]
|
62
|
+
cell && method ? cell.__send__(method) : cell
|
63
|
+
}.compact
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Return _n_ column including headings.
|
68
|
+
|
69
|
+
def column_with_headings n, method = :value
|
70
|
+
column n, method, headings_with_rows
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Return columns.
|
75
|
+
|
76
|
+
def columns
|
77
|
+
(0...number_of_columns).map { |n| column n }
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Return length of column _n_.
|
82
|
+
|
83
|
+
def column_width n
|
84
|
+
width = @column_widths[n] || 0
|
85
|
+
width + additional_column_widths[n].to_i
|
86
|
+
end
|
87
|
+
alias length_of_column column_width # for legacy support
|
88
|
+
|
89
|
+
##
|
90
|
+
# Return total number of columns available.
|
91
|
+
|
92
|
+
def number_of_columns
|
93
|
+
headings_with_rows.map { |r| r.cells.size }.max
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Set the headings
|
98
|
+
|
99
|
+
def headings= array
|
100
|
+
@headings = Row.new(self, array)
|
101
|
+
recalc_column_widths @headings
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Render the table.
|
106
|
+
|
107
|
+
def render
|
108
|
+
adjust_widths
|
109
|
+
separator = Separator.new(self)
|
110
|
+
buffer = [separator]
|
111
|
+
unless @title.nil?
|
112
|
+
buffer << Row.new(self, [title_cell_options])
|
113
|
+
buffer << separator
|
114
|
+
end
|
115
|
+
unless @headings.cells.empty?
|
116
|
+
buffer << @headings
|
117
|
+
buffer << separator
|
118
|
+
end
|
119
|
+
buffer += @rows
|
120
|
+
buffer << separator
|
121
|
+
buffer.map { |r| r.render }.join("\n")
|
122
|
+
end
|
123
|
+
alias :to_s :render
|
124
|
+
|
125
|
+
##
|
126
|
+
# Return rows without separator rows.
|
127
|
+
|
128
|
+
def rows
|
129
|
+
@rows.reject { |row| row.is_a? Separator }
|
130
|
+
end
|
131
|
+
|
132
|
+
def rows= array
|
133
|
+
@rows = []
|
134
|
+
array.each { |arr| self << arr }
|
135
|
+
end
|
136
|
+
|
137
|
+
def style=(options)
|
138
|
+
style.apply options
|
139
|
+
end
|
140
|
+
|
141
|
+
def style
|
142
|
+
@style ||= Style.new
|
143
|
+
end
|
144
|
+
|
145
|
+
def title=(title)
|
146
|
+
@title = title
|
147
|
+
recalc_column_widths Row.new(self, [title_cell_options])
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Check if _other_ is equal to self. _other_ is considered equal
|
152
|
+
# if it contains the same headings and rows.
|
153
|
+
|
154
|
+
def == other
|
155
|
+
if other.respond_to? :render and other.respond_to? :rows
|
156
|
+
self.headings == other.headings and self.rows == other.rows
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def columns_width
|
163
|
+
@column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y.length
|
164
|
+
end
|
165
|
+
|
166
|
+
def wrap_column(index, new_width)
|
167
|
+
@rows.each do |row|
|
168
|
+
row.wrap_cell(index, new_width)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def adjust_widths
|
173
|
+
return if style.wrap == false or style.width.nil? or style.width > columns_width
|
174
|
+
|
175
|
+
# Make a column index to column size mapping, then sort it
|
176
|
+
current_index = -1
|
177
|
+
total_column_widths = @column_widths.map { |s| current_index +=1; [current_index, s + cell_spacing] }
|
178
|
+
total_column_widths.sort_by! { |a,b| b }
|
179
|
+
|
180
|
+
packed_length = 0
|
181
|
+
current_index = 0
|
182
|
+
# Pack the smallest first, but make sure the remaining space is enough for
|
183
|
+
# the rest of the columns to have at least style.wrap_minimum_width spaces
|
184
|
+
# to wrap into.
|
185
|
+
while (style.width - (packed_length + total_column_widths[current_index][1] + style.border_y.length)) >
|
186
|
+
(style.wrap_minimum_width * (total_column_widths.size - current_index - 1)) do
|
187
|
+
packed_length += total_column_widths[current_index][1]
|
188
|
+
current_index += 1
|
189
|
+
end
|
190
|
+
|
191
|
+
# Calculate the remaining space and figure out how big to wrap the other columns to
|
192
|
+
remaining_space = style.width - packed_length - style.border_y.length
|
193
|
+
trim_to = (remaining_space / (total_column_widths.size - current_index)) - cell_spacing
|
194
|
+
trim_to -= (1 + style.padding_left + style.padding_right)
|
195
|
+
if trim_to < 1
|
196
|
+
raise "Cannot fit a #{total_column_widths.size} column table in width #{style.width}."
|
197
|
+
end
|
198
|
+
|
199
|
+
# The remaining columns are then wrapped
|
200
|
+
(current_index...total_column_widths.size).each do |i|
|
201
|
+
wrap_column(total_column_widths[i][0], trim_to)
|
202
|
+
@column_widths[total_column_widths[i][0]] = trim_to + cell_spacing
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def additional_column_widths
|
207
|
+
return [] if style.width.nil?
|
208
|
+
spacing = style.width - columns_width
|
209
|
+
if spacing < 0
|
210
|
+
raise "Table width exceeds wanted width of #{style.width} characters."
|
211
|
+
else
|
212
|
+
per_col = spacing / number_of_columns
|
213
|
+
arr = (1...number_of_columns).to_a.map { |i| per_col }
|
214
|
+
other_cols = arr.inject(0) { |s, i| s + i }
|
215
|
+
arr << spacing - other_cols
|
216
|
+
arr
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def recalc_column_widths row
|
221
|
+
return if row.is_a? Separator
|
222
|
+
i = 0
|
223
|
+
row.cells.each do |cell|
|
224
|
+
colspan = cell.colspan
|
225
|
+
cell_value = cell.value_for_column_width_recalc
|
226
|
+
colspan.downto(1) do |j|
|
227
|
+
cell_length = cell_value.to_s.length
|
228
|
+
if colspan > 1
|
229
|
+
spacing_length = cell_spacing * (colspan - 1)
|
230
|
+
length_in_columns = (cell_length - spacing_length)
|
231
|
+
cell_length = (length_in_columns.to_f / colspan).ceil
|
232
|
+
end
|
233
|
+
if @column_widths[i].to_i < cell_length
|
234
|
+
@column_widths[i] = cell_length
|
235
|
+
end
|
236
|
+
i = i + 1
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Return headings combined with rows.
|
243
|
+
|
244
|
+
def headings_with_rows
|
245
|
+
[@headings] + rows
|
246
|
+
end
|
247
|
+
|
248
|
+
def yield_or_eval &block
|
249
|
+
return unless block
|
250
|
+
if block.arity > 0
|
251
|
+
yield self
|
252
|
+
else
|
253
|
+
self.instance_eval(&block)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def title_cell_options
|
258
|
+
{:value => @title, :alignment => :center, :colspan => number_of_columns}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
data/lib/glom/version.rb
ADDED
data/lib/glom.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'glom/version'
|
2
|
+
require 'json'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
# Require individual registry logic
|
6
|
+
Dir["#{File.dirname __FILE__}/glom/registries/*.rb"].each do |file|
|
7
|
+
require file
|
8
|
+
end
|
9
|
+
|
10
|
+
module Glom
|
11
|
+
self.constants.each do |constant|
|
12
|
+
constant = self.const_get constant
|
13
|
+
(REGISTRIES ||= []) << constant if constant.is_a? Module
|
14
|
+
end
|
15
|
+
|
16
|
+
def detect(query)
|
17
|
+
@query = query.dup
|
18
|
+
|
19
|
+
REGISTRIES.each do |registry|
|
20
|
+
registry::KEYWORDS.each do |keyword|
|
21
|
+
if query.include? keyword
|
22
|
+
(@registries ||= []) << registry
|
23
|
+
@query.slice! keyword
|
24
|
+
@query.strip!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@registries = REGISTRIES unless defined? @registries
|
30
|
+
end
|
31
|
+
|
32
|
+
def search
|
33
|
+
@registries.each do |registry|
|
34
|
+
include registry
|
35
|
+
|
36
|
+
puts "\nSearching `#{registry::URL}` for `#{@query}`...\n"
|
37
|
+
|
38
|
+
(@packages ||= []).concat registry.standardize(@query)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sort
|
43
|
+
@packages.sort_by! do |package|
|
44
|
+
-package[3]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def display
|
49
|
+
# Require terminal-table
|
50
|
+
Dir["#{File.dirname __FILE__}/glom/terminal-table/*.rb"].each do |file|
|
51
|
+
require file
|
52
|
+
end
|
53
|
+
|
54
|
+
table = Terminal::Table.new
|
55
|
+
table.headings = ['Name', 'Description', 'Author', 'Stars', 'Last Updated', 'Registry']
|
56
|
+
table.rows = @packages[0..20]
|
57
|
+
table.style = {
|
58
|
+
:width => `/usr/bin/env tput cols`.to_i
|
59
|
+
}
|
60
|
+
|
61
|
+
puts ""
|
62
|
+
puts table
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(address)
|
66
|
+
cache = "#{Dir.tmpdir}/#{address.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')}.json"
|
67
|
+
|
68
|
+
if File.exist? cache
|
69
|
+
json = IO.read(cache)
|
70
|
+
else
|
71
|
+
uri = URI(address)
|
72
|
+
json = Net::HTTP.get(uri)
|
73
|
+
|
74
|
+
output = File.new(cache, 'w')
|
75
|
+
output.puts json
|
76
|
+
output.close
|
77
|
+
end
|
78
|
+
|
79
|
+
return json
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: glom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jackson Gariety
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Intelligent package search, without leaving your shell.
|
42
|
+
email:
|
43
|
+
- personal@jacksongariety.com
|
44
|
+
executables:
|
45
|
+
- glom
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/glom
|
55
|
+
- glom.gemspec
|
56
|
+
- lib/.DS_Store
|
57
|
+
- lib/glom.rb
|
58
|
+
- lib/glom/.DS_Store
|
59
|
+
- lib/glom/registries/bower.rb
|
60
|
+
- lib/glom/registries/npm.rb
|
61
|
+
- lib/glom/registries/rubygems.rb
|
62
|
+
- lib/glom/terminal-table/cell.rb
|
63
|
+
- lib/glom/terminal-table/core_ext.rb
|
64
|
+
- lib/glom/terminal-table/row.rb
|
65
|
+
- lib/glom/terminal-table/separator.rb
|
66
|
+
- lib/glom/terminal-table/style.rb
|
67
|
+
- lib/glom/terminal-table/table.rb
|
68
|
+
- lib/glom/terminal-table/table_helper.rb
|
69
|
+
- lib/glom/terminal-table/version.rb
|
70
|
+
- lib/glom/version.rb
|
71
|
+
homepage: https://github.com/JacksonGariety/glom
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.0.5
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Intelligent package search, without leaving your shell.
|
95
|
+
test_files: []
|