lg 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/README.md +117 -0
- data/Rakefile +1 -0
- data/bin/lg +13 -0
- data/lib/gist_store/cacert.pem +116 -0
- data/lib/gist_store/gist.rb +255 -0
- data/lib/gist_store/gist_store.rb +62 -0
- data/lib/gist_store/net_http_ext.rb +56 -0
- data/lib/logbook.rb +14 -0
- data/lib/logbook/book.rb +79 -0
- data/lib/logbook/cli.rb +127 -0
- data/lib/logbook/version.rb +3 -0
- data/logbook.gemspec +29 -0
- data/spec/gist_store/gist_store_spec.rb +86 -0
- data/spec/logbook/book_spec.rb +82 -0
- data/spec/logbook/cli_spec.rb +134 -0
- data/spec/spec_helper.rb +58 -0
- metadata +131 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'minitest' do
|
5
|
+
|
6
|
+
# with Minitest::Spec
|
7
|
+
watch(%r|^spec/(.*)_spec\.rb|)
|
8
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
9
|
+
watch(%r|^spec/spec_helper\.rb|) { "spec" }
|
10
|
+
|
11
|
+
# Rails 3.2
|
12
|
+
# watch(%r|^app/controllers/(.*)\.rb|) { |m| "test/controllers/#{m[1]}_test.rb" }
|
13
|
+
# watch(%r|^app/helpers/(.*)\.rb|) { |m| "test/helpers/#{m[1]}_test.rb" }
|
14
|
+
# watch(%r|^app/models/(.*)\.rb|) { |m| "test/unit/#{m[1]}_test.rb" }
|
15
|
+
|
16
|
+
# Rails
|
17
|
+
# watch(%r|^app/controllers/(.*)\.rb|) { |m| "test/functional/#{m[1]}_test.rb" }
|
18
|
+
# watch(%r|^app/helpers/(.*)\.rb|) { |m| "test/helpers/#{m[1]}_test.rb" }
|
19
|
+
# watch(%r|^app/models/(.*)\.rb|) { |m| "test/unit/#{m[1]}_test.rb" }
|
20
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# Logbook
|
2
|
+
|
3
|
+
Logbook allows you to record memories easily from your command line into
|
4
|
+
virtual book(s). Books are simply private Github Gists (backend is
|
5
|
+
replaceable).
|
6
|
+
|
7
|
+
|
8
|
+
It is heavily inspired from its pythonish friend, http://maebert.github.com/jrnl.
|
9
|
+
|
10
|
+
I built it because I love Ruby, it was easy enough, and I really loved
|
11
|
+
the idea of storing as Gists as opposed to plain files.
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
# Usage
|
16
|
+
|
17
|
+
For brevity's sake, the Logbook gem name and executable is `lg`.
|
18
|
+
|
19
|
+
$ gem install lg
|
20
|
+
$ lg
|
21
|
+
|
22
|
+
|
23
|
+
If you want private Gists attached to your user (you most probably
|
24
|
+
want that), make sure to set your Github credentials as environment
|
25
|
+
variables, example:
|
26
|
+
|
27
|
+
$ export GITHUB_USER=youruser
|
28
|
+
$ export GITHUB_PASSWORD=yourpw
|
29
|
+
|
30
|
+
Now we need to make a first `book` and start `add`ing into it.
|
31
|
+
|
32
|
+
## Setting up a book
|
33
|
+
|
34
|
+
Create a new book with the `lg book` command. You can give it a
|
35
|
+
cover, in this case `The Wizzard of Oz`.
|
36
|
+
|
37
|
+
$ lg book The Wizzard of Oz
|
38
|
+
|
39
|
+
|
40
|
+
## Adding lgries
|
41
|
+
|
42
|
+
Simply say 'lg add' and your memory in a short sentence.
|
43
|
+
|
44
|
+
$ lg add just wrote the logbook gem README
|
45
|
+
|
46
|
+
You might find it convenient to specify when a thing happend explicitly,
|
47
|
+
just make sure to specify a natural date such as `yesterday` separated
|
48
|
+
by a colon `:`. Translation done with the `chronic` gem.
|
49
|
+
|
50
|
+
$ lg add yesterday: wrote the logbook gem README
|
51
|
+
|
52
|
+
|
53
|
+
## More
|
54
|
+
|
55
|
+
You can safely skip this if that's all what you're looking for.
|
56
|
+
|
57
|
+
## Switching books
|
58
|
+
|
59
|
+
Switch between books, when you know what you want, you can explicitly
|
60
|
+
specify the ID.
|
61
|
+
|
62
|
+
$ lg book book-id
|
63
|
+
|
64
|
+
Or pick from a menu, leaving arguments blank:
|
65
|
+
|
66
|
+
$ lg book
|
67
|
+
1 The Wizzard of Oz deadbeef0aef
|
68
|
+
...
|
69
|
+
Pick one: 1
|
70
|
+
|
71
|
+
|
72
|
+
## Listing lgries
|
73
|
+
|
74
|
+
Say `lg all` when you want to see everything you've recorded.
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
# Philosophy
|
80
|
+
|
81
|
+
Command line is awesome. Its fast, and you feel it when you're less
|
82
|
+
dependent on your mouse for your development work (e.g. VIM).
|
83
|
+
|
84
|
+
You should just Alt/Command-Tab, write a line and go back working.
|
85
|
+
|
86
|
+
You should be expected to remember at most one commands (pitfall of success) to do actual work. Seriously, [focus](http://ezliu.com/focus/).
|
87
|
+
|
88
|
+
|
89
|
+
## There's no search like in jrnl
|
90
|
+
|
91
|
+
Feature slim. Use gist search for that. True, its limited, but as of now, I believe
|
92
|
+
Github are working on improving that.
|
93
|
+
|
94
|
+
In actuallity, `jrnl`'s search loads all of your entries to memory and
|
95
|
+
performs search on an in-memory structure.
|
96
|
+
If the need arises, it should be dead easy to
|
97
|
+
make *that* kind of search in `logbook`.
|
98
|
+
|
99
|
+
## There's no delete/modify
|
100
|
+
|
101
|
+
Again, feature slim. If you were using a real logbook, you'd
|
102
|
+
just cross the bad entry. It will still be there.
|
103
|
+
If you must, you can always use the gist interface for that.
|
104
|
+
|
105
|
+
## There's no analytics, let me reap added value from my work!
|
106
|
+
|
107
|
+
Actually, a gist entry is a Git repository.
|
108
|
+
The modeling on-top-of a
|
109
|
+
Gist was done intentionally. Clone your book and
|
110
|
+
treat it like a Git repo.
|
111
|
+
|
112
|
+
From there, you can script against git and/or run countless analysis tools on your repository.
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/lg
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
Certificate chain
|
2
|
+
0 s:/O=*.github.com/OU=Domain Control Validated/CN=*.github.com
|
3
|
+
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287
|
4
|
+
-----BEGIN CERTIFICATE-----
|
5
|
+
MIIFVTCCBD2gAwIBAgIHBGX+dPs18DANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
|
6
|
+
BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
|
7
|
+
BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm
|
8
|
+
aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5
|
9
|
+
IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky
|
10
|
+
ODcwHhcNMDkxMjExMDUwMjM2WhcNMTQxMjExMDUwMjM2WjBRMRUwEwYDVQQKEwwq
|
11
|
+
LmdpdGh1Yi5jb20xITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDEV
|
12
|
+
MBMGA1UEAxMMKi5naXRodWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
13
|
+
CgKCAQEA7dOJw11wcgnzM08acnTZtlqVULtoYZ/3+x8Z4doEMa8VfBp/+XOvHeVD
|
14
|
+
K1YJAEVpSujEW9/Cd1JRGVvRK9k5ZTagMhkcQXP7MrI9n5jsglsLN2Q5LLcQg3LN
|
15
|
+
8OokS/rZlC7DhRU5qTr2iNr0J4mmlU+EojdOfCV4OsmDbQIXlXh9R6hVg+4TyBka
|
16
|
+
szzxX/47AuGF+xFmqwldn0xD8MckXilyKM7UdWhPJHIprjko/N+NT02Dc3QMbxGb
|
17
|
+
p91i3v/i6xfm/wy/wC0xO9ZZovLdh0pIe20zERRNNJ8yOPbIGZ3xtj3FRu9RC4rG
|
18
|
+
M+1IYcQdFxu9fLZn6TnPpVKACvTqzQIDAQABo4IBtjCCAbIwDwYDVR0TAQH/BAUw
|
19
|
+
AwEBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQD
|
20
|
+
AgWgMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Rz
|
21
|
+
MS0xMS5jcmwwUwYDVR0gBEwwSjBIBgtghkgBhv1tAQcXATA5MDcGCCsGAQUFBwIB
|
22
|
+
FitodHRwOi8vY2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMIGA
|
23
|
+
BggrBgEFBQcBAQR0MHIwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHku
|
24
|
+
Y29tLzBKBggrBgEFBQcwAoY+aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
|
25
|
+
bS9yZXBvc2l0b3J5L2dkX2ludGVybWVkaWF0ZS5jcnQwHwYDVR0jBBgwFoAU/axh
|
26
|
+
MpNsRdbi7oVfmrrndplozOcwIwYDVR0RBBwwGoIMKi5naXRodWIuY29tggpnaXRo
|
27
|
+
dWIuY29tMB0GA1UdDgQWBBSH0Y8ZbuSHb1OMd5EHUN+jv1VHIDANBgkqhkiG9w0B
|
28
|
+
AQUFAAOCAQEAwIe/Bbuk1/r38aqb5wlXjoW6tAmLpzLRkKorDOcDUJLtN6a9XqAk
|
29
|
+
cgMai7NCI1YV+A4IjEENj53mV2xWLpniqLDHI5y2NbQuL2deu1jQSSNz7xE/nZCk
|
30
|
+
WGt8OEtm6YI2bUsq5EXy078avRbigBko1bqtFuG0s5+nFrKCjhQVIk+GX7cwiyr4
|
31
|
+
XJ49FxETvePrxNYr7x7n/Jju59KXTw3juPET+bAwNlRXmScjrMylMNUMr3sFcyLz
|
32
|
+
DciaVnnextu6+L0w1+5KNVbMKndRwgg/cRldBL4AgmtouTC3mlDGGG3U6eV75cdH
|
33
|
+
D03DXDfrYYjxmWjTRdO2GdbYnt1ToEgxyA==
|
34
|
+
-----END CERTIFICATE-----
|
35
|
+
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287
|
36
|
+
i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
|
37
|
+
-----BEGIN CERTIFICATE-----
|
38
|
+
MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
|
39
|
+
ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
|
40
|
+
RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
|
41
|
+
MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
|
42
|
+
QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
|
43
|
+
b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
|
44
|
+
b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
|
45
|
+
YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
|
46
|
+
AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
|
47
|
+
KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
|
48
|
+
VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
|
49
|
+
SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
|
50
|
+
cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
|
51
|
+
6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
|
52
|
+
MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
|
53
|
+
kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
|
54
|
+
BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
|
55
|
+
BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
|
56
|
+
c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
|
57
|
+
AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
|
58
|
+
BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
|
59
|
+
OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
|
60
|
+
A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
|
61
|
+
0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
|
62
|
+
RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
|
63
|
+
qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
|
64
|
+
U+4=
|
65
|
+
-----END CERTIFICATE-----
|
66
|
+
2 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
|
67
|
+
i:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
|
68
|
+
-----BEGIN CERTIFICATE-----
|
69
|
+
MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
|
70
|
+
bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
|
71
|
+
Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
|
72
|
+
QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
|
73
|
+
BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoX
|
74
|
+
DTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBE
|
75
|
+
YWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0
|
76
|
+
aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC
|
77
|
+
ggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
|
78
|
+
2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+q
|
79
|
+
N1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiO
|
80
|
+
r18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN
|
81
|
+
f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEH
|
82
|
+
U1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHU
|
83
|
+
TBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMb
|
84
|
+
VmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwg
|
85
|
+
SW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlv
|
86
|
+
biBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEg
|
87
|
+
MB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUw
|
88
|
+
AwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdv
|
89
|
+
ZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMu
|
90
|
+
Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUd
|
91
|
+
IAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
|
92
|
+
bS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1
|
93
|
+
QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4O
|
94
|
+
WBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0Vmsf
|
95
|
+
SxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==
|
96
|
+
-----END CERTIFICATE-----
|
97
|
+
3 s:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
|
98
|
+
i:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
|
99
|
+
-----BEGIN CERTIFICATE-----
|
100
|
+
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
|
101
|
+
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
|
102
|
+
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
|
103
|
+
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
|
104
|
+
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
|
105
|
+
NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
|
106
|
+
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
|
107
|
+
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
|
108
|
+
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
|
109
|
+
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
|
110
|
+
dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
|
111
|
+
WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
|
112
|
+
v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
|
113
|
+
UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
|
114
|
+
IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
|
115
|
+
W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
|
116
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'gist_store/net_http_ext'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'net/https'
|
4
|
+
require 'optparse'
|
5
|
+
require 'json'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
# You can use this class from other scripts with the greatest of
|
11
|
+
# ease.
|
12
|
+
#
|
13
|
+
# >> Gist.read(gist_id)
|
14
|
+
# Returns the body of gist_id as a string.
|
15
|
+
#
|
16
|
+
# >> Gist.write(content)
|
17
|
+
# Creates a gist from the string `content`. Returns the URL of the
|
18
|
+
# new gist.
|
19
|
+
#
|
20
|
+
# >> Gist.copy(string)
|
21
|
+
# Copies string to the clipboard.
|
22
|
+
#
|
23
|
+
# >> Gist.browse(url)
|
24
|
+
# Opens URL in your default browser.
|
25
|
+
module Gist
|
26
|
+
extend self
|
27
|
+
|
28
|
+
GIST_URL = 'https://api.github.com/gists/%s'
|
29
|
+
CREATE_URL = 'https://api.github.com/gists'
|
30
|
+
|
31
|
+
if ENV['HTTPS_PROXY']
|
32
|
+
PROXY = URI(ENV['HTTPS_PROXY'])
|
33
|
+
elsif ENV['HTTP_PROXY']
|
34
|
+
PROXY = URI(ENV['HTTP_PROXY'])
|
35
|
+
else
|
36
|
+
PROXY = nil
|
37
|
+
end
|
38
|
+
PROXY_HOST = PROXY ? PROXY.host : nil
|
39
|
+
PROXY_PORT = PROXY ? PROXY.port : nil
|
40
|
+
|
41
|
+
|
42
|
+
# Create a gist on gist.github.com
|
43
|
+
def write(files, private_gist = false, description = nil)
|
44
|
+
url = URI.parse(CREATE_URL)
|
45
|
+
|
46
|
+
if PROXY_HOST
|
47
|
+
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
|
48
|
+
http = proxy.new(url.host, url.port)
|
49
|
+
else
|
50
|
+
http = Net::HTTP.new(url.host, url.port)
|
51
|
+
end
|
52
|
+
|
53
|
+
http.use_ssl = true
|
54
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
55
|
+
http.ca_file = ca_cert
|
56
|
+
|
57
|
+
req = Net::HTTP::Post.new(url.path)
|
58
|
+
req.body = JSON.generate(data(files, private_gist, description))
|
59
|
+
|
60
|
+
user, password = auth()
|
61
|
+
if user && password
|
62
|
+
req.basic_auth(user, password)
|
63
|
+
end
|
64
|
+
|
65
|
+
response = http.start{|h| h.request(req) }
|
66
|
+
case response
|
67
|
+
when Net::HTTPCreated
|
68
|
+
JSON.parse(response.body)['html_url']
|
69
|
+
else
|
70
|
+
raise "Creating gist failed: #{response.code} #{response.message}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create a gist on gist.github.com
|
75
|
+
def update(id, files)
|
76
|
+
url = URI.parse(CREATE_URL)
|
77
|
+
|
78
|
+
if PROXY_HOST
|
79
|
+
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
|
80
|
+
http = proxy.new(url.host, url.port)
|
81
|
+
else
|
82
|
+
http = Net::HTTP.new(url.host, url.port)
|
83
|
+
end
|
84
|
+
|
85
|
+
http.use_ssl = true
|
86
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
87
|
+
http.ca_file = ca_cert
|
88
|
+
|
89
|
+
req = Net::HTTP::Patch.new(GIST_URL % id)
|
90
|
+
req.body = JSON.generate(data(files, nil, nil))
|
91
|
+
|
92
|
+
user, password = auth()
|
93
|
+
if user && password
|
94
|
+
req.basic_auth(user, password)
|
95
|
+
end
|
96
|
+
|
97
|
+
response = http.start{|h| h.request(req) }
|
98
|
+
case response
|
99
|
+
when Net::HTTPOK
|
100
|
+
JSON.parse(response.body)['html_url']
|
101
|
+
else
|
102
|
+
raise "Updating gist failed: #{response.code} #{response.message}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
# Create a gist on gist.github.com
|
109
|
+
def delete(id)
|
110
|
+
url = URI.parse(CREATE_URL)
|
111
|
+
|
112
|
+
if PROXY_HOST
|
113
|
+
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
|
114
|
+
http = proxy.new(url.host, url.port)
|
115
|
+
else
|
116
|
+
http = Net::HTTP.new(url.host, url.port)
|
117
|
+
end
|
118
|
+
|
119
|
+
http.use_ssl = true
|
120
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
121
|
+
http.ca_file = ca_cert
|
122
|
+
|
123
|
+
req = Net::HTTP::Delete.new(GIST_URL % id)
|
124
|
+
|
125
|
+
user, password = auth()
|
126
|
+
if user && password
|
127
|
+
req.basic_auth(user, password)
|
128
|
+
end
|
129
|
+
|
130
|
+
response = http.start{|h| h.request(req) }
|
131
|
+
case response
|
132
|
+
when Net::HTTPNoContent
|
133
|
+
true
|
134
|
+
else
|
135
|
+
raise "Creating gist failed: #{response.code} #{response.message}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Given a gist id, returns its content.
|
140
|
+
def read(gist_id)
|
141
|
+
data = read_raw(gist_id)
|
142
|
+
data["files"].map{|name, content| content['content'] }.join("\n\n")
|
143
|
+
end
|
144
|
+
|
145
|
+
def read_raw(gist_id)
|
146
|
+
data = JSON.parse(open(GIST_URL % gist_id).read)
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
private
|
151
|
+
# Give an array of file information and private boolean, returns
|
152
|
+
# an appropriate payload for POSTing to gist.github.com
|
153
|
+
def data(files, private_gist, description)
|
154
|
+
i = 0
|
155
|
+
file_data = {}
|
156
|
+
files.each do |file|
|
157
|
+
i = i + 1
|
158
|
+
filename = file[:filename] ? file[:filename] : "gistfile#{i}"
|
159
|
+
file_data[filename] = {:content => file[:input]}
|
160
|
+
end
|
161
|
+
|
162
|
+
data = {"files" => file_data}
|
163
|
+
data.merge!({ 'description' => description }) unless description.nil?
|
164
|
+
data.merge!({ 'public' => !private_gist }) unless private_gist.nil?
|
165
|
+
data
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns a basic auth string of the user's GitHub credentials if set.
|
169
|
+
# http://github.com/guides/local-github-config
|
170
|
+
#
|
171
|
+
# Returns an Array of Strings if auth is found: [user, password]
|
172
|
+
# Returns nil if no auth is found.
|
173
|
+
def auth
|
174
|
+
user = config("github.user")
|
175
|
+
password = config("github.password")
|
176
|
+
|
177
|
+
token = config("github.token")
|
178
|
+
if password.to_s.empty? && !token.to_s.empty?
|
179
|
+
raise "Please set GITHUB_PASSWORD or github.password instead of using a token."
|
180
|
+
end
|
181
|
+
|
182
|
+
if user.to_s.empty? || password.to_s.empty?
|
183
|
+
nil
|
184
|
+
else
|
185
|
+
[ user, password ]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns default values based on settings in your gitconfig. See
|
190
|
+
# git-config(1) for more information.
|
191
|
+
#
|
192
|
+
# Settings applicable to gist.rb are:
|
193
|
+
#
|
194
|
+
# gist.private - boolean
|
195
|
+
# gist.extension - string
|
196
|
+
def defaults
|
197
|
+
extension = config("gist.extension")
|
198
|
+
|
199
|
+
return {
|
200
|
+
"private" => config("gist.private"),
|
201
|
+
"browse" => config("gist.browse"),
|
202
|
+
"extension" => extension
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
# Reads a config value using:
|
207
|
+
# => Environment: GITHUB_PASSWORD, GITHUB_USER
|
208
|
+
# like vim gist plugin
|
209
|
+
# => git-config(1)
|
210
|
+
#
|
211
|
+
# return something useful or nil
|
212
|
+
def config(key)
|
213
|
+
env_key = ENV[key.upcase.gsub(/\./, '_')]
|
214
|
+
return env_key if env_key and not env_key.strip.empty?
|
215
|
+
|
216
|
+
str_to_bool `git config --global #{key}`.strip
|
217
|
+
end
|
218
|
+
|
219
|
+
# Parses a value that might appear in a .gitconfig file into
|
220
|
+
# something useful in a Ruby script.
|
221
|
+
def str_to_bool(str)
|
222
|
+
if str.size > 0 and str[0].chr == '!'
|
223
|
+
command = str[1, str.length]
|
224
|
+
value = `#{command}`
|
225
|
+
else
|
226
|
+
value = str
|
227
|
+
end
|
228
|
+
|
229
|
+
case value.downcase.strip
|
230
|
+
when "false", "0", "nil", "", "no", "off"
|
231
|
+
nil
|
232
|
+
when "true", "1", "yes", "on"
|
233
|
+
true
|
234
|
+
else
|
235
|
+
value
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def ca_cert
|
240
|
+
cert_file = [
|
241
|
+
File.expand_path("../cacert.pem", __FILE__),
|
242
|
+
"/tmp/gist_cacert.pem"
|
243
|
+
].find{|l| File.exist?(l) }
|
244
|
+
|
245
|
+
if cert_file
|
246
|
+
cert_file
|
247
|
+
else
|
248
|
+
File.open("/tmp/gist_cacert.pem", "w") do |f|
|
249
|
+
f.write(DATA.read.split("__CACERT__").last)
|
250
|
+
end
|
251
|
+
"/tmp/gist_cacert.pem"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|