til-rb 0.0.2 → 0.0.7

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 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: {}