Initial commit
commit
c5fe900f96
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -0,0 +1,2 @@
|
|||
vendor
|
||||
.env
|
|
@ -0,0 +1,4 @@
|
|||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.5
|
|
@ -0,0 +1,30 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2018-06-01 00:51:49 +0530 using RuboCop version 0.56.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 1
|
||||
Metrics/AbcSize:
|
||||
Max: 24
|
||||
|
||||
# Offense count: 2
|
||||
# Configuration parameters: CountComments.
|
||||
Metrics/MethodLength:
|
||||
Max: 14
|
||||
|
||||
# Offense count: 2
|
||||
Style/Documentation:
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'test/**/*'
|
||||
- 'app.rb'
|
||||
- 'opml.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||
# URISchemes: http, https
|
||||
Metrics/LineLength:
|
||||
Max: 87
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'nokogiri', '~> 1.8'
|
||||
gem 'octokit', '~> 4.0'
|
||||
gem 'redis', '~> 4.0'
|
||||
gem 'sinatra', '~> 2.0'
|
||||
gem 'sinatra-contrib', '~> 2.0'
|
||||
gem 'dotenv'
|
|
@ -0,0 +1,52 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
backports (3.11.3)
|
||||
dotenv (2.4.0)
|
||||
faraday (0.15.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
mini_portile2 (2.3.0)
|
||||
multi_json (1.13.1)
|
||||
multipart-post (2.0.0)
|
||||
mustermann (1.0.2)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
public_suffix (3.0.2)
|
||||
rack (2.0.5)
|
||||
rack-protection (2.0.1)
|
||||
rack
|
||||
redis (4.0.1)
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
sinatra (2.0.1)
|
||||
mustermann (~> 1.0)
|
||||
rack (~> 2.0)
|
||||
rack-protection (= 2.0.1)
|
||||
tilt (~> 2.0)
|
||||
sinatra-contrib (2.0.1)
|
||||
backports (>= 2.0)
|
||||
multi_json
|
||||
mustermann (~> 1.0)
|
||||
rack-protection (= 2.0.1)
|
||||
sinatra (= 2.0.1)
|
||||
tilt (>= 1.3, < 3)
|
||||
tilt (2.0.8)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
dotenv
|
||||
nokogiri (~> 1.8)
|
||||
octokit (~> 4.0)
|
||||
redis (~> 4.0)
|
||||
sinatra (~> 2.0)
|
||||
sinatra-contrib (~> 2.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
require 'dotenv/load'
|
||||
require 'sinatra/base'
|
||||
require 'sinatra/reloader'
|
||||
require 'octokit'
|
||||
require 'redis'
|
||||
require 'json'
|
||||
require './opml'
|
||||
|
||||
Octokit.auto_paginate = true
|
||||
|
||||
class MyApp < Sinatra::Base
|
||||
configure :development do
|
||||
register Sinatra::Reloader
|
||||
also_reload 'opml.rb'
|
||||
end
|
||||
configure do
|
||||
set :r, Redis.new
|
||||
set :client, Octokit::Client.new(
|
||||
client_id: ENV.fetch('GITHUB_CLIENT_ID'),
|
||||
client_secret: ENV.fetch('GITHUB_CLIENT_SECRET'),
|
||||
per_page: 100,
|
||||
auto_traversal: true,
|
||||
auto_paginate: true
|
||||
)
|
||||
end
|
||||
|
||||
def get_starred_repos_from_cache(user)
|
||||
from_cache = settings.r.get "#{user}.repos"
|
||||
if from_cache
|
||||
time = settings.r.get "#{user}.repos.time" || Time.now.to_i
|
||||
return [JSON.parse(from_cache), time]
|
||||
end
|
||||
repos = []
|
||||
starred = settings.client.starred(user)
|
||||
|
||||
starred.each do |repo|
|
||||
repos.push repo[:full_name]
|
||||
end
|
||||
|
||||
time = Time.now.to_i
|
||||
settings.r.set "#{user}.repos", repos
|
||||
settings.r.set "#{user}.repos.time", time
|
||||
[repos, time]
|
||||
end
|
||||
|
||||
get '/github/:user/starred.opml' do
|
||||
user = params[:user]
|
||||
repos, time = get_starred_repos_from_cache(user)
|
||||
opml = OPML.new do
|
||||
@title = "Releases: #{user}/starred"
|
||||
@date_created = Time.new(time.to_i).rfc822
|
||||
@date_modified = Time.now.rfc822
|
||||
@owner_name = user
|
||||
end
|
||||
repos.each do |r|
|
||||
title = "Release notes from #{r}"
|
||||
html_url = "https://github.com/#{r}/releases"
|
||||
rss_url = "#{html_url}.atom"
|
||||
opml.add_outline(
|
||||
text: title, description: "#{r}/releases", html_url: html_url,
|
||||
xml_url: rss_url, title: title,
|
||||
type: 'rss', version: 'RSS2'
|
||||
)
|
||||
end
|
||||
|
||||
filename = "#{user}-starred-releases.opml"
|
||||
|
||||
response.headers['Content-Disposition'] = "attachment; filename=#{filename};"
|
||||
response.headers['Content-Type'] = 'application/octet-stream'
|
||||
response.headers['Content-Transfer-Encoding'] = 'binary'
|
||||
|
||||
opml.xml
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require './app'
|
||||
run MyApp
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'nokogiri'
|
||||
|
||||
class OPML
|
||||
PROPERTIES = %w[title date_created date_modified
|
||||
owner_name owner_email docs].freeze
|
||||
|
||||
attr_accessor :title, :date_created, :date_modified, :owner_name, :owner_email, :docs
|
||||
|
||||
def initialize(&block)
|
||||
instance_eval(&block)
|
||||
@docs = 'http://dev.opml.org/spec2.html'
|
||||
@outlines = []
|
||||
end
|
||||
|
||||
def camel_case(str)
|
||||
str.downcase.split('_').each_with_index.map do |v, i|
|
||||
i.zero? ? v : v.capitalize
|
||||
end.join
|
||||
end
|
||||
|
||||
def add_outline(params)
|
||||
params = Hash[params.map { |k, v| [camel_case(k.to_s), v] }]
|
||||
@outlines.push params
|
||||
end
|
||||
|
||||
def add_note_with_params(xml, attrs)
|
||||
attrs.each do |attr|
|
||||
val = send(attr.to_sym)
|
||||
key = camel_case(attr).to_sym
|
||||
xml.send(key, val) if val
|
||||
end
|
||||
end
|
||||
|
||||
def xml
|
||||
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
||||
xml.opml(version: '2.0') do
|
||||
xml.head do
|
||||
add_note_with_params xml, PROPERTIES
|
||||
end
|
||||
|
||||
xml.body do
|
||||
xml.outline text: 'GitHub Releases' do
|
||||
@outlines.each do |outline|
|
||||
xml.outline outline
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end.to_xml
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue