-
-
Save tarao/224325 to your computer and use it in GitHub Desktop.
HatenaDiaryFS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
require 'rubygems' | |
require 'pit' | |
require 'fusefs' | |
require 'atomutil' | |
require 'time' | |
module REXML | |
class Text | |
def clone | |
# bug fix: prevent entities from beeing escaped again | |
return Text.new(@string, true, nil, @raw) | |
end | |
end | |
end | |
class Time | |
ENTRY_FORMAT = '%Y-%m-%dT%H:%M:%S' | |
def to_entry_name | |
return strftime(ENTRY_FORMAT) | |
end | |
def self.entry_name(name) | |
return Time.iso8601(name) | |
end | |
end | |
module Atompub | |
class HatenaClient < Client | |
XMLNS = 'http://www.hatena.ne.jp/info/xmlns#' | |
DIARY_ATOM = 'http://d.hatena.ne.jp/%s/atom' | |
def publish_entry(uri) | |
@hatena_publish = true | |
update_resource(uri, ' ', Atom::MediaType::ENTRY.to_s) | |
ensure | |
@hatena_publish = false | |
end | |
def get_body(entry) | |
get_resource(entry.edit_link) | |
begin | |
return @rc.get(XMLNS, 'syntax').text | |
rescue | |
return @rc.content.body | |
end | |
end | |
def remove_entry(entry) | |
delete_entry(entry.edit_link) | |
end | |
def write_entry(col, entry, title, body, update) | |
if entry | |
entry.title = title | |
entry.content = Atom::Content.new(:body => body, :type => 'text') | |
update_entry(entry.edit_link, entry) | |
else | |
entry = Atom::Entry.new(:title => title, | |
:content => body, | |
:updated => update) | |
create_entry(col, entry) | |
end | |
end | |
private | |
def set_common_info(req) | |
req['X-Hatena-Publish'] = 1 if @hatena_publish | |
super(req) | |
end | |
end | |
end | |
class HatenaDiaryFS < FuseFS::MetaDir | |
def initialize(config) | |
super() | |
@config = config | |
auth = Atompub::Auth::Wsse.new(config) | |
@client = Atompub::HatenaClient.new(:auth => auth) | |
service_uri = Atompub::HatenaClient::DIARY_ATOM % config[:username] | |
@service = @client.get_service(service_uri) | |
@collections = {} | |
@service.workspace.collections.each do |col| | |
colname = File.basename(col.href) | |
mkdir(File.join('', colname), Collection.new(col.href, @client)) | |
end | |
end | |
class Collection | |
CREATE_PATH = 'create' | |
def initialize(collection, client) | |
@collection = collection | |
@client = client | |
end | |
def feed | |
return @client.get_feed(@collection) | |
end | |
def entries | |
return @entries ||= feed.entries | |
end | |
def find_entry(name) | |
name = name.to_entry_name if name.kind_of?(Time) | |
return entries.find do |e| | |
e.updated.to_entry_name == name | |
end | |
end | |
def contents(path) | |
return [CREATE_PATH] + (entries.map{|e| e.updated.to_entry_name} || []) | |
end | |
def file?(path) | |
return path == CREATE_PATH || !!find_entry(path) | |
end | |
def read_file(path) | |
begin | |
e = find_entry(path) | |
if e | |
return ['* ' + e.title, @client.get_body(e)].join("\n\n") | |
else | |
return '' | |
end | |
rescue Exception => e | |
puts(['read error', e, e.backtrace]) | |
end | |
end | |
def can_delete?(path) | |
true | |
end | |
def delete(path) | |
begin | |
entry = find_entry(path) | |
@client.remove_entry(entry) | |
@entries = nil | |
rescue Exception => e | |
puts(['delete error', e, e.backtrace].flatten) | |
end | |
end | |
def can_write?(path) | |
begin | |
return !!(path == CREATE_PATH || Time.entry_name(path)) | |
rescue ArgumentError | |
return false | |
end | |
end | |
def write_to(path, body) | |
begin | |
title, body = body.split("\n", 2) | |
if title && body | |
title = title.sub(/^\*\s*/, '') | |
body = body.sub(/^\n+/, '') | |
entry = find_entry(path) | |
u = (path == CREATE_PATH) ? Time.now : Time.entry_name(path) | |
@client.write_entry(@collection, entry, title, body, u) | |
@entries = nil | |
end | |
rescue Exception => e | |
puts(['write error', e, e.backtrace].flatten) | |
end | |
end | |
end | |
end | |
if ARGV.length < 1 | |
puts("Usage: #{File.basename($0)} mountpoint") | |
exit | |
end | |
config = Pit.get('hatena', :require => { | |
:username => "your Hatena ID", | |
:password => "your Hatena password" | |
}) | |
fs = HatenaDiaryFS.new(config) | |
FuseFS.set_root(fs) | |
FuseFS.mount_under(ARGV.first) | |
FuseFS.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment