fingerer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7b2b3a0868aa7f6d1ced9f93f2d381d9bd9ba90c
4
+ data.tar.gz: b407e16d2083f0dbc534ad6e27c4a380d773588a
5
+ SHA512:
6
+ metadata.gz: ff3c0c850611150cebcde23a8ada4fa14b9428c5607fb80d72279773938b0c806b466d314a7935accd3038c174761b8dd18b3cae78285d74ae3194f91d35be8a
7
+ data.tar.gz: e96786fc74de2191b224f600338a8b0be7bf288067b87468470f47a63dc6a5894b083cfa5bb8b9c8b931a7094b4a52b6d1fa8e23d2961e4d2b989da5b16be4cd
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *.pid
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4.0
4
+ - 2.3.0
5
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fingerer.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ end
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Zachary Flower
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,85 @@
1
+ # Fingerer
2
+
3
+ [![Code Climate](https://codeclimate.com/github/zachflower/fingerer/badges/gpa.svg)](https://codeclimate.com/github/zachflower/fingerer) [![Build Status](https://travis-ci.org/zachflower/fingerer.svg?branch=master)](https://travis-ci.org/zachflower/fingerer) [![Beerpay](https://beerpay.io/zachflower/fingerer/badge.svg?style=flat)](https://beerpay.io/zachflower/fingerer)
4
+
5
+ Fingerer is a [finger](https://en.wikipedia.org/wiki/Finger_protocol) server that returns the GitHub profile associated with the provided username. Quickly lookup GitHub user and organization information using a standard `finger` command.
6
+
7
+ **Syntax**
8
+
9
+ ```
10
+ finger zachflower@fingerer.zacharyflower.com
11
+ ```
12
+
13
+ **Response**
14
+
15
+ ```
16
+ +-[ User ]-------------------------------------------------------------------+
17
+ | |
18
+ | Username: zachflower Full Name: Zachary Flower |
19
+ | Website: http://zacharyflower.com Email: zach@zacharyflower.com |
20
+ | Location: Boulder, CO |
21
+ | |
22
+ +-[ Stats ]------------------------------------------------------------------+
23
+ | |
24
+ | Followers: 26 Following: 11 Repositories: 35 |
25
+ | |
26
+ +-[ Biography ]--------------------------------------------------------------+
27
+ | |
28
+ | Professional nerd, avid indoorsman, technical writer, contributor |
29
+ | @fixateio, lead developer @emersonstone |
30
+ | |
31
+ +----------------------------------------------------------------------------+
32
+
33
+ Member since Sat Jul 16 19:20:34 2011
34
+ ```
35
+
36
+ ## Background
37
+
38
+ Via Wikipedia:
39
+
40
+ > The Name/Finger protocol, written by David Zimmerman, is based on Request for Comments document [RFC 742](https://tools.ietf.org/html/rfc742) (December 1977) as an interface to the **name** and **finger** programs that provide status reports on a particular computer system or a particular person at network sites. The finger program was written in 1971 by [Les Earnest](https://en.wikipedia.org/wiki/Les_Earnest) who created the program to solve the need of users who wanted information on other users of the network. Information on who is logged-in was useful to check the availability of a person to meet. This was probably the earliest form of [presence information](https://en.wikipedia.org/wiki/Presence_information) for remote network users.
41
+ >
42
+ > Prior to the finger program, the only way to get this information was with a [**who**](https://en.wikipedia.org/wiki/Who_(Unix)) program that showed IDs and terminal line numbers (the server's internal number of the communication line, over which the user's terminal is connected) for logged-in users. Earnest named his program after the idea that people would run their fingers down the who list to find what they were looking for.
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ```
49
+ gem 'fingerer'
50
+ ```
51
+
52
+ And then execute:
53
+
54
+ ```
55
+ bundle install
56
+ ```
57
+
58
+ Or install it yourself as:
59
+
60
+ ```
61
+ gem install fingerer
62
+ ```
63
+
64
+ ## Usage
65
+
66
+ ```
67
+ Usage: fingerer [options]
68
+ -d, --daemonize Run as a background process
69
+ -p, --port PORT Port to run on (default: 79)
70
+ -l, --listen IP IP to listen on (default: 0.0.0.0)
71
+ ```
72
+
73
+ **Note: Fingerer must be run as root, as fingerd is expected to be listening on port 79.**
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create new Pull Request
82
+
83
+ ## License
84
+
85
+ Fingerer is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ desc 'Run all specs'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
8
+ task test: :spec
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'daemons'
4
+ require 'fingerer'
5
+ require 'optparse'
6
+
7
+ options = {}
8
+ OptionParser.new do |opt|
9
+ opt.on('-d', '--daemonize', 'Run as a background process') { options[:daemonize] = TRUE }
10
+ opt.on('-p', '--port PORT', 'Port to run on (default: 79)') { |o| options[:port] = o }
11
+ opt.on('-l', '--listen IP', 'IP to listen on (default: 0.0.0.0)') { |o| options[:listen] = o }
12
+ end.parse!
13
+
14
+ Daemons.daemonize(:app_name => 'fingerer') if options[:daemonize]
15
+
16
+ Fingerer::Server.start(options)
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fingerer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fingerer"
8
+ spec.version = Fingerer::VERSION
9
+ spec.authors = ["Zachary Flower"]
10
+ spec.email = ["zach@zacharyflower.com"]
11
+ spec.description = %q{Fingerer is a finger server that returns the GitHub profile associated with the provided username. Quickly lookup GitHub user and organization information using a standard finger command.}
12
+ spec.summary = %q{Lookup GitHub profile information using a standard finger command.}
13
+ spec.homepage = "https://github.com/zachflower/fingerer"
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.required_ruby_version = '>= 2.2.0'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "octokit", "~> 4.0"
25
+ spec.add_development_dependency "daemons", "~> 1.2"
26
+ spec.add_development_dependency "rake"
27
+ end
@@ -0,0 +1,117 @@
1
+ require 'octokit'
2
+ require 'socket'
3
+
4
+ require "fingerer/version"
5
+ require "fingerer/debug"
6
+ require "fingerer/table"
7
+ require "fingerer/time"
8
+
9
+ module Fingerer
10
+ class Server
11
+
12
+ def self.start(options = {})
13
+ options[:listen] ||= '0.0.0.0'
14
+ options[:port] ||= 79
15
+
16
+ Debug.info("Booting Fingerer")
17
+
18
+ server = ::TCPServer.new(options[:listen], options[:port])
19
+
20
+ Debug.info("Listening on tcp://#{options[:listen]}:#{options[:port]}");
21
+ Debug.info("Ctrl-C to shutdown server\n");
22
+
23
+ loop do
24
+ Thread.start(server.accept) do |client|
25
+ # start timer
26
+ start_time = Time.now
27
+ remote_ip = client.peeraddr[-1]
28
+ username = client.gets.strip
29
+
30
+ Debug.info("Started request for \"#{username}\" from #{remote_ip}")
31
+
32
+ begin
33
+ user = ::Octokit.user(username)
34
+
35
+ client.puts(response(user))
36
+ rescue Octokit::NotFound
37
+ client.puts("No such user \"#{username}\"")
38
+ rescue => e
39
+ client.puts("Something went wrong")
40
+ Debug.error(e.message)
41
+ end
42
+
43
+ client.close
44
+
45
+ # end timer
46
+ end_time = Time.now
47
+
48
+ Debug.info("Completed request for \"#{username}\" from #{remote_ip} in #{(end_time.to_ms - start_time.to_ms)}ms")
49
+ end
50
+ end
51
+ end
52
+
53
+ private_class_method def self.response(user)
54
+ response = []
55
+
56
+ response << ""
57
+ response << info(user)
58
+ response << stats(user)
59
+ response << biography(user)
60
+ response << ""
61
+ response << "Member since #{user.created_at.strftime("%c")}"
62
+
63
+ response.join("\n")
64
+ end
65
+
66
+ private_class_method def self.info(user)
67
+ response = []
68
+
69
+ if user.type == 'User'
70
+ response << Table.header("User")
71
+ else
72
+ response << Table.header("Organization")
73
+ end
74
+
75
+ response << Table.row("")
76
+ response << Table.row("Username: #{user.login}", "Full Name: #{user.name}")
77
+ response << Table.row("Website: #{user.blog}", "Email: #{user.email}") if user.blog || user.email
78
+ response << Table.row("Location: #{user.location}") if user.location
79
+ response << Table.row("")
80
+
81
+ response.join("\n")
82
+ response
83
+ end
84
+
85
+ private_class_method def self.stats(user)
86
+ response = []
87
+
88
+ if user.type == 'User'
89
+ response << Table.header("Stats")
90
+ response << Table.row("")
91
+ response << Table.row("Followers: #{user.followers}", "Following: #{user.following}", "Repositories: #{user.public_repos}")
92
+ response << Table.row("")
93
+ end
94
+
95
+ response.join("\n")
96
+ end
97
+
98
+ private_class_method def self.biography(user)
99
+ response = []
100
+
101
+ if user.bio
102
+ response << Table.header("Biography")
103
+ response << Table.row("")
104
+
105
+ user.bio.scan(/\S.{0,72}\S(?=\s|$)|\S+/).each do |line|
106
+ response << Table.row(line)
107
+ end
108
+
109
+ response << Table.row("")
110
+ end
111
+
112
+ response << Table.line
113
+
114
+ response.join("\n")
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,12 @@
1
+ module Fingerer
2
+ class Debug
3
+
4
+ def self.info(message)
5
+ puts("[#{Time.now.getutc}] INFO #{message}")
6
+ end
7
+
8
+ def self.error(message)
9
+ puts("[#{Time.now.getutc}] ERROR #{message}")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module Fingerer
2
+ class Table
3
+
4
+ @@_width = 74
5
+
6
+ def self.row(*cols)
7
+ count = cols.count;
8
+
9
+ return "|#{" " * (@@_width + 2)}|" if count == 0
10
+
11
+ width = ((@@_width - (count - 1)) / count).to_i
12
+ remainder = (@@_width - ((width * count) + (count - 1))).to_i
13
+ widths = Array.new(count, width)
14
+
15
+ # add remainder to last element, if needed
16
+ widths[-1] += remainder if remainder
17
+
18
+ widths.map!{|item| "%-#{item}.#{item}s"}
19
+
20
+ sprintf("| #{widths.join(" ")} |", *cols)
21
+ end
22
+
23
+ def self.header(title = "")
24
+ title = title[0..(@@_width - 5)]
25
+ "+-[ #{title} ]#{"-" * (@@_width - (title.length + 3))}+"
26
+ end
27
+
28
+ def self.line
29
+ "+#{"-" * (@@_width + 2)}+"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,8 @@
1
+ module Fingerer
2
+ class Time < ::Time
3
+
4
+ def to_ms
5
+ (self.to_f * 1000.0).to_i
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Fingerer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fingerer::Debug do
4
+
5
+ describe ".info" do
6
+ context "given an empty string" do
7
+ it "returns an empty INFO debug statement" do
8
+ expect(STDOUT).to receive(:puts).with(/INFO $/)
9
+ Fingerer::Debug.info("")
10
+ end
11
+ end
12
+
13
+ context "given a non-empty string" do
14
+ it "returns an empty INFO debug statement" do
15
+ expect(STDOUT).to receive(:puts).with(/INFO hullabaloo$/)
16
+ Fingerer::Debug.info("hullabaloo")
17
+ end
18
+ end
19
+ end
20
+
21
+ describe ".error" do
22
+ context "given an empty string" do
23
+ it "returns an empty ERROR debug statement" do
24
+ expect(STDOUT).to receive(:puts).with(/ERROR $/)
25
+ Fingerer::Debug.error("")
26
+ end
27
+ end
28
+
29
+ context "given an empty string" do
30
+ it "returns a non-empty ERROR debug statement" do
31
+ expect(STDOUT).to receive(:puts).with(/ERROR hullabaloo$/)
32
+ Fingerer::Debug.error("hullabaloo")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fingerer::Table do
4
+
5
+ describe ".row" do
6
+ context "given no parameters" do
7
+ it "returns an empty row statement" do
8
+ response = Fingerer::Table.row
9
+
10
+ expect(response).to eq("|#{" " * 76}|")
11
+ expect(response.length).to be(78)
12
+ end
13
+ end
14
+
15
+ context "given an empty string" do
16
+ it "returns an empty row statement" do
17
+ response = Fingerer::Table.row("")
18
+ expect(response).to eq("|#{" " * 76}|")
19
+ expect(response.length).to be(78)
20
+ end
21
+ end
22
+
23
+ context "given one column" do
24
+ it "returns a one-column row statement" do
25
+ response = Fingerer::Table.row("one")
26
+ expect(response).to eq("| one #{" " * 71}|")
27
+ expect(response.length).to be(78)
28
+ end
29
+
30
+ it "truncates a long string" do
31
+ response = Fingerer::Table.row("this is a really really really really really really really really really long string")
32
+ expect(response).to eq("| this is a really really really really really really really really really l |")
33
+ expect(response.length).to be(78)
34
+ end
35
+ end
36
+
37
+ context "given two columns" do
38
+ it "returns a two-column row statement" do
39
+ response = Fingerer::Table.row("one", "two")
40
+ expect(response).to eq("| one two |")
41
+ expect(response.length).to be(78)
42
+ end
43
+ end
44
+
45
+ context "given three columns" do
46
+ it "returns a three-column row statement" do
47
+ response = Fingerer::Table.row("one", "two", "three")
48
+ expect(response).to eq("| one two three |")
49
+ expect(response.length).to be(78)
50
+ end
51
+ end
52
+
53
+ context "given four columns" do
54
+ it "returns a four-column row statement" do
55
+ response = Fingerer::Table.row("one", "two", "three", "four")
56
+ expect(response).to eq("| one two three four |")
57
+ expect(response.length).to be(78)
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ describe ".header" do
64
+ context "given no parameter" do
65
+ it "returns an empty header line" do
66
+ response = Fingerer::Table.header
67
+ expect(response).to eq("+-[ ]-----------------------------------------------------------------------+")
68
+ expect(response.length).to be(78)
69
+ end
70
+ end
71
+
72
+ context "given an empty string" do
73
+ it "returns an empty header line" do
74
+ response = Fingerer::Table.header("")
75
+ expect(response).to eq("+-[ ]-----------------------------------------------------------------------+")
76
+ expect(response.length).to be(78)
77
+ end
78
+ end
79
+
80
+ context "given a non-empty string" do
81
+ it "returns a non-empty header line" do
82
+ response = Fingerer::Table.header("header")
83
+ expect(response).to eq("+-[ header ]-----------------------------------------------------------------+")
84
+ expect(response.length).to be(78)
85
+ end
86
+
87
+ it "returns a truncated header line" do
88
+ response = Fingerer::Table.header("this is a really really really really really really really really really long string")
89
+ expect(response).to eq("+-[ this is a really really really really really really really really real ]-+")
90
+ expect(response.length).to be(78)
91
+ end
92
+ end
93
+ end
94
+
95
+ describe ".line" do
96
+ it "returns a line" do
97
+ response = Fingerer::Table.line
98
+ expect(response).to eq("+#{"-" * 76}+")
99
+ expect(response.length).to be(78)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,6 @@
1
+ require 'fingerer'
2
+
3
+ RSpec.configure do |config|
4
+ # Use color in STDOUT
5
+ config.color = true
6
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fingerer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Zachary Flower
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-31 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: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: daemons
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Fingerer is a finger server that returns the GitHub profile associated
70
+ with the provided username. Quickly lookup GitHub user and organization information
71
+ using a standard finger command.
72
+ email:
73
+ - zach@zacharyflower.com
74
+ executables:
75
+ - fingerer
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - LICENSE.md
84
+ - README.md
85
+ - Rakefile
86
+ - bin/fingerer
87
+ - fingerer.gemspec
88
+ - lib/fingerer.rb
89
+ - lib/fingerer/debug.rb
90
+ - lib/fingerer/table.rb
91
+ - lib/fingerer/time.rb
92
+ - lib/fingerer/version.rb
93
+ - spec/fingerer/debug_spec.rb
94
+ - spec/fingerer/table_spec.rb
95
+ - spec/spec_helper.rb
96
+ homepage: https://github.com/zachflower/fingerer
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 2.2.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.6.8
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Lookup GitHub profile information using a standard finger command.
120
+ test_files:
121
+ - spec/fingerer/debug_spec.rb
122
+ - spec/fingerer/table_spec.rb
123
+ - spec/spec_helper.rb