til-rb 0.0.2 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40d7142e1a89db2641e1d6da0699c13ee4a4da511331c9822deaef61ca9d546b
4
- data.tar.gz: 29eedc188aa255a16d0cb9734412594013b1b2a18ae2836c5194cc0a3ae827e5
3
+ metadata.gz: 929173ad31be6464b75779784d2c1bb1d37f04d548b1869783e156bc1a13cb11
4
+ data.tar.gz: 351871aa23247e031ab56b163a3e3684b8cc995d8fe0dc65b6c529b851c51332
5
5
  SHA512:
6
- metadata.gz: d58c4a7620cfd20fc9901fa3bc10526662a6eea6c0393d58334976cf67a002a871bfad747c6b8ddd95190e824a9f98d2cd81548eebab2811cccefdda7a0d6aa6
7
- data.tar.gz: c8f88bf4d8a0336efafcbcb4d0add65a8979845fbea4e34cca19e29a2563e06668335c94d22a3af1003f5eb35782db1329de158685a7518b84131276a0ea6486
6
+ metadata.gz: ce2a20ab053f5e3ed0f8bb68cd085beb60e87a5f45c39e9af95626714eb309275f6464b9d5690ad6b7eedf34b6d476999b890ac16e72db8b1fc2dc0a838eedb8
7
+ data.tar.gz: c418c12ae3bf2ee02255350c330b6271a07d0bcd053ea2b45acf762b852e5e0eb04daf4676bbec9980bfe803f2e6b83c598bd39e51075527fbf8233b9f2f4087
data/README.md CHANGED
@@ -5,3 +5,69 @@
5
5
  A work-in-progress tool to maintain a TIL repo.
6
6
 
7
7
  Inspiration: https://github.com/jbranchaud/til
8
+
9
+ See it in action below:
10
+
11
+ ![til in action](til.gif)
12
+
13
+ ## Installation
14
+
15
+ ### Step 1: Install the gem
16
+
17
+ ```
18
+ gem install til-rb
19
+ ```
20
+
21
+ You will also need `fzf` to run `til`, it's available on homebrew, so unless you already installed it, you'll have to run
22
+
23
+ ```
24
+ brew install fzf
25
+ ```
26
+
27
+ _fzf is technically not a hard requirement, we really could have a slightly different workflow if it's not available.
28
+ Given that I currently am the only user, there's no need to change this at the moment, but if you'd like to use this
29
+ gem without `fzf`, let me know and I'll happily work on it!_
30
+
31
+ ### Step 2: Create a GitHub repo
32
+
33
+ You need a GitHub repo to store your TILs. The gem has pretty strict expectations about the format of the README.md
34
+ file, so I recommend forking [this repo](https://github.com/pjambet/til-template), or just copying the content to a
35
+ fresh new repo.
36
+
37
+ Note: The format expectations mentioned above are the following:
38
+
39
+ - A "categories" section as defined by a leading markdown separator `---`, followed by a blank line, the `###
40
+ Categories` title, and a list of all the categories.
41
+ - A links section as defined by a leading markdown separator `---`, followed by a blank line and a series of markdown
42
+ titles for each categories present in the previous section.
43
+
44
+ ### Step 3: Add the environment variables
45
+
46
+ Add the following variables to your environment:
47
+
48
+ - `GH_TOKEN`: The only required scope is `public_repo` if your TIL repo is public and `repo` if it is private. You can
49
+ create a token [in the GitHub
50
+ settings](https://github.com/settings/tokens/new?scopes=public_repo&description=Token%20for%20til-rb)
51
+ - `GH_REPO`: The repo name, e.g. `pjambet/til`
52
+
53
+ You might want to add those to either your `.bashrc` or `.zshrc` but please be careful in case you share those publicly
54
+ as the token is private and *must not* be shared publicly.
55
+
56
+ Note: An earlier version of this gem used different names, `GH_TOKEN` & `GH_REPO`, it still works, but is not the
57
+ recommended approach anymore, see #2.
58
+
59
+ ### Step 4:
60
+
61
+ Run `til` from the command line
62
+
63
+ ## Future improvements
64
+
65
+ - An `init` command that will create a repo for you
66
+ - A `configure` command that will store the token and the repo name in a file in `~/.config/til/config` or `~/.til`
67
+
68
+ ## Known issues
69
+
70
+ The current version (0.0.5) deletes the temporary file before attempting to create the new commit, so if anything goes
71
+ wrong there, the content of the file will be lost.
72
+ This will be fixed soon, by only deleting the temporary file after the commit was created, but please keep that in mind
73
+ if you typed a long TIL and definitely don't want to lose its content.
data/bin/til CHANGED
@@ -1,5 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'til-rb'
4
+ require 'optparse'
4
5
 
5
- Til::Core.run
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = 'Usage: til [options]'
9
+
10
+ opts.on('-t', '--title=TITLE', 'Set the title from the command line') do |title|
11
+ options[:title] = title
12
+ end
13
+
14
+ opts.on('-h', '--help', 'Prints this help') do
15
+ puts opts
16
+ exit
17
+ end
18
+ end.parse!
19
+
20
+ Til::Core.run options: options
@@ -1,2 +1,3 @@
1
1
  require 'til/core'
2
+ require 'til/readme_updater'
2
3
  require 'til/version'
@@ -1,10 +1,14 @@
1
1
  require 'octokit'
2
2
  require 'tempfile'
3
+ require 'readline'
3
4
 
4
5
  module Til
5
6
  class Core
6
7
 
7
- def self.run
8
+ GH_TOKEN_ENV_VAR_NAME = 'TIL_RB_GITHUB_TOKEN'
9
+ GH_REPO_ENV_VAR_NAME = 'TIL_RB_GITHUB_REPO'
10
+
11
+ def self.run(options: {})
8
12
  # Exit if `fzf` is not available
9
13
  # Optionally print a spinner
10
14
  # Grab the list of existing categories
@@ -17,17 +21,18 @@ module Til
17
21
  # Create the new file
18
22
  # Create a new commit
19
23
  # Output a link to the file and the link to edit it
20
- til = new
24
+ til = new(options: options)
21
25
  til.run
22
26
  end
23
27
 
24
- def initialize(kernel: Kernel, process: Process, env: ENV, github_client: nil, stderr: $stderr)
28
+ def initialize(options: {}, kernel: Kernel, process: Process, env: ENV, github_client: nil, stderr: $stderr)
29
+ @options = options
25
30
  @kernel = kernel
26
31
  @process = process
27
32
  @env = env
28
33
  @stderr = stderr
29
34
  @github_client = github_client
30
- @repo_name = 'pjambet/til'
35
+ @repo_name = nil
31
36
  @new_category = false
32
37
  end
33
38
 
@@ -37,7 +42,10 @@ module Til
37
42
  check_environment_variables
38
43
  existing_categories = fetch_existing_categories
39
44
  selected_category = prompt_fzf(existing_categories)
40
- prepopulate_tempfile(selected_category)
45
+ if @new_category
46
+ selected_category = prompt_for_new_category
47
+ end
48
+ prepopulate_tempfile(selected_category, @options[:title])
41
49
  open_editor
42
50
  til_content = read_file
43
51
  commit_new_til(selected_category, til_content)
@@ -54,17 +62,25 @@ module Til
54
62
  end
55
63
 
56
64
  def check_environment_variables
57
- if @env['GH_TOKEN'].nil? || @env['GH_TOKEN'] == ''
58
- raise 'The GH_TOKEN (with the public_repo or repo scope) environment variable is required'
65
+ if @env[GH_TOKEN_ENV_VAR_NAME].nil? || @env[GH_TOKEN_ENV_VAR_NAME] == ''
66
+ if @env['GH_TOKEN'].nil? || @env['GH_TOKEN'] == ''
67
+ raise "The #{GH_TOKEN_ENV_VAR_NAME} (with the public_repo or repo scope) environment variable is required"
68
+ else
69
+ @stderr.puts "Using GH_TOKEN is deprecated, use #{GH_TOKEN_ENV_VAR_NAME} instead"
70
+ end
59
71
  end
60
72
 
61
- if (@env['VISUAL'].nil? || @env['VISUAL'] == '') && (@env['EDITOR'].nil? || @env['EDITOR'] == '')
62
- raise 'The VISUAL or EDITOR environment variables are required'
73
+ if @env[GH_REPO_ENV_VAR_NAME].nil? || @env[GH_REPO_ENV_VAR_NAME] == ''
74
+ if @env['GH_REPO'].nil? || @env['GH_REPO'] == ''
75
+ raise "The #{GH_REPO_ENV_VAR_NAME} environment variable is required"
76
+ else
77
+ @stderr.puts "Using GH_REPO is deprecated, use #{GH_REPO_ENV_VAR_NAME} instead"
78
+ end
63
79
  end
64
80
  end
65
81
 
66
82
  def fetch_existing_categories
67
- existing_categories = github_client.contents(@repo_name, path: '').filter do |c|
83
+ existing_categories = github_client.contents(repo_name, path: '').filter do |c|
68
84
  c['type'] == 'dir'
69
85
  end
70
86
 
@@ -76,7 +92,11 @@ module Til
76
92
  end
77
93
 
78
94
  def github_client
79
- @github_client ||= Octokit::Client.new(access_token: @env['GH_TOKEN'])
95
+ @github_client ||= Octokit::Client.new(access_token: @env[GH_TOKEN_ENV_VAR_NAME] || @env['GH_TOKEN'])
96
+ end
97
+
98
+ def repo_name
99
+ @repo_name ||= (@env[GH_REPO_ENV_VAR_NAME] || @env['GH_REPO'])
80
100
  end
81
101
 
82
102
  def prompt_fzf(categories)
@@ -99,6 +119,10 @@ module Til
99
119
  throw :exit
100
120
  end
101
121
 
122
+ def prompt_for_new_category
123
+ Readline.readline("New category > ").downcase
124
+ end
125
+
102
126
  def prepopulate_tempfile(selected_category, title = 'Title Placeholder')
103
127
  @tempfile = Tempfile.new('til.md')
104
128
  @tempfile.write("# #{title}")
@@ -108,7 +132,7 @@ module Til
108
132
  end
109
133
 
110
134
  def open_editor
111
- editor = ENV['VISUAL'] || ENV['EDITOR']
135
+ editor = ENV['VISUAL'] || ENV['EDITOR'] || 'vi'
112
136
  system(*editor.split, @tempfile.path)
113
137
  end
114
138
 
@@ -118,22 +142,26 @@ module Til
118
142
  content
119
143
  end
120
144
 
145
+ def new_filename(commit_title)
146
+ today = Time.now.strftime '%Y-%m-%d'
147
+ name = CGI.escape(commit_title.split.map(&:downcase).join('-'))
148
+ "#{today}_#{name}.md"
149
+ end
150
+
121
151
  def commit_new_til(category, content)
122
152
  commit_title = content.lines[0].chomp
123
153
  if commit_title.start_with?('#')
124
154
  commit_title = commit_title[1..].strip
125
155
  end
126
- today = Time.now.strftime '%Y-%m-%d'
127
- name = commit_title.split.map(&:downcase).join('-')
128
- filename = "#{today}_#{name}.md"
156
+ filename = new_filename(commit_title)
129
157
 
130
- ref = github_client.ref @repo_name, 'heads/master'
131
- commit = github_client.commit @repo_name, ref.object.sha
132
- tree = github_client.tree @repo_name, commit.commit.tree.sha, recursive: true
133
- readme = github_client.readme @repo_name
158
+ ref = github_client.ref repo_name, 'heads/master'
159
+ commit = github_client.commit repo_name, ref.object.sha
160
+ tree = github_client.tree repo_name, commit.commit.tree.sha, recursive: true
161
+ readme = github_client.readme repo_name
134
162
  readme_content = Base64.decode64 readme.content
135
163
 
136
- blob = github_client.create_blob @repo_name, content
164
+ blob = github_client.create_blob repo_name, content
137
165
  blobs = tree.tree.filter { |object|
138
166
  object[:type] == 'blob' && object[:path] != 'README.md'
139
167
  }.map { |object|
@@ -141,42 +169,26 @@ module Til
141
169
  }
142
170
 
143
171
  updated_readme_content = update_readme_content(category, commit_title, filename, readme_content)
144
- new_readme_blob = github_client.create_blob @repo_name, updated_readme_content
172
+ new_readme_blob = github_client.create_blob repo_name, updated_readme_content
145
173
  blobs << { path: 'README.md', mode: '100644', type: 'blob', sha: new_readme_blob }
146
174
 
147
175
  blobs << { path: "#{category}/#{filename}", mode: '100644', type: 'blob', sha: blob }
148
176
 
149
- tree = github_client.create_tree @repo_name, blobs
150
- commit = github_client.create_commit @repo_name, commit_title, tree.sha, ref.object.sha
151
- github_client.update_ref @repo_name, 'heads/master', commit.sha
177
+ tree = github_client.create_tree repo_name, blobs
178
+ commit = github_client.create_commit repo_name, commit_title, tree.sha, ref.object.sha
179
+ github_client.update_ref repo_name, 'heads/master', commit.sha
152
180
 
153
- puts "You can see your new TIL at : https://github.com/pjambet/til/blob/master/#{category}/#{filename}"
154
- puts "You can edit your new TIL at : https://github.com/pjambet/til/edit/master/#{category}/#{filename}"
181
+ puts "You can see your new TIL at : https://github.com/#{repo_name}/blob/master/#{category}/#{filename}"
182
+ puts "You can edit your new TIL at : https://github.com/#{repo_name}/edit/master/#{category}/#{filename}"
155
183
  end
156
184
 
157
185
  def update_readme_content(category, commit_title, filename, readme_content)
158
- beginning = readme_content.index('### Categories') + '### Categories'.length
159
- eend = readme_content.index('---', readme_content.index('---') + 1) - 1
160
-
161
- # [["[Git](#git)", "Git", "git"], ["[Qux](#qux)", "Qux", "qux"]]
162
- categories = readme_content[beginning..eend].scan(/(\[(\w+)\]\(#(\w+)\))/)
186
+ updater = Til::ReadmeUpdater.new(readme_content)
163
187
 
164
188
  if @new_category
189
+ updater.add_item_for_new_category(category, commit_title, filename)
165
190
  else
166
- existing_cat = categories.find { |c| c[2] == category }
167
-
168
- loc_in_page = readme_content.index("### #{existing_cat[1]}")
169
- next_cat_location = readme_content.index('###', loc_in_page + 1)
170
-
171
- new_line = "- [#{commit_title}](#{category}/#{filename})"
172
- new_readme_content = ''
173
- if next_cat_location
174
- breakpoint = next_cat_location - 2
175
- new_readme_content = readme_content[0..breakpoint] + new_line + readme_content[breakpoint..]
176
- else
177
- new_readme_content = readme_content + new_line + '\n'
178
- end
179
- new_readme_content
191
+ updater.add_item_for_existing_category(category, commit_title, filename)
180
192
  end
181
193
  end
182
194
 
@@ -0,0 +1,85 @@
1
+ module Til
2
+ class ReadmeUpdater
3
+
4
+ def initialize(initial_content)
5
+ @initial_content = initial_content
6
+ end
7
+
8
+ def add_item_for_existing_category(category, item_title, filename)
9
+ beginning = @initial_content.index('### Categories') + '### Categories'.length
10
+ eend = @initial_content.index('---', @initial_content.index('---') + 1) - 1
11
+
12
+ # [["[Git](#git)", "Git", "git"], ["[Qux](#qux)", "Qux", "qux"]]
13
+ categories = @initial_content[beginning..eend].scan(/(\[(\w+)\]\(#(\w+)\))/)
14
+
15
+ existing_cat = categories.find { |c| c[2] == category }
16
+
17
+ loc_in_page = @initial_content.index("### #{existing_cat[1]}")
18
+ next_cat_location = @initial_content.index('###', loc_in_page + 1)
19
+
20
+ new_line = "- [#{item_title}](#{category}/#{filename})"
21
+ new_readme_content = ''
22
+ if next_cat_location
23
+ breakpoint = next_cat_location - 2
24
+ new_readme_content = @initial_content[0..breakpoint] + new_line + @initial_content[breakpoint..]
25
+ else
26
+ new_readme_content = @initial_content + new_line + "\n"
27
+ end
28
+ new_readme_content
29
+ end
30
+
31
+ def add_item_for_new_category(category, item_title, filename)
32
+ # TODO: We'll need some form of validation on the category name
33
+ beginning = @initial_content.index('### Categories') + '### Categories'.length
34
+ first_dashdashdash = @initial_content.index('---')
35
+ eend = @initial_content.index('---', first_dashdashdash + 1) - 1
36
+
37
+ # [["[Git](#git)", "Git", "git"], ["[Qux](#qux)", "Qux", "qux"]]
38
+ categories = @initial_content[beginning..eend].scan(/(\[(\w+)\]\(#(\w+)\))/)
39
+
40
+ insert_at = categories.bsearch_index do |category_triplet|
41
+ category_triplet[2] >= category
42
+ end
43
+
44
+ if insert_at.nil?
45
+ # It's the last category
46
+ insert_at = categories.length
47
+ end
48
+
49
+ categories.insert(insert_at, ["[#{category.capitalize}](\##{category})", category.capitalize, category])
50
+
51
+ new_categories_formatted = categories.map do |category|
52
+ "* #{category[0]}"
53
+ end.join("\n")
54
+
55
+ new_categories_formatted.prepend("### Categories\n\n")
56
+
57
+ category_sections_found = 0
58
+ current_search_index = eend + 1 + 3
59
+
60
+ while category_sections_found < insert_at
61
+ current_search_index = @initial_content.index('###', current_search_index + 1)
62
+ category_sections_found += 1
63
+ end
64
+
65
+ next_bound = @initial_content.index('###', current_search_index + 1)
66
+
67
+ new_line = "- [#{item_title}](#{category}/#{filename})"
68
+
69
+ if next_bound
70
+ new_readme_content = @initial_content[0..(first_dashdashdash + 2)] \
71
+ + "\n\n#{new_categories_formatted}\n" \
72
+ + @initial_content[eend..(next_bound - 2)] \
73
+ + "\n### #{category.capitalize}\n\n#{new_line}\n\n" \
74
+ + @initial_content[next_bound..]
75
+ else
76
+ new_readme_content = @initial_content[0..(first_dashdashdash + 2)] \
77
+ + "\n\n#{new_categories_formatted}\n" \
78
+ + @initial_content[eend..] \
79
+ + "\n### #{category.capitalize}\n\n#{new_line}\n"
80
+ end
81
+
82
+ new_readme_content
83
+ end
84
+ end
85
+ end
@@ -1,5 +1,5 @@
1
1
  module Til
2
2
 
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.7'
4
4
 
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: til-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Jambet
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.11.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: timecop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.1
41
55
  description: til-rb helps you manage a repo of TILs similar to https://github.com/jbranchaud/til
42
56
  email: hello@pjam.me
43
57
  executables:
@@ -50,8 +64,9 @@ files:
50
64
  - bin/til
51
65
  - lib/til-rb.rb
52
66
  - lib/til/core.rb
67
+ - lib/til/readme_updater.rb
53
68
  - lib/til/version.rb
54
- homepage: https://rubygems.org/gems/til-rb
69
+ homepage: https://github.com/pjambet/til-rb/
55
70
  licenses:
56
71
  - MIT
57
72
  metadata: {}