lg 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.
@@ -0,0 +1,62 @@
1
+ require 'gist_store/gist'
2
+
3
+ module Logbook
4
+ class GistStore
5
+ DATE_FMT = "%Y%m%d".freeze
6
+ attr_reader :error
7
+
8
+ def initialize
9
+ @valid = !!(ENV['GITHUB_USER'] && ENV['GITHUB_PASSWORD'])
10
+ @error = @valid ? '' : "Please set up GITHUB_USER and GITHUB_PASSWORD in your env."
11
+ end
12
+
13
+ def valid?
14
+ @valid
15
+ end
16
+
17
+ def get(id, time)
18
+ gist = Gist.read_raw(id)
19
+ return nil unless gist
20
+ file = gist['files'][gist_file(time)]
21
+ return nil unless file
22
+ file['content']
23
+ end
24
+
25
+ def create(covertext)
26
+ u = URI.parse Gist.write(gist_data(covertext, "cover"), true)
27
+ u.path[1..-1]
28
+ end
29
+
30
+ def update(id, time, page)
31
+ Gist.update(id, gist_data(page, gist_file(time)))
32
+ end
33
+
34
+ def all(id)
35
+ gist = Gist.read_raw(id)
36
+ return nil unless gist
37
+
38
+ entries = gist['files'].reject{|k| ["cover"].include? k }.map do |fname, v|
39
+ { :date => DateTime.strptime(v['filename'], DATE_FMT),
40
+ :content => v['content'] }
41
+ end
42
+
43
+ { :id => id,
44
+ :cover => gist['files']['cover']['content'],
45
+ :entries => entries }
46
+ end
47
+
48
+ def destroy(id)
49
+ Gist.delete(id)
50
+ end
51
+
52
+ private
53
+ def gist_file(time)
54
+ time.strftime DATE_FMT
55
+ end
56
+
57
+ def gist_data(text, fname)
58
+ [{:input => text, :filename => fname, :extension => "txt"}]
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,56 @@
1
+ require 'net/http'
2
+ module Net
3
+ class HTTP
4
+
5
+ # Adding the patch method if it doesn't exist (rest-client issue: https://github.com/archiloque/rest-client/issues/79)
6
+ if !defined?(Net::HTTP::Patch)
7
+ # Code taken from this commit: https://github.com/ruby/ruby/commit/ab70e53ac3b5102d4ecbe8f38d4f76afad29d37d#lib/net/http.rb
8
+ class Protocol
9
+ # Sends a PATCH request to the +path+ and gets a response,
10
+ # as an HTTPResponse object.
11
+ def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
12
+ send_entity(path, data, initheader, dest, Patch, &block)
13
+ end
14
+
15
+ # Executes a request which uses a representation
16
+ # and returns its body.
17
+ def send_entity(path, data, initheader, dest, type, &block)
18
+ res = nil
19
+ request(type.new(path, initheader), data) {|r|
20
+ r.read_body dest, &block
21
+ res = r
22
+ }
23
+ unless @newimpl
24
+ res.value
25
+ return res, res.body
26
+ end
27
+ res
28
+ end
29
+ end
30
+
31
+ class Patch < HTTPRequest
32
+ METHOD = 'PATCH'
33
+ REQUEST_HAS_BODY = true
34
+ RESPONSE_HAS_BODY = true
35
+ end
36
+ end
37
+
38
+ #
39
+ # Replace the request method in Net::HTTP to sniff the body type
40
+ # and set the stream if appropriate
41
+ #
42
+ # Taken from:
43
+ # http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
44
+
45
+ alias __request__ request
46
+
47
+ def request(req, body=nil, &block)
48
+ if body != nil && body.respond_to?(:read)
49
+ req.body_stream = body
50
+ return __request__(req, nil, &block)
51
+ else
52
+ return __request__(req, body, &block)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,14 @@
1
+ require 'gist_store/gist_store'
2
+
3
+ module Logbook
4
+ def self.store
5
+ @store ||= GistStore.new
6
+ end
7
+ def self.store=(store)
8
+ @store = store
9
+ end
10
+ end
11
+
12
+ require "logbook/version"
13
+ require 'logbook/book'
14
+
@@ -0,0 +1,79 @@
1
+
2
+ require 'date'
3
+ require 'chronic'
4
+ require 'gist_store/gist_store'
5
+
6
+ module Logbook
7
+ class Book
8
+ attr_reader :id
9
+ attr_accessor :store
10
+
11
+
12
+ def initialize(id=nil)
13
+ @id = id
14
+ @store = Logbook.store
15
+ end
16
+
17
+ def create(covertext=nil)
18
+ store_available
19
+
20
+ cover = covertext || "#{ENV['USER'] || 'Captain'}'s log."
21
+ @id = store.create cover
22
+ end
23
+
24
+ def add_temporal(text)
25
+ temporal = false
26
+
27
+ time = nil
28
+ if text =~ /(.+?)\s*:\s*(.*)/
29
+ time = Chronic.parse($1)
30
+ text = $2 if time
31
+ temporal = true if time
32
+ end
33
+
34
+ time ||= Time.now
35
+
36
+ add time, text
37
+ end
38
+
39
+ def add(time, text)
40
+ store_available
41
+ must_exist
42
+
43
+ page = get(time)
44
+ store.update(id, time, "#{page ? "#{page}\n" : ""}#{text}")
45
+ time
46
+ end
47
+
48
+ def get(time)
49
+ store_available
50
+ must_exist
51
+
52
+ page = store.get(id, time)
53
+ end
54
+
55
+
56
+ def all
57
+ store_available
58
+ must_exist
59
+
60
+ store.all(id)
61
+ end
62
+
63
+ def destroy
64
+ store_available
65
+ must_exist
66
+
67
+ store.destroy(id)
68
+ end
69
+
70
+ def store_available
71
+ raise store.error unless store.valid?
72
+ end
73
+
74
+ def must_exist
75
+ raise "create a book first" unless @id
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,127 @@
1
+
2
+ require 'logbook'
3
+ require 'thor'
4
+ require 'user_config'
5
+
6
+
7
+
8
+ class Logbook::CLI < Thor
9
+
10
+ desc "book?", "which book are we on?"
11
+ def book?
12
+ j = current_book
13
+ return unless j
14
+ say config[:books][j.id]
15
+ end
16
+
17
+ desc "book [ID]", "open a book"
18
+ def book(*params)
19
+ text = params.join ' '
20
+ config[:books] ||= {}
21
+
22
+ # no argument given: prompt user to switch books from a menu
23
+ if text.empty? && !config[:books].empty?
24
+ choices = config[:books].to_a
25
+ choices = choices.map.with_index{ |a, i| [i+1, *a]}
26
+ print_table choices
27
+ selection = ask("Pick one:").to_i
28
+ if selection == 0 || choices.size < selection
29
+ error "No such option"
30
+ return
31
+ end
32
+
33
+ selected_id = choices[selection-1][1]
34
+ self.current_book = selected_id
35
+ ok "selected"
36
+ return
37
+ end
38
+
39
+ # we have an argument, switch to, or create a new one.
40
+ if config[:books].has_key? text
41
+ self.current_book = text
42
+ ok "switched"
43
+ else
44
+ if yes? "Create #{em(text)}?"
45
+ j = Logbook::Book.new
46
+ begin
47
+ id = j.create text
48
+ self.current_book = id
49
+
50
+ config[:books][id] = text
51
+ config.save
52
+ ok "saved '#{text}' as #{em(id)}"
53
+ rescue
54
+ error $!
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ desc "add MEMORY", "add a new memory"
61
+ def add(*memory)
62
+ text = memory.join ' '
63
+ j = current_book
64
+ return unless j
65
+ begin
66
+ time = j.add_temporal text
67
+ ok("at #{em(time.to_s)}")
68
+ rescue
69
+ error $!
70
+ end
71
+ end
72
+
73
+ desc "all", "list the entire book"
74
+ def all
75
+ j = current_book
76
+ begin
77
+ data = j.all
78
+ say "[#{em data[:cover]}]\n"
79
+ data[:entries].group_by{|g| g[:date]}.each do |key, entries|
80
+ say "#{em key.strftime("%A, %d-%b-%Y")}"
81
+ entries.each do |entry|
82
+ say "#{entry[:content]}"
83
+ end
84
+ end
85
+ rescue
86
+ error $!
87
+ end
88
+ end
89
+
90
+
91
+ private
92
+ def current_book
93
+ id = config[:current_book]
94
+ unless id
95
+ error "No book is set. Create one with $ lg book #{em 'My Stories Unfold'}."
96
+ return nil
97
+ end
98
+
99
+ Logbook::Book.new id
100
+ end
101
+
102
+ def current_book=(id)
103
+ config[:current_book] = id
104
+ config.save
105
+ end
106
+
107
+ def config
108
+ @uconf ||= UserConfig.new(".logbook")
109
+ @uconf["logbook.yaml"]
110
+ end
111
+
112
+ def em(text)
113
+ shell.set_color(text, nil, true)
114
+ end
115
+
116
+ def ok(detail=nil)
117
+ text = detail ? "OK, #{detail}." : "OK."
118
+ say text, :green
119
+ end
120
+
121
+ def error(detail)
122
+ say detail, :red
123
+ end
124
+
125
+
126
+ end
127
+
@@ -0,0 +1,3 @@
1
+ module Logbook
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "logbook/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lg"
7
+ s.version = Logbook::VERSION
8
+ s.authors = ["Dotan Nahum"]
9
+ s.email = ["jondotan@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{log your memories onto virtual logbooks made of Gists}
12
+ s.description = %q{log your memories onto virtual logbooks made of Gists}
13
+
14
+ s.rubyforge_project = "lg"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rr"
23
+ s.add_development_dependency "fakefs"
24
+ s.add_development_dependency "guard-minitest"
25
+ s.add_runtime_dependency "user_config"
26
+ s.add_runtime_dependency "thor"
27
+ s.add_runtime_dependency "chronic"
28
+
29
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'gist_store/gist_store'
3
+
4
+ def stamp
5
+ @stamp ||= DateTime.new(1981,9,8)
6
+ end
7
+
8
+ describe Logbook::GistStore do
9
+ it "should get content" do
10
+ mock(Gist).read_raw('bid') {
11
+ {
12
+ 'files' => {
13
+ '19810908' => {
14
+ 'content' => "hello world"
15
+ }
16
+ }
17
+ }
18
+ }
19
+
20
+ g = Logbook::GistStore.new
21
+ g.get('bid', stamp).must_equal "hello world"
22
+ end
23
+
24
+ it "should create" do
25
+ mock(Gist).write(
26
+ [{
27
+ :input => "hello world",
28
+ :filename => "cover",
29
+ :extension => "txt"
30
+ }],
31
+ true
32
+ ){
33
+ "http://example.com/id"
34
+ }
35
+
36
+ g = Logbook::GistStore.new
37
+ g.create("hello world")
38
+ end
39
+
40
+ it "should list all" do
41
+ mock(Gist).read_raw('bid'){
42
+ {
43
+ 'files' => {
44
+ '19810908' => {
45
+ 'filename' => '19810908',
46
+ 'content' => "hello world"
47
+ },
48
+ 'cover' => {
49
+ 'filename' => 'cover',
50
+ 'content' => "my book"
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ g = Logbook::GistStore.new
57
+ book = g.all('bid')
58
+
59
+ book.must_equal(
60
+ {
61
+ :id => 'bid',
62
+ :cover => 'my book',
63
+ :entries => [{:date => DateTime.strptime("19810908", "%Y%m%d"), :content => 'hello world'}]
64
+ }
65
+ )
66
+ end
67
+
68
+ it "should be invalid when env variables are not set" do
69
+ ENV['GITHUB_USER'] = nil
70
+ ENV['GITHUB_PASSWORD'] = nil
71
+
72
+ g = Logbook::GistStore.new
73
+ g.valid?.must_equal false
74
+ g.error.must_match /Please set up/
75
+ end
76
+
77
+ it "should be valid when env variables are set" do
78
+ ENV['GITHUB_USER'] = 'ghuser'
79
+ ENV['GITHUB_PASSWORD'] = 'ghpwd'
80
+
81
+ g = Logbook::GistStore.new
82
+ g.valid?.must_equal true
83
+ end
84
+
85
+ end
86
+