Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User repos caching improvements #18

Merged
merged 3 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ You can also check the [releases history](https://github.com/edgarjs/alfred-gith

---

## Actions
## Authentication

1. Call `gh-token` to generate personal access token.
2. Login by calling the `gh-login <email> <token>` action.
3. Search any repository by calling the `gh <term>` action.

## Usage

1. Search your repositories by calling the `gh <term>` action.
2. Search all repositories by calling the `gha <term>` action.
3. Your repositories are cached and updated every 24 hours. To force re-download cache use `gh-reset-cache` action.

### Other Actions

Expand Down
1 change: 0 additions & 1 deletion bin/github-repos
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# frozen_string_literal: true

require_relative '../lib/github_repos'
require 'github/repo_formatter'
require 'github/authorization'
require 'json'

Expand Down
2 changes: 1 addition & 1 deletion info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
<key>queuedelayimmediatelyinitially</key>
<false/>
<key>queuedelaymode</key>
<integer>1</integer>
<integer>0</integer>
<key>queuemode</key>
<integer>1</integer>
<key>runningsubtext</key>
Expand Down
59 changes: 44 additions & 15 deletions lib/github/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'json'
require 'uri'
require 'cgi'
require 'github/repo'
require 'github/authorization'
require 'local_storage'
require 'config_path'
Expand All @@ -14,6 +15,10 @@ class Api
DEFAULT_HOST = 'https://api.github.com'
HOST_FILE_NAME = 'host'
CACHE_FILE_NAME = 'cache'
CACHE_TIMESTAMP_FILE_NAME = 'cache-timestamp'

# Remember to update Readme if changed.
CACHE_LIFETIME = 86_400 # 24 hours

# 100 is maximum items per page
LIST_USER_REPOS_PATH = '/user/repos?per_page=100'
Expand All @@ -22,21 +27,24 @@ def search_all_repos(query)
path = "/search/repositories?q=#{escape(query)}"

repos = get(path)[:body][:items]
Github::RepoFormatter.new(repos).to_formatted_hash

repos.map do |i|
Repo.from_api_response(i).to_alfred_hash
end
end

def search_repos(query)
query_downcase = query.downcase

repos = cached_repos
repos = read_cached_repos
repos = reset_cache if repos.empty?

repos_filtered = repos.filter do |i|
title_downcase = i[:title].downcase
title_downcase = i.name.downcase
title_downcase.include?(query_downcase)
end

repos_filtered
repos_filtered.map(&:to_alfred_hash)
end

def reset_cache
Expand All @@ -45,14 +53,15 @@ def reset_cache

until next_page.nil?
response = get(LIST_USER_REPOS_PATH + "&page=#{next_page}")
repos.push(*response[:body])
repos_from_response = response[:body].map do |i|
Repo.from_api_response(i)
end
repos.push(*repos_from_response)
next_page = response[:next_page]
end

repos_formatted = Github::RepoFormatter.new(repos).to_formatted_hash

save_repos_to_disk(repos_formatted)
repos_formatted
save_repos_to_disk(repos)
repos
end

class << self
Expand Down Expand Up @@ -126,15 +135,35 @@ def get_next_page(header)
header.split(',').map { |i| i[regex, 1] }.find(&:itself)
end

def cache_storage
@cache_path ||= ConfigPath.new(CACHE_FILE_NAME)
@cache_storage ||= LocalStorage.new(@cache_path.get, serialize: false)
end

def cache_timestamp_storage
@cache_timestamp_path ||= ConfigPath.new(CACHE_TIMESTAMP_FILE_NAME)
@cache_timestamp_storage ||=
LocalStorage.new(@cache_timestamp_path.get, serialize: false)
end

def save_repos_to_disk(repos)
cache = LocalStorage.new(ConfigPath.new(CACHE_FILE_NAME).get)
cache.put(repos.to_json)
content = repos.map(&:to_storage_string).join("\n")
cache_storage.put(content)
cache_timestamp_storage.put(Time.now.to_i)
end

def read_cache_timestamp
cache_timestamp = cache_timestamp_storage.get
cache_timestamp.nil? ? 0 : cache_timestamp.to_i
end

def cached_repos
cache_path = ConfigPath.new(CACHE_FILE_NAME).get
cache_string = LocalStorage.new(cache_path).get
cache_string.nil? ? [] : JSON.parse(cache_string, symbolize_names: true)
def read_cached_repos
cache_string = cache_storage.get
cache_expired = (read_cache_timestamp + CACHE_LIFETIME) < Time.now.to_i

return [] if cache_string.nil? || cache_expired

cache_string.split("\n").map { |i| Repo.from_storage_string(i) }
end
end
end
41 changes: 41 additions & 0 deletions lib/github/repo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require 'json'

module Github
class Repo
attr_reader :name, :link

def initialize(name, link)
@name = name
@link = link
end

def to_storage_string
"#{@name},#{@link}"
end

def to_alfred_hash
{
title: @name,
subtitle: @link,
arg: @link,
text: {
copy: @link,
largetype: @link
}
}
end

class << self
def from_storage_string(storage_string)
name, link = storage_string.split(',')
new(name, link)
end

def from_api_response(api_response)
new(api_response[:full_name], api_response[:html_url])
end
end
end
end
31 changes: 0 additions & 31 deletions lib/github/repo_formatter.rb

This file was deleted.

9 changes: 6 additions & 3 deletions lib/local_storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ class LocalStorage
attr_reader :location

# @param [String] filepath
def initialize(filepath)
def initialize(filepath, serialize: true)
@location = File.expand_path(filepath)
@serialize = serialize
create_parent_dir
end

def put(object)
File.open(location, 'w') do |f|
f.puts serialize(object)
to_insert = @serialize ? serialize(object) : object
f.puts to_insert
end

object
Expand All @@ -23,7 +25,8 @@ def put(object)
def get
return unless exists?

deserialize File.read(location)
raw_content = File.read(location)
@serialize ? deserialize(raw_content) : raw_content
end

private
Expand Down