Browse Source

Initial commit

master
Nemo 1 year ago
commit
c5fe900f96
9 changed files with 239 additions and 0 deletions
  1. 9
    0
      .editorconfig
  2. 2
    0
      .gitignore
  3. 4
    0
      .rubocop.yml
  4. 30
    0
      .rubocop_todo.yml
  5. 10
    0
      Gemfile
  6. 52
    0
      Gemfile.lock
  7. 75
    0
      app.rb
  8. 4
    0
      config.ru
  9. 53
    0
      opml.rb

+ 9
- 0
.editorconfig View File

@@ -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

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
vendor
.env

+ 4
- 0
.rubocop.yml View File

@@ -0,0 +1,4 @@
inherit_from: .rubocop_todo.yml

AllCops:
TargetRubyVersion: 2.5

+ 30
- 0
.rubocop_todo.yml View File

@@ -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

+ 10
- 0
Gemfile View File

@@ -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'

+ 52
- 0
Gemfile.lock View File

@@ -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

+ 75
- 0
app.rb View File

@@ -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

+ 4
- 0
config.ru View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true

require './app'
run MyApp

+ 53
- 0
opml.rb View File

@@ -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…
Cancel
Save