はてなキーワード: ADDとは
比較的問題の全体に対する影響度が低い「改善案」だと私が思ったものは、省いている場合があります。
細かい改善提案などについても、いつかまとめようと思います。(参考:http://i.hatena.ne.jp/t/UI%E6%94%B9%E5%96%84)
1.動作不具合と見込まれるもの
1.1.半角スペースが含まれるキーワードページが正常に表示されない
「iPod touch」というキーワードにアクセスしようとすると、「iPod」に飛びます。
発生するものと、発生しないものがあるようです。アイデアにて検討中ステータスです。
関連アイデア:http://i.hatena.ne.jp/idea/24207
1.2.1.URLキーワードは、キーワード表示が文字化けすることがある
主題の通りです。
関連アイデア:http://i.hatena.ne.jp/idea/24215
1.2.2.URLキーワードが、タイトル名で検索したときヒットしない
主題の通りです。
関連アイデア:http://i.hatena.ne.jp/idea/24509
http://i.hatena.ne.jp/idea/18718
1.3.お絵かきモード時、キャンパスリサイズが制御不能になることがある
「ウィンドウうにょーん現象」として、多くのお絵かきハイクユーザを苦しめている不具合です。
「絵を描く」→「狭くなってきたので下に枠を広げる」→「絵の続きを描こうとする」
→「下の枠が上に引きずられてウィンドウが小さくなる」→「絵の下半分が削られる」
関連アイデア:http://i.hatena.ne.jp/idea/18837
http://i.hatena.ne.jp/idea/20483
2.不具合ではないが、解決されるべきと考えられるもの
mobileからアクセスしているユーザの「誤エントリを消せない」という戸惑いの声がたまに見られます。
関連アイデア:http://i.hatena.ne.jp/idea/18629
http://i.hatena.ne.jp/idea/21943
2.2.過去のエントリを遡って閲覧することが困難、検索もできない
エントリを時系列どおりに閲覧することが現在のGUIでは簡単にできません。
関連アイデア:http://i.hatena.ne.jp/idea/18807
http://i.hatena.ne.jp/idea/18469
http://i.hatena.ne.jp/idea/20578
主題の通りです。
関連アイデア:http://i.hatena.ne.jp/idea/21587
http://i.hatena.ne.jp/idea/18644
http://i.hatena.ne.jp/idea/23919
http://i.hatena.ne.jp/idea/21089
2.4.PC版とモバイル版で各種タブの表示位置、リンク先が不統一
主題の通りです。他にも、タブの表示位置などは不統一な点が多くあります。
関連アイデア:http://i.hatena.ne.jp/idea/22471
http://i.hatena.ne.jp/idea/20671
http://i.hatena.ne.jp/idea/19119
2.5.Fanが大量にいるユーザの、Fans表示の量が多すぎる
省略表示されないので、Fanが大量にいるユーザのEntriesページが、
関連アイデア:http://i.hatena.ne.jp/idea/21356
2.6.エントリ欄の使い方がわかり難い
入力欄に説明も何もないため、初めてハイクを閲覧するユーザがRecent Entriesを見たときに、
入力欄に説明を書き、フォームをフォーカスすると文字列が消えるなどのサポートが必要と見込まれます。
参考ページ:http://h.hatena.ne.jp/invite
関連アイデア:http://i.hatena.ne.jp/idea/24335
3.不具合ではないが、検討してみるべきと考えられるもの
3.1.閲覧環境の整理に関するもの
3.1.1.見たくないキーワードがある
興味のないキーワードや、見たくないキーワード等を非表示にしたいという要望です。
Fx+GreaseMonkeyで実現されていますが、mobileや他ブラウザでは適用されませんし、
基本機能としての実装検討が期待されます。
関連アイデア:http://i.hatena.ne.jp/idea/21160
詳細は「3.1.1.見たくないキーワードがある」と同様です。
関連アイデア:http://i.hatena.ne.jp/idea/24495
3.2.1.お気に入り登録を制御したい
自分に対するお気に入り登録を「ストーキング」されていると感じる場合があるようです。
登録された際に拒否できるようにするなどの対応が考えられます。
関連アイデア:http://i.hatena.ne.jp/idea/21300
http://i.hatena.ne.jp/idea/21391
http://i.hatena.ne.jp/idea/23918
http://i.hatena.ne.jp/idea/21179
http://i.hatena.ne.jp/idea/18295
3.2.2.Replyを制御したい
「3.2.1.お気に入り登録を制御したい」と方向性は同じです。
執拗にReplyされるなどの行為が「ストーキング」されていると感じる場合があるようです。
特定ユーザからのReplyを拒否できるようにするなどの対応が考えられます。
関連アイデア:http://i.hatena.ne.jp/idea/23762
http://i.hatena.ne.jp/idea/19451
3.3.ユーザをお気に入り指定した際、対象のユーザのAboutがお気に入りキーワード扱いになる。
仕様か不具合か不明です。少々、不自然な動作であると考えられます。
関連アイデア:http://i.hatena.ne.jp/idea/20725
仕様か不具合か不明です。少々、不自然な動作であると考えられます。
関連アイデア:http://i.hatena.ne.jp/idea/23776
本来キーワードとして指定できる文字列は「255バイトまで」となっていて、
エントリ時にキーワードを指定する場合は、超過分はカットされます。
しかし、関連キーワードにキーワードを追加する際は、この制限が適用されません。
結果、「関連キーワードに長大なキーワードをAddする」→「そのリンクをクリック」→
「長大なキーワードページでエントリ」とすることで、長大なキーワードを指定してエントリすることができます。
迷惑行為に利用することができる点で問題視されます。
関連アイデア:http://i.hatena.ne.jp/idea/20722
http://i.hatena.ne.jp/idea/20545
画像は過去にある昆虫の画像が大量に張られる事態に際し、1エントリには3枚までとなっています。
しかし、動画の数には制限がなく、迷惑行為に利用することができる点で問題視されます。
関連アイデア:http://i.hatena.ne.jp/idea/20981
4.現在の利用状況に関連するもの
4.1.RecentEntriesページに関するもの
4.1.1.過度長文・動画・画像の連投によってRecentEntriesが使い辛くなる
RecentEntriesを中心に閲覧しているユーザはかなり多いようです。
このため、一部のユーザ(&BOT)が大量にエントリをすると、そういったユーザにとって
ハイクの閲覧が困難・視界に入るうものの多様性の喪失となり、不満を抱く原因になるようです。
非表示機能の充実、RecentEntriesをTOPページにしない、などの対応が考えられます。
関連アイデア:http://i.hatena.ne.jp/idea/21136
http://i.hatena.ne.jp/idea/19817
疲れたので一旦ここまで……気が向いたら思いつき次第追記します。
自分のための覚え書き。知ってる人からすれば全然当たり前のことかも…
IIS上で、Visual Studio 2008で開発したASP.NETなWebサイトを動かす時に、SQLiteを使う方法
IISの動作しているマシンに「SQLite for ADO.NET 2.0」をインストール。
それだけで良いと思ったけれど、以下のエラーが発生。
ArgumentException: 要求された .Net Framework データ プロバイダが見つかりません。これは、インストールされていない可能性があります。
いろいろ調べたら、手動でmachine.configを書き換える必要があるみたい。
<system.data> <DbProviderFactories> <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.61.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /> </DbProviderFactories> </system.data>
無事に動くようになりました。
img_passがimg_pathの間違いなんじゃないか、と思う以外は別にそんなに分かりにくいと思わないけどなぁ。
俺も同じようにすると思う。
Thumbnail, thumbnail, thumbnailsが紛らわしいと思うなら、
thumb_array = array.new(); while(img_path = gets()) thumb = Thumbnail.new(img_path) thumb_array.add(thumb) end
とすればよろし。
ギークではないし、どちらかといえば下手なのですが。
http://anond.hatelabo.jp/20090324005857
例えば、サムネイルを作る makeThumbnails という関数なら、
result = array.new() while(src = gets()) result.add(Thumbnail.new(src)) end return result
かもしれないし、例えばサムネイルを一覧表示時のアイコンに使うなら、index_iconsとか、gif化してアニメーションするならframesとかつけるかも。
ちなみに、perlなら $frame = shift @frame だ。
Deploy Merb, Sinatra, or any Rack App to Heroku
http://blog.heroku.com/archives/2009/3/5/32_deploy_merb_sinatra_or_any_rack_app_to_heroku/
http://heroku.com/pages/quickstart
HerokuをGit経由で使ってみる
http://d.hatena.ne.jp/aki-s-119/20081110/1226335713
http://github.com/guides/using-git-and-github-for-the-windows-for-newbies
http://d.hatena.ne.jp/kusakari/20080715/1216091060
http://code.google.com/p/msysgit/
http://net-newbie.com/putty.html
>heroku help === General Commands help # show this usage list # list your apps create [<name>] # create a new app keys # show your user's public keys keys:add [<path to keyfile>] # add a public key keys:remove <keyname> # remove a key by name (user@host) keys:clear # remove all keys === App Commands (execute inside a checkout directory) info # show app info, like web url and git repo open # open the app in a web browser rename <newname> # rename the app sharing:add <email> # add a collaborator sharing:remove <email> # remove a collaborator domains:add <domain> # add a custom domain name domains:remove <domain> # remove a custom domain name domains:clear # remove all custom domains rake <command> # remotely execute a rake command console <command> # remotely execute a single console command console # start an interactive console to the remote restart # restart app servers logs # fetch recent log output for debugging logs:cron # fetch cron log output bundles # list bundles for the app bundles:capture [<bundle>] # capture a bundle of the app's code and dat bundles:download # download most recent app bundle as a tarba bundles:download <bundle> # download the named bundle bundles:animate <bundle> # animate a bundle into a new app bundles:destroy <bundle> # destroy the named bundle destroy # destroy the app permanently === Example story: rails myapp cd myapp (...make edits...) git init git add . git commit -m "my new app" heroku create myapp git remote add heroku git@heroku.com:myapp.git git push heroku master
そういえばinheritなんてものがあるのを忘れてた。
// ==UserScript== // @name add style // @namespace http://anond.hatelabo.jp/ // @include http://anond.hatelabo.jp/* // ==/UserScript== GM_addStyle( "font{color:inherit;font-size:inherit}"+ "u, s, strike{text-decoration:inherit}"+ "b{font-weight:inherit}"+ ".section{max-height:80em;overflow-y:auto}" );
firefoxオンリーだそうで http://anond.hatelabo.jp/20090120114819 ユーザースタイルシート的には
font{color:inherit;font-size:inherit} u, s, strike{text-decoration:inherit} b{font-weight:inherit} .section{max-height:80em;overflow-y:auto}
かな?
その他は http://anond.hatelabo.jp/20090120094216 の辺で。
それでストーカーするには Web::Scraperでその辺のタグの有無をチェックして DBIx::Simple で記録すればよいかな?
ついでに WWW::Mechanize::Plugin::Web::Scraper で封じ込めという手もありそう。
http://anond.hatelabo.jp/20090120114819
追加でこんなかんじにした
// ==UserScript== // @name add style // @namespace http://anond.hatelabo.jp/ // @include http://anond.hatelabo.jp/* // ==/UserScript== GM_addStyle( "font{color:black;font-size: 100%}"+ "s, strike{text-decoration:none}"+ ".section{max-height:80em;overflow-y:auto}" );
あ、GM_addStyle使ってるから動かないのかな。
strike{
text-decoration:none;
}
s{
text-decoration:none;
}
font{
color:black;font-size:100%;
}
これだけあればわりと快適。
// ==UserScript==
// @namespace http://anond.hatelabo.jp/
// @include http://anond.hatelabo.jp/*
// ==/UserScript==
GM_addStyle(
"font{color:black;font-size:100%}"+
"s, strike{text-decoration:none}"
);
前回は何日くらい活動してたっけ?
ここまでひつこいとストーキングしたくなる。
まずは張っとく
あと、自分のぐりもん
// ==UserScript== // @name add style // @namespace http://anond.hatelabo.jp/ // @include http://anond.hatelabo.jp/* // ==/UserScript== GM_addStyle( "font{color:black;font-size:100%}"+ "s, strike{text-decoration:none}" );
IEは「ユーザースタイルシートのススメ - Personnel」を参考に、
#hatena-anond font{color:black;font-size:100%} #hatena-anond s, strike{text-decoration:none} #hatena-anond center{text-align:left}
これでおしまいだよ
(require 'cl) ; for cl-seq (defvar sangels-movies-dir nil) (defvar sangels-player "c:/Program Files/GRETECH/GomPlayer/GOM.exe") (defvar sangels-sort-by 'sangels-sort-by-rate) (defvar sangels-rate-file "~/.emacs.d/.sangels/rate") (defvar sangels-buffer "*sangels*") (defvar sangels-thumbnail "00_thumbnail.jpg") (defvar sangels-m3u "00_movies.m3u") (defface sangels-name '((t (:family "fixed" :weight bold :height 3.0))) "") (defface sangels-rate '((t (:family "fixed" :weight bold :height 1.5))) "") (defvar sangels-mode-map (let ((map (make-sparse-keymap))) (define-key map "n" 'next-line) (define-key map "p" 'previous-line) (define-key map (kbd "RET") 'sangels-select) (define-key map (kbd "SPC") 'sangels-select) (define-key map "q" 'sangels-quit) (define-key map "+" 'sangels-rate-plus) (define-key map "-" 'sangels-rate-minus) map)) (defvar sangels-mode-hook nil) (defvar sangels-highlight-overlay nil) (defvar sangels-rate-alist nil) (defconst sangels-rate-max 6) (defun sangels-insert-movies () (save-excursion (let* ((inhibit-read-only t) (files (remove-if-not (lambda (x) (and (not (member (file-name-nondirectory x) '("." ".."))) (file-directory-p x) (member sangels-thumbnail (directory-files x)))) (directory-files sangels-movies-dir t))) (ids (mapcar 'file-name-nondirectory files))) (erase-buffer) (setq ids (sangels-sort-ids ids)) (dolist (id ids) (let ((file (expand-file-name id sangels-movies-dir)) (pos (point))) (insert-image-file (expand-file-name sangels-thumbnail file)) (end-of-line) (insert (propertize (format "%-15s " id) 'face 'sangels-name)) (sangels-insert-rate id) (insert "\n") (put-text-property pos (point) 'sangels-id id)))))) (defun sangels-sort-by-name (a b) (string< a b)) (defun sangels-sort-by-rate (a b) (or (> (sangels-rate a) (sangels-rate b)) (sangels-sort-by-name a b))) (defun sangels-sort-ids (ids) (sort ids (or sangels-sort-by 'sangels-sort-by-name))) (defun sangels-insert-rate (id) (let ((rate (sangels-rate id))) (insert (propertize (concat (make-string rate ?★) (make-string (- sangels-rate-max rate) ?☆)) 'sangels-rate t 'face 'sangels-rate)))) (defun sangels-current-id () (get-text-property (point) 'sangels-id)) (defun sangels-play-movie (movie) (let ((explicit-shell-file-name "cmdproxy") (shell-file-name "cmdproxy")) (apply 'call-process-shell-command "start" nil "*tmp*" nil (mapcar (lambda (x) (concat "\"" x "\"")) (list sangels-player (unix-to-dos-filename movie)))))) (defun sangels-select () (interactive) (let ((id (sangels-current-id))) (when id (sangels-play-movie (expand-file-name sangels-m3u (expand-file-name id sangels-movies-dir)))))) (defun sangels-quit () (interactive) (kill-buffer sangels-buffer)) (defun sangels-rate (id) (or (cdr (assoc id sangels-rate-alist)) (/ sangels-rate-max 2))) (defun sangels-rate-save () (interactive) (let ((dir (file-name-directory sangels-rate-file))) (unless (file-exists-p dir) (make-directory dir t))) (with-temp-file sangels-rate-file (insert (pp-to-string sangels-rate-alist)))) (defun sangels-rate-load () (interactive) (when (file-exists-p sangels-rate-file) (with-temp-buffer (insert-file-contents sangels-rate-file) (goto-char (point-min)) (setq sangels-rate-alist (read (current-buffer)))))) (defun sangels-rate-plus (&optional n) (interactive "p") (setq n (or n 1)) (let* ((id (sangels-current-id)) (cell (assoc id sangels-rate-alist))) (unless cell (setq cell (cons id (sangels-rate id))) (setq sangels-rate-alist (cons cell sangels-rate-alist))) (setcdr cell (+ (cdr cell) n)) (save-excursion (let ((inhibit-read-only t)) (beginning-of-line) (goto-char (next-single-property-change (point) 'sangels-rate)) (delete-region (point) (next-single-property-change (point) 'sangels-rate)) (sangels-insert-rate id))) (sangels-rate-save))) (defun sangels-rate-minus (&optional n) (interactive "p") (setq n (or n -1)) (sangels-rate-plus (- n))) (defun sangels-post-command-hook () (save-excursion (move-overlay sangels-highlight-overlay (progn (move-beginning-of-line 1) (point)) (progn (move-end-of-line 1) (forward-line) (point)) (current-buffer)))) (defun sangels-mode () (interactive) (kill-all-local-variables) (use-local-map sangels-mode-map) (setq sangels-highlight-overlay (make-overlay 0 0)) (overlay-put sangels-highlight-overlay 'face 'highlight) (overlay-put sangels-highlight-overlay 'evaporate t) (make-local-variable 'post-command-hook) (add-hook 'post-command-hook 'sangels-post-command-hook nil t) (setq major-mode 'sangels-mode) (setq mode-name "Sangels") (run-mode-hooks 'sangels-mode-hook) (set-buffer-modified-p nil) (setq buffer-read-only t)) (defun sangels (&optional arg) (interactive "P") (when (or arg (not sangels-movies-dir)) (setq sangels-movies-dir (read-directory-name "movies dir: "))) (sangels-rate-load) (switch-to-buffer (get-buffer-create sangels-buffer)) (sangels-insert-movies) (sangels-mode)) (provide 'sangels)
せっかく書いたから匿名でのせてみるよ
使い方は
必要なものを gem で取ってくるにはこうすればいいよ
長すぎてelispが消えたから続きがあるよ
@echo off setlocal set WD=%~dp0 cd /d %WD% ruby get_movies.rb ruby get_images.rb ruby create_m3u.rb
user: ユーザID password: パスワード ids_file: ids.txt done_file: ids_done.txt movies_dir: movies log4r_config: pre_config: global: INFO loggers: - name: app type: Log4r::Logger level: INFO outputters: - STDOUT - FILE outputters: - name: STDOUT type: Log4r::StdoutOutputter formatter: type: Log4r::PatternFormatter pattern: "%d [%l] %C - %M" date_pattern: "%H:%M:%S" - name: FILE type: Log4r::FileOutputter filename: "#{LOGDIR}/sangels.log" formatter: type: Log4r::PatternFormatter pattern: "%d [%l] %C - %M" date_pattern: "%Y-%m-%d %H:%M:%S"
require 'fileutils' require 'logger' require 'mechanize' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" require "#{BASEDIR}/sangels" $config = load_config(BASEDIR) prepare_logger(BASEDIR) $log = new_logger("get_movies") WWW::Mechanize.log = new_logger("mechanize") WGet.log = $log class IDFile def initialize(file) @file = file unless File.exist?(@file) Fileutils.touch(@file) end end def ids(contains_comment = nil) File.open(@file) {|io| io.to_a.map {|x| x.chomp }.select {|x| if x.empty? nil elsif contains_comment true else not /^\s*\#/ =~ x end } } end def add(id) ids = ids(true) unless ids.any? {|x| x == id} write(ids + [id]) end end def delete(id) ids = ids(true) if ids.any? {|x| x == id} write(ids - [id]) end end def write(ids) File.open(@file, "w") {|io| ids.each {|x| io.puts x} } end end $log.info("BEGIN #{$0} ================") exit_code = 0 begin ids_file = IDFile.new($config.ids_file) done_file = IDFile.new($config.done_file) movies_dir = $config.movies_dir wget = WGet.new sangels = SAngels.new sangels.login($config.user, $config.password) ids_file.ids.each {|id| begin movies = sangels.movies(id) rescue SAngels::Movies::InvalidMoviesError $log.warn("invalid movie id: #{id}") next end dir = File.expand_path(id, movies_dir) movies.each {|link| wget.retrieve(link.href, dir) } expected = movies.movie_links.map{|x| File.basename(x.href)} actual = Dir.glob("#{dir}/*").map {|x| File.basename(x)} if (expected - actual).empty? done_file.add(id) ids_file.delete(id) end } rescue => e $log.error(e) exit_code = 1 end $log.info("END #{$0} (#{exit_code}) ================") exit exit_code
require 'fileutils' require 'logger' require 'mechanize' require 'ostruct' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" require "#{BASEDIR}/sangels" $config = load_config(BASEDIR) prepare_logger(BASEDIR) $log = new_logger("get_images") WWW::Mechanize.log = new_logger("mechanize") WGet.log = $log $log.info("BEGIN #{$0} ================") exit_code = 0 begin movies_dir = $config.movies_dir sangels = SAngels.new sangels.login($config.user, $config.password) thumbnails = sangels.thumbnails Dir.glob("#{movies_dir}/*").each {|dir| next unless File.directory? dir id = File.basename(dir) url = thumbnails.url(id) unless url $log.warn("#{id} is not found") next end path = File.expand_path("00_thumbnail#{File.extname(url)}", dir) next if File.exist? path $log.info("retrieving #{url}") thumbnail = thumbnails.get_file(id) File.open(path, "wb") {|io| io.write(thumbnail)} } rescue => e $log.error(e) exit_code = 1 end $log.info("END #{$0} (#{exit_code}) ================") exit exit_code
BASEDIR = File.dirname($0) require "#{BASEDIR}/util" $config = load_config(BASEDIR) movies_dir = $config.movies_dir Dir.glob("#{movies_dir}/*") {|dir| next unless File.directory? dir name = File.basename(dir) files = Dir.glob("#{dir}/*.wmv").sort File.open("#{movies_dir}/#{name}.m3u", "w") {|io| files.each {|file| io.puts "#{name}/#{File.basename(file)}" } } File.open("#{dir}/00_movies.m3u", "w") {|io| files.each {|file| io.puts "#{File.basename(file)}" } } }
require 'mechanize' require 'hpricot' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" class SAngels HOST = "real2.s-angels.com" LOGIN_URL = "http://#{HOST}/member/" INFO_URL = "http://#{HOST}/teigaku/item.php" THUMBNAILS_URL = "http://#{HOST}/teigaku/" THUMBNAIL_URL = "http://#{HOST}/images/default/thumb/" def initialize() @agent = WWW::Mechanize.new end def login(user, password) login_form = @agent.get(LOGIN_URL).forms.find {|form| form.fields.any? {|field| field.name == "frmLoginid"} } login_form.frmLoginid = user login_form.frmPw = password @agent.submit(login_form) end def movies(id, no_validate = nil) Movies.new(@agent, id, !no_validate) end def thumbnails Thumbnails.new(@agent) end class Thumbnails def initialize(agent) @agent = agent doc = Hpricot(@agent.get_file(THUMBNAILS_URL)) elems = doc.search("div[@class=realthum]/a") @links = Hash( elems.map {|elem| href = elem["href"] id = $1 if /ID=(.+)/ =~ href url = elem.search("img")[0]["src"] [id, url] }) end def get_file(id) @agent.get_file(url(id)) end def url(id) @links[id] end def exist?(id) url(id) end end class Movies class InvalidMoviesError < StandardError end def initialize(agent, id, no_validate) @agent = agent @id = id if !no_validate && !valid? raise InvalidMoviesError end end def info_page_url "#{INFO_URL}?ID=#{@id}" end def info_page @agent.get(info_page_url) end def movies_page @agent.click(info_page.links.find {|link| /P=10/ =~ link.href}) end def movie_links movies_page.links.select {|link| /wmv$/ =~ link.href }.sort {|a, b| File.basename(a.href) <=> File.basename(b.href) } end def valid? info_page.uri.to_s == info_page_url end def each(&block) orig_links = movie_links orig_links.each {|orig_link| link = movie_links.find {|l| File.basename(l.href) == File.basename(orig_link.href)} block.call(link) } end end end
require 'log4r' require 'log4r/yamlconfigurator' require 'singleton' require 'fileutils' require 'ostruct' def Hash(a) Hash[*a.flatten] end def load_config(basedir) OpenStruct.new(File.open("#{basedir}/config.yaml") {|io| YAML.load(io)}) end def new_logger(name) Log4r::Logger.new("app::#{name}") end def prepare_logger(basedir, logdir = nil) logdir ||= basedir Log4r::YamlConfigurator["LOGDIR"] = logdir Log4r::YamlConfigurator.load_yaml_file("#{basedir}/config.yaml") end class NullObject include Singleton def method_missing(message, *arg) NullObject.singleton end end class WGet class << self attr_accessor :log def initialize super @log = NullObject.singleton end end def log self.class.log end def retrieve(url, dir) FileUtils.mkdir_p(dir) file = File.expand_path(File.basename(url), dir) if File.exist?(file) log.info("already retrieved #{url}") return true end tmp = "#{file}.part" log.info("retrieving #{url}") ret = system("wget", "-c", "-O", tmp, url) if ret log.info("retrieving succeeded #{url}") File.rename(tmp, file) else if $? == 0x020000 # Ctrl-C exit($?) else log.error("retrieving failure #{url} (#{$?})") end end return ret end end
例えば「 はてなキーワードが嫌いになった理由 - 将来が不安」とか、最近、はてなキーワードがだめとか何とか、そういう話題がかまびすしかったわけですが、いやいや、今更言うのもなんですが、はてなキーワードはすごいんですよ?
キーワードページからそのキーワードを含む日記が解る。こういう機能、なかなか無いです。
でも、そのキーワードを使っているはてな以外の日記も見たい時があるかもしれません。
テクノラティなら、はてなに限らず、色々なブログサービスの、そのキーワードを含む記事を探すことができます。
でも、日記のキーワードリンクからダイレクトに飛べないと、いちいち見る気になりません。なので、そういうGreasemonkeyスクリプトを書いてみました。はてダの記事中のキーワードリンクの、リンク先をテクノラティにします。
// ==UserScript== // @name Keyword to Technorati // @namespace http://anond.hatelabo.jp/ // @include http://d.hatena.ne.jp/* // ==/UserScript== var keywords = document.getElementsByClassName("keyword"); for(var i=0; i<keywords.length; i++){ keywords.item(i).href = "http://www.technorati.jp/search/" + keywords.item(i).textContent; }
でも、これもちょっと不便です。キーワードリンクで、そのキーワードの意味を知りたい時もあるからです。
はてなキーワードの素晴らしいところは、キーワードを含む日記とキーワードの意味、どちらも一つのページで確認できる事です!まぁキーワードの説明とWikipedia、どっちか片方でも良いような気もしますがf(^ ^;)
そこで、テクノラティのキーワード検索結果ページにも、そのキーワードの意味が表示されれば便利です。
(余談ですけど、Wikipediaの記事があるなら、Wikipediaの記事だけ表示すれば十分かなぁとか思っちゃったり?概してWikipediaの記述の方が優れてるし…)
// ==UserScript== // @name Technorati with Wikipedia or ?keyword // @namespace http://anond.hatelabo.jp/ // @description Add Wikipedia in Technorati search page // @include http://www.technorati.jp/search/* // ==/UserScript== var keyword = (decodeURIComponent(document.URL).split("?")[0]+" ").slice("http://www.technorati.jp/search/".length, -1); function appendKeyword(title,body,url){ var div = document.createElement("div"); div.style.border = "inset gray thin"; div.style.padding = "5px 14px"; var h2 = document.createElement("h2"); h2.innerHTML = title.link(url); h2.style.fontSize = "2em"; div.appendChild(h2); var content = document.createElement("div"); content.innerHTML = body; div.appendChild(content); div.appendChild(document.createElement("hr")); var foot = document.createElement("div"); foot.innerHTML = "["+decodeURIComponent(url).link(url)+"]"; foot.style.textAlign = "right"; div.appendChild(foot); //document.getElementById("main").insertBefore(div, document.getElementById("main").firstChild); document.getElementById("extra").insertBefore(div, document.getElementById("extra").firstChild); } GM_xmlhttpRequest({ method: "GET", url: "http://wikipedia.simpleapi.net/api?output=json&keyword="+keyword, onload: function(response){ var wp = eval(response.responseText); if(wp){ appendKeyword(wp[0].title, wp[0].body, "http://ja.wikipedia.org/wiki/"+encodeURIComponent(wp[0].title)); }else{ GM_xmlhttpRequest({ method: "GET", url: "http://d.hatena.ne.jp/keyword?mode=rss&ie=utf8&word="+encodeURIComponent(keyword), onload: function(response){ var hk = (new DOMParser).parseFromString(response.responseText, "text/xml"); appendKeyword(keyword, hk.getElementsByTagName("description").item(1).textContent, "http://d.hatena.ne.jp/keyword/"+keyword ); } }); } } });
これで、はてダのキーワードリンクで飛んだ先に、その単語の説明とはてな以外も含めたブログ記事が表示されます。やったね\(^o^)/
追記
用語の説明は、検索結果の上に表示するより、サイドバー(広告が表示されてる)にあった方が便利かなーとか思ったので、コードをちょぴっと変更しました。既にインスコしちゃってた人、ごめんね!
ところでテクノラティ検索結果のAutopagerize、なんか1頁目ばっかり継ぎ足される気がするけど、ボクだけかな?
http://www.ubuntulinux.jp/products/JA-Localized http://www.ubuntulinux.jp/
http://jody.sci.hokudai.ac.jp/~ike/colum/ubuntu_feisty_amd64.html
wget -q http://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add -
sudo wget http://www.ubuntulinux.jp/sources.list.d/hardy.list -O /etc/apt/sources.list.d/ubuntu-ja.list
sudo apt-get update
sudo apt-get install ubuntu-desktop-ja
sudo apt-get install ubuntu-ja-keyring
sudo apt-get upgrade
sudo apt-get install language-pack-gnome-ja language-pack-ja
sudo apt-get install ipafont ipamonafont lha-sjis
sudo apt-get install scim-anthy
sudo apt-get install scim-bridge im-switch
sudo apt-get install openoffice.org-l10n-ja openoffice.org-help-ja mozilla-firefox-locale-ja-jp
.
本記事末尾のローカルなkey mappingを実現するコードを改良してプラグインにしました。
↓
Vimperatorでローカルなkey mappingを実現するプラグイン local_mappings.js を書いた。
http://anond.hatelabo.jp/20080826124641
2008-07-14 - やぬすさんとこの日記
http://d.hatena.ne.jp/janus_wel/20080714
→n秒後/前に移動するkey mappingも忘れずに!
Re: autocmd が分からない - hogehoge
http://d.hatena.ne.jp/teramako/20080731/p1
のコードをいじった。
" --- autocmd --- " nicovideo " cでコメント入力、Cでコマンド入力、sでシーク、lでボリューム調整、 " pで停止/再生、mでミュートのon/off、vでコメの表示トグル、zでズーム。 javascript <<EOM liberator.plugins.nicomap = function(){ // no args var list=[ ["p","nicopause"], ["m","nicomute"], ["v","nicomementvisible"], ["z","nicosize"], ["s","nicoseek"], ]; // has args var list2=[ ["c","nicomment"], ["C","nicommand"], ["l","nicovolume"], ["s","nicoseek"], ]; if(buffer.URL.indexOf("http://www.nicovideo.jp/watch") == 0){ for (var j=0; j<list.length; j++){ let i = j; liberator.mappings.addUserMap([1],[list[i][0]],list[i][1], function(){ liberator.execute(list[i][1]); },{ rhs:":"+list[i][1]+"<CR>" } ); } for (var j=0; j<list2.length; j++){ let i = j; liberator.mappings.addUserMap([1],[list2[i][0]],list2[i][1], function(){ liberator.execute('normal :'+list2[i][1]+'<Space>'); },{ rhs:":"+list2[i][1]+"<Space>" } ); } } else { for (var i=0; i<list.length; i++){ liberator.mappings.remove(1,list[i][0]); } for (var i=0; i<list2.length; i++){ liberator.mappings.remove(1,list2[i][0]); } } }; liberator.autocommands.add('LocationChange','.*','js liberator.plugins.nicomap()'); EOM
http://d.hatena.ne.jp/nokturnalmortum/20080802#1217633913
.
現在のマウスカーソル位置でクリックイベント発生するkey mappingを設定できればより快適なんだが・・・。
(「コメントする」ボタンのDOMノードが取得できれば、dispatchEventでいけそうだけど)
" ************* local key mappings **************** javascript <<EOM (function(){ function setlocalmap(obj){ var list = obj.list; var list2 = obj.list2; var exp = obj.exp; if(list.constructor != Array || list2.constructor != Array){ echr("invalid argument: array argument required");return; } if(exp.constructor != RegExp){ echr("invalid argument: regex argument required");return; } if(exp.test(liberator.buffer.URL)){ for (var j=0; j<list.length; j++){ let i = j; liberator.mappings.addUserMap([1],[list[i][0]],list[i][1], function(){ liberator.execute(list[i][1]); },{ rhs:":"+list[i][1]+"<CR>" } ); } for (var j=0; j<list2.length; j++){ let i = j; liberator.mappings.addUserMap([1],[list2[i][0]],list2[i][1], function(){ liberator.execute('normal :'+list2[i][1]+'<Space>'); },{ rhs:":"+list2[i][1]+"<Space>" } ); } } else { for (var i=0; i<list.length; i++){ liberator.mappings.remove(1,list[i][0]); } for (var i=0; i<list2.length; i++){ liberator.mappings.remove(1,list2[i][0]); } } } /** * Add Key Mappings to Specific Web Pages * @param obj : has following properties * list : commands that take no args * list2 : commands that take args * exp : target page's URL (regex) * @see Re: autocmd が分からない - hogehoge * http://d.hatena.ne.jp/teramako/20080731/p1 */ liberator.plugins.addLocalUserMap = function(obj){ liberator.plugins[obj.name + "MapSetter"] = function(){ setlocalmap(obj); } liberator.autocommands.add( 'LocationChange', '.*', 'js liberator.plugins.' + obj.name + 'MapSetter()' ); }; // nicovideo // cでコメント入力、Cでコマンド入力、sでシーク、lでボリューム調整、 // pで停止/再生、mでミュートのon/off、vでコメの表示トグル、zでズーム。 var nicovideo = { name : 'nico', exp : /^http:\/\/www.nicovideo.jp\/watch/, list : [ ["p","nicopause"], ["m","nicomute"], ["v","nicomementvisible"], ["z","nicosize"], ["s","nicoseek"], ], list2 : [ ["c","nicomment"], ["C","nicommand"], ["l","nicovolume"], ["s","nicoseek"], ], }; liberator.plugins.addLocalUserMap(nicovideo); })(); EOM
動作未確認。すんげー冗談半分。
// Hatena Monge Star user script // 2008-07-07 // by masda. (http://anond.hatelabo.jp/20080707043247) // ==UserScript== // @name Hatena Monge Star // @namespace http://anond.hatelabo.jp/20080707043247 // @description Hatena Monge Star // @include http://b.hatena.ne.jp/entry/* // @version 0.3.1 // ==/UserScript== // deriving from [http://d.hatena.ne.jp/Hamachiya2/20080707/HatenaBlackStar2] ver Firefox // [http://f.hatena.ne.jp/hatenacinnamon/20070109001332] // Thx! and CUTE! location.href = 'javascript:(' + function() { (function (w) { if (typeof(w.Ten) == 'undefined') { return; } HatenaBookmarkMongeStar = new Ten.Class({ initialize: function(li, entryTitle) { var comment = ''; var tags = ''; var commentSpans = Ten.DOM.getElementsByTagAndClassName('span', 'comment', li); if (commentSpans.length > 0) { comment = Ten.DOM.scrapeText(commentSpans[0]); } var tagsSpans = Ten.DOM.getElementsByTagAndClassName('span', 'user-tag', li); if (tagsSpans.length > 0) { $A(tagsSpans[0].getElementsByTagName('a')).each(function(a) { tags += '[' + Ten.DOM.scrapeText(a)+ ']'; }); } var title = tags + comment; if (title.length == 0) { var name = Ten.DOM.scrapeText(li.getElementsByTagName('a')[1]); title = name + 'のブックマーク'; } // this.uri = 'http://b.hatena.ne.jp/keyword/' + li.getElementsByTagName('a')[1].href; var u = li.getElementsByTagName('a')[1].href; if (u.indexOf('#') == -1) { this.uri = u + '#_HatenaMongeStar'; } else { this.uri = u + '_HatenaMongeStar'; } this.title = title + ' - ' + entryTitle; this.comment_container = Hatena.Star.EntryLoader.createCommentContainer(); var target = commentSpans[0] || li; target.appendChild(this.comment_container); this.star_container = Hatena.Star.EntryLoader.createStarContainer(); this.star_container.className = 'hatena-star-star-container MongeStarContainer'; target.appendChild(this.star_container); } }); var tryCount = 0; var tryMax = 300; function waitForHatenaStar() { // if (Hatena.Star.EntryLoader.loaded) { // Hatena.Star.EntryLoader.loaded = false; var s = document.getElementsByClassName('hatena-star-add-button'); if (s.length) { Hatena.Star.EntryLoader.loaded = false; Hatena.Star.EntryLoader.loadEntries = function() { var entries = []; var title = Ten.DOM.scrapeText(Ten.DOM.getElementsByTagAndClassName('span', 'title', document.body)[0]); var ul = document.getElementById('bookmarked_user'); if (ul) { $A(ul.getElementsByTagName('li')).each(function(li) { if (li.className != 'more') { entries.push(new HatenaBookmarkMongeStar(li, title)); } }); } return entries; } new Hatena.Star.EntryLoader(); } else { if (++tryCount > tryMax) { setTimeout(waitForHatenaStar, 400); } } } setTimeout(waitForHatenaStar, 500); })(window); }.toString() + ')()'; GM_addStyle(<><![CDATA[ .MongeStarContainer { margin-left: 4px; } .MongeStarContainer .hatena-star-add-button { background-color: #fc6 ! important; } .MongeStarContainer a { text-decoration: none ! important; color: #f80 ! important; font-size: 10px; position: relative; } .MongeStarContainer a:before { content: '毛'; font-size:small; } .MongeStarContainer a .hatena-star-star { filter: alpha(opacity=00); -moz-opacity:0.00; opacity:0.00; position: absolute; top: 0; left: 0; } .MongeStarContainer .hatena-star-inner-count { color: #f90 ! important; } ]]></>);
動いたらいいな-
プログラムを学ぶ上での一番根源的なものとしてアセンブリ言語が扱われることってわりと多いように思う。
便利なプログラムもコンピューター内ではこう処理されてます、とか。
だけど、その説明ってあんまり意味が無いよなとか思ったり。
add D1 D2 D3(アセンブリの命令でD1とD2の値を足してD3へ代入を意味)なんてもはや単なるプログラミング言語に過ぎない訳で、それCでも出来るよ、としか思わない。
はいはいアセンブリ不便ですね、ってなって結局主軸は別の言語に移す。
プログラミングの楽しさや便利さはその別の言語を通じて学ぶことになるのだし、じゃあアセンブリ言語を学ぶ意義ってなによ。
c=a+bって書けばコンピューターが計算してくれますっていうのと、add D1 D2 D3ってやればCPUが計算してくれますっていうのは感覚としては同じ。
本当に根源的なものまで突き詰めるのなら電気信号をどう解釈してどういう仕組みで演算して数を格納してっていうことをやってほしい。
アセンブリを根源扱いして崇めることにはなにか抵抗を感じる。
Event.add=function(_element,_name,_func){ if(_name=="load") { Event.onload.add(_func); return; } if(ua.name.indexOf("IE")>0 && _name=="keypress") _name="keydown"; if(window.addEventListener) _element.addEventListener(_name, _func, false); else if (window.attachEvent) { if(_element==window) _element=document; _element.attachEvent("on"+_name,function(){ var e=arguments[0]; e.target=e.srcElement; e.pageX=document.body.scrollLeft+e.clientX; e.pageY=document.body.scrollTop+e.clientY; _func.apply(_element,arguments); }); } };
はじめてのラッパー
##はじめに
→OpenIDが仮に広まった未来には、サービス事業者がユーザの個人情報をどれだけ持つのが適正なのかを考えられるようになりたいよ
##OpenIDを利用したサービスは、将来オープンにOpenIDプロバイダを受け入れることができる?
OpenIDを受け入れる、ということは「特定ではないIDプロバイダによって認証」されたユーザをサービス事業者は受け入れるということになるよ。
※以下サービス事業者の例を、わかりやすくするために京都発のWebサービス提供会社、はてなさん(以下はてな)にするよ
ここでいう「特定ではないIDプロバイダによって認証」というカッコ書きについて整理しておくね。
これ、逆に言うとこれまでのサービスって、はてなも勿論そうだけど「特定されるIDプロバイダによって認証」が行われていたんだということになるよね。
例えば、はてなというサービスにエンドユーザの増田が、はてなのIDとパスワードでもってログインを行う場合は、
増田・はてなサービス・はてな会員管理システム(これもはてなの一部だけど)の3者関係で考えると、
増田がはてなのサービスを利用するためにログインすると、はてなサービスは、はてなの会員管理システムに僕が僕であるためのID・パスワードを問い合わせして
はてな側に僕だよ、っていうことを認証、そしてサービス利用の許可(認可)していたわけだよね。
これまでの
はてなのサービス→はてなの会員管理システムで認証する、というお決まりのやり方を
はてなのサービス→「特定ではないIDプロバイダによって認証」もOKにしちゃう!
っていうのがOpenIDの基本的な考えだと思ってるよ。
つまり、はてなに対してみんな大好きなmixi(渋谷区)のゆるふわIDパスワードでOpenID認証しちゃえ!ていう感じ。
OpenIDと呼ばれるもののコアなところって、この自分じゃない余所様でログインをさせるにあたっての
通信の決まり・振る舞い方についての仕組みとかのことなんだね。
認証機能の委譲、なんて難しい言葉で言われてもバカな僕にはわかんなかったから、とりあえずこんな感じで整理してみたよ。
でもね。増田自身がはてなの立場になって考えてみるとこう思うよきっと。
他所のプロバイダさんに認証をお願いしたら、、、
「コノ人確カニ○○○君!ザッツヒム!イッツOK!!」ていう怪しげな応答があったとしてもさ
「うちは京都のサービスさかいに、妙ちくりんな英語まじりのプロバイダさんの言うことなんか信用できまへんなー」
て自然と思ってしまうよきっと。これがひいてはOpenIDプロバイダの評判問題ってやつにつながる話だね。
あと、じゃあOpenIDプロバイダの認証結果は信じることにしたとしてでも今度は
「まーmixiさんところが認証OKてゆなら確実でっしゃろう?遠いところからよくきはりました。
どれ、アンタうちでもサービス使わせてやるさかい...あれれ?君、うちでいうところのid:誰くんでしたっけ?」
てなっちゃうねーやっぱり。。これが認証と認可(属性情報交換)に関わる問題てやつだよ。
うーん、ちょっと自分自身にとってもムツカシくなってきたなぁ。もう少しわかりやすく書くね。
上の話ははてな子ちゃんが自分の会員管理システムでログインさせない(外の会員管理システムでログインする)ことにより、
自社のサービス提供では当たり前にできていたことができない、という問題が2つ出てきたねーということだよね。
1. 「あなた(Openプロバイダ)の認証、あ、あたし。信じていいの?ゴクリ・・・」
という信頼関係について。
2. 「あなた(エンドユーザ)は彼(OpenIDプロバイダ)に認められた人だから、アタシも、が、がんばって信じる!…けど、○○○君(エンドユーザ)のことをもっと知る必要があるの。。。」
という(エンドユーザの)認可・(OpenIDプロバイダからの)属性情報の受入(交換)について。
うー、あれ?
はてなスターではこの2つの問題をどうしているの?って思う人は多いよね。
たぶんはてなスターがOpenID対応しているっていうのを聞いたことがあっても、実際にやったことある人は少数派じゃないかなまだ。。
じゃあこっからははてなスターを例にとって説明するよ!
詳しくは下のリンクの説明通りなんだけど、
http://www.hatena.ne.jp/info/openid
今回増田が問題としている2つについてはてなスターの機能はどーなってるの?ていうのを整理すると
1.「OpenIDプロバイダとの信頼関係について」=「フレンドプロバイダのみ認証OK!」(いわゆるホワイトリスト)
2.「(エンドユーザ)認可・(OPとの)属性情報交換」=OpenIDのユーザ名でスターがつく
という対応をしているみたい。
※ちなみにこの記事書くにあたって増田ははじめてOpenID経由ででスターつけてみたよ!!
つまり、
1.の信頼関係については、Livedoorなど数社のOpenIDプロバイダのみを受入OKにしているし、
2.の属性情報については、OpenID認証を行う際に必要なOpenIDプロバイダ側の「ユーザ名@OP名」でスターがつくだけ
→なので属性情報交換などはほぼゼロだよね、って感じだったよ。
1.は、
「なーんだ。Open何とか言っておきながら内輪でのID連携かよ。うちも一応OpenIDプロバイダたててんだよ?え?無理?うちみたいなチンケなプロバイダは無視ですかそーですか」
みたいな中小企業のボヤキが聞こえてくるくらい全然Openじゃなくすることで一方での
はてな子ちゃんにとっての問題=「あなた(Openプロバイダ)の認証、あ、あたし。信じていいの?ゴクリ・・・」問題を回避しているということになるよね。
2.についてははてなスターはほぼガン無視を決め込んでいるのが今回よくわかりました!
今回増田がためしにOpenID認証経由でスターをつけてみたんだけど、
あのー、、増田も一応こうして増田をやっているので一応はてな市民であって、「あいでぃー:xxx」みたいな立場ではあるじゃないですか。
なのに、LivedoorのIDでスターつけちゃったら「あいでぃー:xxx」でスターつけたことにならない><!(※)ので、
うーん、、ちょっとこれは深刻な機能不足だなーと思った次第ですー。いや?いいのかこれでOpenIDとしては。微妙だなぁ・・
(※)
だって、増田のidhttp://s.hatena.ne.jp/xxx/starsでスターが反映されない
あと<増田のLivedooeのアカウント名>@livedoor のスターのカウント(上のhatena/user/starsに相当するページね)はどこにいったのだろう??
でもさぁ、
はてな子ちゃんの立場はそれはそれでよくわかるのよね。
いまいまのOpenIDのセキュリティレベルでは、どこの馬の骨ともわからん奴にあなたのことについて
※ほんとは、はてなの「あいでぃー:xxx」とLivedoorのidがSocialに結びついてくれて、
自動的にhatenaのidでスターをAddしたことになればいいんだけどねー。でもそれじゃあはてなIDでログインしろってことと変わらんかー。
とも思うし難しいなぁこの辺。
こういう問題があるOpenID界隈では、でもこれらの問題について色々知恵を出し合って解決しようとしている
人もいるみたい。サイボウズのzigorouさんとか、他にもいっぱいいらっしゃるけど、皆さんすごいがんばってるみたい!すごい!
増田個人は、
1.については各OpenIDプロバイダとIDを利用するサービス側(Ryling Party)それぞれのホワイトリストが
Socialに連携/公開されてグラフになってエンドユーザが利用できる・できないの仕組みになるのがいいのかなー、
と思っていたりするよ。DNSみたいな公開されて相互利用できるよな仕組みがあればいいのかなー。
2.については属性情報の仕組みとしてはAXとかsregとかあるけど、要は使い方でリバティ・アライアンスの頃からしきりと言われているらしい
「串刺しにした」サービスの連携のためにどう属性情報を流通させるのか?SSO連携が肝だよねー。とか。
また属性情報流通させるにあたってのその情報粒度は?っていう話を詰めなきゃいけないんだろうなー、というレベルでぼんやり中です。
もう少し↑について知識・考えついてきたら、またまとめてみたいです。じゃあまたね!!
public class Main { public static void main( String[] args ) { // ( 5 - 3 ) + 1 Exp exp = new Add(new Sub(new Num(5), new Num(3)), new Num(1)); System.out.println(exp.eval()); } } /** * 抽象クラス */ abstract class Exp { public abstract int eval(); } /** * 足し算 */ class Sub extends Exp { /** 左辺 */ private Exp hidari; /** 右辺 */ private Exp migi; /** * コンストラクタ */ public Sub(Exp hidari, Exp migi){ this.hidari = hidari; this.migi = migi; } /** * 評価 */ @Override public int eval() { return hidari.eval() - migi.eval(); } } /** * 引き算 */ class Add extends Exp { /** 左辺 */ private Exp hidari; /** 右辺 */ private Exp migi; /** * コンストラクタ */ public Add(Exp hidari, Exp migi){ this.hidari = hidari; this.migi = migi; } /** * 評価 */ @Override public int eval() { return hidari.eval() + migi.eval(); } } /** * 数 */ class Num extends Exp { /** * 数 */ private int self; /** * コンストラクタ */ public Num(int self) { this.self = self; } /** * 評価 */ @Override public int eval() { return self; } }