MacBook Air Mid 2013 を入手したのでOSXを起動せずにGentoo入れてみた
ことはじめ
おやおや,こんなところに Mid 2013 な感じの MacBook Air がありますね.ふうむ,電源を入れようと思ったが,そのまま起動しても面白くないな.OSXのセットアップしてる時間があったらGentooが入るんじゃないか?そうだ,ここはGentooInstallBattleっぽく,OSXを起動せずにGentooを入れてみようじゃないか!いやぁ,我ながら良い考えだなあ.
ぶーと
皆さんも2-3本はGentooのインストール用USBスティックを持ち歩いていると思います.色々役に立つし,出張先や飮み屋でもGentooInstallBattleができて非常に便利ですからね.もちろん,一応手元にはGentooのLiveCDがありますから,まずはインストールメディアの選択から始めるわけです.いかにもGentooらしいですね.
さて,Optionキーを押しながら電源を入れるとbootするメディアを選択できるので,USBなり外付けCDドライブなりから起動します.起動してしまえばすんなり動いてくれる…と思っていた時期が僕にもありました.あいにく手元にUSBなEthernetがありませんし,無線LANはもちろん動かないわけですので,ちょっとだけ困るわけですね.でも時代はクラウドとスマホの時代,テザリングができるじゃありませんか.ネットワーク接続を手に入れるために,おもむろにスマートフォンとMBAを接続します.USBテザリングを有効にしたら,普通にNICが見えますので,dhcpcdを叩いてあげると繋がります.やったね.
ぱーてぃしょん
ネットワーク接続を手に入れたら,次はパーティションを切りたくなりますね.全部消しちゃいたくなりますよね.OSXの領域とかリカバリ領域とか,使わないのに残しておいてももったいないですからね.しかし,このまま全部消してしまうと,起動時の「じゃーんwww」という音が消せなくなってしまうので,とりあえず外付けHDDとかをつないでddしておきます.この際,パーティションごとにddしたほうが後々使い勝手が良いです.MBRも忘れずにddしましょう.
dd if=/dev/sda of=/path/to/backup/MBR.img bs=512 count=1
dd if=/dev/sda1 of=/path/to/backup/sda1.img bs=1M
dd if=/dev/sda2 of=/path/to/backup/sda2.img bs=1M
dd if=/dev/sda3 of=/path/to/backup/sda3.img bs=1M
になっている(と思う)ので,sda2に関してはサイズも大きいし別にいいかなーと思います.MBRとsda1とsda3はあとで使うので必ずddしておきましょう.
パーティションはGPTで作ります.僕はこんな感じで切りました.
% sudo parted /dev/sdaインストールしてから気付いたのでもはや後戻りはしたくないですが,EFIだとISOイメージからブートできたりするので,/bootは大きめに切っておいたほうが良かったですね.あと,現状ではサスペンドしたあとにディスプレイ輝度が0%か100%の2択になってしまうので,必ずswap領域を作っておきましょう./etc/fstabはこんな感じです.
GNU Parted 3.1
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s
(parted) print
Model: ATA APPLE SSD TS0128 (scsi)
Disk /dev/sda: 236978176s
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:Number Start End Size File system Name Flags
1 2048s 585727s 583680s fat32 non-fs bios_grub
2 585728s 1171455s 585728s ext4 ext4
3 1171456s 17172479s 16001024s linux-swap(v1) swap
4 17172480s 87171071s 69998592s ext4 ext4
5 87171072s 236976127s 149805056s ext4 ext4
/dev/sda1 /boot/efi vfat noauto,errors=remount-ro 0 2
/dev/sda2 /boot ext4 noauto,noatime,discard,errors=remount-ro 1 2
/dev/sda3 none swap sw 0 0
/dev/sda4 / ext4 noatime,discard,errors=remount-ro 0 1
/dev/sda5 /home ext4 defaults,discard,errors=remount-ro 0 0
いんすとーる
システムのインストールは何も考えなくていいです.普通にインストールして下さい.非常に簡単ですね.でもおそらく再起動できないので,UbuntuのライブCDとかからEFIでブートします.その後,
modprobe efivarsコマンドを叩いてブート用ディスクを作ります(この辺はググったら出てきます).EFIブートできたら,Gentooから同様の作業をするといい感じになると思います.また,grub2-installするときに多分失敗するので,自分でEFIの設定を書き換える必要があります.efibootmgrを使ってこんな感じにブートローダ周りを設定してあげる必要があります.
# efibootmgr -v
BootCurrent: 0000
Timeout: 5 seconds
BootOrder: 0000
Boot0000* gentoo HD(1,800,8e800,9b3bd77d-799f-46f7-a569-95a251a4f759)File(\EFI\gentoo\grubx64.efi)
Boot0080* ACPI(a0341d0,0)PCI(1c,5)PCI(0,0)03120a00000000000000HD(2,64028,e066090,553ed906-2f93-4629-a5e6-c90ddbcec833)File(\System\Library\CoreServices\boot.efi)
BootFFFF* ACPI(a0341d0,0)PCI(1c,5)PCI(0,0)03120a00000000000000HD(2,64028,e066090,553ed906-2f93-4629-a5e6-c90ddbcec833)File(\System\Library\CoreServices\boot.efi)
きどうおんをけす
起動するときの「じゃーんwww」はOSXの音量とリンクしているらしいですが,もはやそんなものはありません.どうやら,リカバリ領域から起動したりリカバリUSBから起動すると良いらしいです.でも,最近のMacにはリカバリUSBなどというレガシーなものは同梱されていないようです.僕はレガシーな人間なので,仕方なくUSBスティックを作ります.
まずは,USBにMBR.imgをddします.次に,partedでこんな感じにパーティションを割ります.イメージとしては,USBにMBRをddした段階でパーティションテーブルができているので,テーブルの2と3を消します.次に,3番目のテーブルがリカバリ領域と同じになるように2番目のテーブルを切ります.あとは,1番目と3番目のパーティションにsda1とsda3をそれぞれddします.簡単ですね.
% sudo parted /dev/sdbUSBから起動したら,ターミナルを立ち上げてnvramを使ってブートサウンドを殺します.02で死ななかったら00や01や80なんかもあるらしいです.何のマジックナンバーかはよく知りません.
GNU Parted 3.1
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s
(parted) print
Model: silicon -power (scsi)
Disk /dev/sdb: 15654912s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:Number Start End Size File system Name Flags
1 40s 409639s 409600s fat32 EFI System Partition boot
2 409640s 1000000s 590361s Customer
3 1000001s 2269536s 1269536s hfs+ Recovery HD
# nvram SystemAudioVolume=%02とか叩くといい感じになりました.
# shutdown -h now
おわりに
あとで追記するか別記事で各項の詳細が書ければと思います
アクセス制限のあるネットワーク環境下でmikutterがしたいんです?
想定環境
みなさんはどんな環境でmikutterを利用しているでしょうか.様々な環境が想定されますが,今回はファイアウオールやアクセス制限によってTwitterにアクセスできなくなっているネットワークでmikutterを使う,という想定のもと記事を書こうと思います.今回の方法を適用するために必要な条件は「Proxy認証なしに外部にアクセス可能なポートが1つ以上存在する」ということだけです.そのポートから適当なVPSとかにsshかけてsocksしましょう.
やり方
まずsocksifyをインストールします.
gem install socksify
次に,適当なVPSとかにsshでログインします.例えば,53番が空いてるということが分かったら,
ssh -p 53 -D 8080 myvps.mydomain.com
みたいな感じでログインします.-pはポート指定のオプション,-DはSocksするポートの指定です.このコマンドを叩くと,localhost:8080をProxyに設定するとVPSからパケットが出て行きます.なお,このコマンドを叩く前にVPS側のssh待ち受けポートを53番に設定しておきましょう(もちろん非推奨です).その後,下記のコマンドでmikutterを起動します.
socksify_ruby localhost 8080 mikutter.rb
動きましたね!
mikutterの Service.primary.post :message => "Hello, mikutter!" を旅してみる
ことはじめ
「mikutterでアイコンを設定したい」「mikutterから名前を変更したい」そう思ったことはありませんか?mikutterにはプロフィール周りを設定する機能がないような気がするので,mikutterからTwitterAPIを叩いて実現しようと考えました.mikutter/core/mikutwitter/ あたりにTwitterAPIを叩くときに関係するソースが置いてあるので,その辺を読めば使い方が分かります.ちなみに,Twitterの REST API v1.1 に関する情報は https://dev.twitter.com/docs/api/1.1 にまとめられています.
Service.primary.post :message => "Hello, mikutter!"
mikutterでツイートするときは,タイトルにもあるように次のように入力します.
Service.primary.post :message => "Hello, mikutter!"
さて,Service.primary.postは何をしているのでしょうか? mikutter/core/service.rb をのぞいてみましょう!
class Service include ConfigLoader # MikuTwitter のインスタンス attr_reader :twitter # 現在ログイン中のアカウント @@services = Set.new def self.services_refresh Service.new if(@@services.empty?) end # 存在するServiceオブジェクトをSetで返す。 # つまり、投稿権限のある「自分」のアカウントを全て返す。 def self.all Service.services_refresh @@services.dup end class << self; alias services all end def self.primary services.first end class << self; alias primary_service primary end
Serviceクラスには@@servicesというクラス変数があり,ここにログイン中のアカウント情報を格納しているようです. Service.primary はこの中の最初のインスタンスを指定しています.@@servicesに複数のServiceインスタンスを格納すれば,複垢対応も簡単にできそうですね.そして,上の方にコメントがある MikuTwitter が気になります.
さて,次は Service.primary.post に進みます.Serviceクラスの中にpostメソッドが定義されています.同じく mikutter/core/service.rb の中をのぞいてみます.
# なんかコールバック機能つける # Deferred返すから無くてもいいんだけどねー def self.define_postal(method, twitter_method = method, &wrap) function = lambda{ |api, options, &callback| if(callback) callback.call(:start, options) callback.call(:try, options) api.call(options).next{ |res| callback.call(:success, res) res }.trap{ |exception| callback.call(:err, exception) callback.call(:fail, exception) callback.call(:exit, nil) Deferred.fail(exception) }.next{ |val| callback.call(:exit, nil) val } else api.call(options) end } if block_given? define_method(method){ |*args, &callback| wrap.call(lambda{ |options| function.call(twitter.method(twitter_method), options, &callback) }, self, *args) } else define_method(method){ |options, &callback| function.call(twitter.method(twitter_method), options, &callback) } end end define_postal(:update){ |parent, service, options| parent.call(options).next{ |message| notice 'event fire :posted and :update by statuses/update' Plugin.call(:posted, service, [message]) Plugin.call(:update, service, [message]) message } }
このあたりでupdateメソッドを定義しています.読みにくいですが,
class Foo define_method(:method_name) { |args..| ... } end
の形でFooクラスにmethod_nameメソッドを定義することができます.ここではこの形でupdateメソッドを定義しています. define_postal(:update){...} からがメソッド定義開始になります.主要部分だけを抜き出すと,だいたい次のような形に展開できます.
def update(options, &callback) callback.call(:start, options) #APIを叩くよー callback.call(:try, options) #試してみる.Javaで言うtryブロック的なやつ deferred = twitter.update(options) #APIを叩くとDeferredが戻ってくる deferred.next { |res| #成功したときのブロックを記述 callback.call(:success, res) res }.trap{ |exception| #失敗したとき.Javaで言うcatchブロック的なやつ callback.call(:fail, exception) Deferred.fail(exception) } end
ここで,Deferredは mikutter/core/lib/deferred/ に定義されているクラスです.ネットワークを介しているので,リクエストの結果が戻ってくるまで時間がかかりますが,Deferredを使えばリクエストを別スレッドで実行して,あとから結果を取り出すことができます.
twitter.update(options) でTwitterAPIを叩いているように見えますね.twitterはMikuTwitterのインスタンスです.MikuTwitterはTwitterAPIにアクセスするための機能を提供しています. mikutter/core/mikutwitter/api_shortcuts.rb を見ると,APIを叩いてツイートしている部分が見つかります.
def update(message) text = message[:message] replyto = message[:replyto] receiver = message[:receiver] data = {:status => text } data[:in_reply_to_user_id] = User.generate(receiver)[:id].to_s if receiver data[:in_reply_to_status_id] = Message.generate(replyto)[:id].to_s if replyto (self/'statuses/update').message(data) end alias post update
引数で受け取っているmessageはMessageオブジェクトですね.text = message[:message]はツイート本文です.textを追っていくと,最終的に (self/'statuses/update').message(data) のdataに入っていくことが分かります. self/'statuses/update' は不思議な形をしていますが, mikutter/core/mikutwitter/api_call_support.rb を見ると,MikuTwitterには / メソッドがあり,リクエストを作成していることが分かります.
# APIのパスを指定する。 # 例えば、 statuses/show/1234567890.json?include_entities=true を叩きたい場合は、以下のように書く。 # (twitter/:statuses/:show/1234567890).json include_entities: true def /(api) Request.new(api, self) end
messageメソッドでdataを引数に渡していますが,ツイートするだけなら data は最小限で構わないようです.つまり,ここまでの結果を見ると,
(Service.primary.twitter/'statuses/update').message({:status => "Hello, mikutter!"})
でツイートできるようです. https://api.twitter.com/1.1/statuses/update.json を見ると,確かにstatusだけがrequiredになっています.ここのmessageメソッドは mikutter/core/lib/mikutwitter/api_call_support.rb 中に定義されています.
defparser :user, :users, Users defparser :message, :messages, Messages defparser :list defparser :id defparser :direct_message
defparser メソッドを呼び出していますが,先ほどの define_postal と同様に,defparserメソッド内でdefine_methodを呼んでいます.
def self.defparser(uni, multi = :"#{uni}s", container = Array, defaults = {}) parser = lazy{ MikuTwitter::ApiCallSupport::Request::Parser.method(uni) } define_method(multi){ |options = {}| type_strict options => Hash json(defaults.merge(options)).next{ |node| Thread.new{ container.new(node.map(&parser)).freeze } } } define_method(uni){ |options = {}| type_strict options => Hash json(defaults.merge(options)).next{ |node| Thread.new{ parser.call(node) } } } define_method(:"paged_#{multi}"){ |options| type_strict options => Hash json(defaults.merge(options)).next{ |node = {}| Thread.new { node[multi] = node[multi].map(&parser) node } } } end
さて,jsonメソッドでは twitter.api(api, options, force_oauth) を呼んでいます.
# APIリクエストを実際に発行する # ==== Args # [options] API引数(Hash) # ==== Return # Deferredのインスタンス def json(options) type_strict options => Hash twitter.api(api, options, force_oauth).next{ |res| Thread.new{ JSON.parse(res.body).symbolize } } end
twitter.api は mikutter/core/lib/mikutwitter/query.rb に定義されています.
# 別のThreadで MikuTwitter::Query#query! を実行する。 # ==== Args # MikuTwitter::Query#query! と同じ # ==== Return # Deferredのインスタンス def api(api, options = {}, force_oauth = false) type_strict options => Hash promise = Thread.new do query!(api, options, force_oauth) end promise.abort_on_exception = false promise end
query!メソッドを呼んでいますね.
# APIを叩く # ==== Args # [method] メソッド。:get, :post, :put, :delete の何れか # [api] APIの種類(文字列) # [options] # API引数。ただし、以下のキーは特別扱いされ、API引数からは除外される # :head :: HTTPリクエストヘッダ(Hash) # [force_oauth] 互換性のため # ==== Return # API戻り値(HTTPResponse) # ==== Exceptions # TimeoutError, MikuTwitter::Error def query!(api, options = {}, force_oauth = false) type_strict options => Hash resource = ratelimit(api.to_s) if resource and resource.limit? raise MikuTwitter::RateLimitError.new("Rate limit #{resource.endpoint}", nil) end method = get_api_property(api, options, method_of_api) || :get url = if options[:host] "http://#{options[:host]}/#{api}.json" else "#{@base_path}/#{api}.json" end res = _query!(api, options, method, url) if('2' == res.code[0]) res else raise MikuTwitter::Error.new("#{res.code} #{res.to_s}", res) end rescue MikuTwitter::RateLimitError => e # 変数 resource の情報は振るい可能性がある(他のTwitterクライアントが同じエンドポイントを使用した時等) Plugin.call(:mikutwitter_ratelimit, self, ratelimit(api.to_s)) raise e end
内部でさらに_query!メソッドを呼んでいますね.
# query! の本質的な部分。単純に query_with_oauth! を呼び出す def _query!(api, options, method, url) query_uri = (url + get_args(options)).freeze MikuTwitter::Query.api_lock(query_uri) { cache(api, url, options, method) { retry_if_fail(method, query_uri){ fire_request_event(api, url, options, method) { query_with_oauth!(method, url, options) } } } } end
さらにさらにquery_with_oauth!を呼んでいます. mikutter/core/lib/mikutwitter/connect.rb の中に定義部分があります.
def query_with_oauth!(method, url, options = {}) if [:get, :delete].include? method path = url + get_args(options) res = access_token.__send__(method, path, options[:head]) else path = url query_args = options.melt head = options[:head] query_args.delete(:head) res = access_token.__send__(method, path, query_args, head) end if res.is_a? Net::HTTPResponse case res.code when '200' when '401' notice "#{res.code} Authorization failed." notice res.body notice "trigger request: #{path}" begin errors = (JSON.parse(res.body)["errors"] rescue nil) errors.each { |error| notice error if [INVALID_OR_EXPIRED_TOKEN].include? error["code"] atoken = authentication_failed_action(method, url, options, res) notice atoken return query_with_oauth!(method, url, options) if atoken end } rescue Exception => e notice e end when '429' raise MikuTwitter::RateLimitError.new("Rate limit #{url}", nil) end end res end
ツイートするにはaccess tokenが必要ですね.
attr_accessor :consumer_key, :consumer_secret, :a_token, :a_secret, :oauth_url def initialize(*a, &b) @oauth_url = 'https://twitter.com' super(*a, &b) end def consumer(url=oauth_url) OAuth::Consumer.new(consumer_key, consumer_secret, :site => url) end def access_token(url=@oauth_url) OAuth::AccessToken.new(consumer(url), a_token, a_secret) end
やっとURLを叩けるようになりました.ここまできてやっとツイートができるんですね.
mikutterで適当なAPIを叩く方法
TwitterのAPIリファレンスを見れば,実際にAPIを叩くことができるようになります.ここでは例としてプロフィールアイコンと名前を設定してみます.APIは https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image にまとめられています.これを見ると,Base64エンコードされた700kB以下のGIF,JPG,PNG画像であればアップロードできるようです.基本的にRequestオブジェクトを作成してjsonメソッドを呼べばよいので,割と簡単に実装できます.
require 'base64' (Service.primary.twitter/'account/update_profile_image').json(:image => Base64.encode64(open('path/to/icon.png').read))
こんな感じでアイコンが変更できます.名前の変更も同様に叩けます.
(Service.primary.twitter/'account/update_profile').json(:name => "ぺんぎんさんだよー")
TwitterAPIが手で叩けるTwitterクライアントはたぶんmikutterだけなので,名前変更芸やアイコン芸が簡単にできそうですね!
mikutterの投稿ボックスを強化するプラグイン: mikutter_shell_post
mikutter_shell_postとは
特定の宛先に向けたリプライをコマンドとして解釈し,実行するプラグインです.C++やHaskellのコードをコンパイルして実行したりもできます.また,式展開機能もあるので,気軽に使えるプラグインでもあります.現在存在している中では,おそらく最も気持ち悪い部類のmikutterプラグインの1つでしょう.
インストール方法
下記のコマンドを実行するだけです.
mkdir -p ~/.mikutter/plugin cd ~/.mikutter/plugin git clone git://github.com/penguin2716/mikutter_shell_post.git
機能紹介
mikutterコマンドの実行
@systemに向けたリプライはmikutter内部で実行され,実行結果がシステムメッセージとしてタイムラインに流れてきます.
@system 10.times do |x| Plugin.call(:update, nil, [Message.new(:message => "count#{x}", :system => true)]) end
シェルコマンドの実行
@shellに向けたリプライはシェルコマンドとして認識され,シェルでの実行結果がシステムメッセージとしてタイムラインに流れてきます.
@shell uname -a
@shellに向けたコマンドには自動的にtimeout 10が先頭についているので,pingのようなコマンドをうっかり叩いてしまってもちゃんと終了します.これでは端末を開いたりするときに都合が悪くなるので,@shell_p宛のコマンドはtimeoutしないようになっています.
@shell_p urxvt
Ruby,Python,Perlの実行
@shell_rb,@shell_py,@shell_plに向けたリプライはそれぞれRuby,Python,Perlのコマンドとして解釈されます.
@shell_rb 10.times do |x| puts "count #{x}" end
@shell_py for i in range(10): print "count%d" % i
@shell_pl for ($i = 0; $i < 10; $i++) { print "count$i\n"; }
C,C++のコードの実行
@shell_cと@shell_cppにむけたリプライはC,C++のコードとして解釈され,コンパイル後に実行されます.
@shell_c #include <stdio.h> int main(int argc, char **argv) { printf("Hello, world!\n"); return 0; }
@shell_cpp #include <iostream> using namespace std; int main(int argc, char **argv) { cout << "Hello, world!" << endl; return 0; }
任意のスクリプト言語の実行
@script 実行コマンド コード...
の形式で
@script gnuplot -p v0 = 0 g = 9.8 set yrange [0:10] set xrange [0:2] set xlabel "time [sec]" set ylabel "height [m]" set title "Free Fall" unset key plot v0 * x + 0.5 * (-g) * x **2 + 10 linewidth 2
任意のコンパイル言語の実行
@compile [保存するファイル名] [実行時のコマンド] コンパイルコマンド ソース...
の形式で投稿すると,ソースがファイルに書かれてコンパイルされ,実行されます.実行結果はmikutterのシステムメッセージとしてタイムラインに流れてきます.
@compile [src.cpp] [timeout 10 ./a.out] g++ -O3 -march=native #include <iostream> using namespace std; int main(void) { cout << "Hello, world!" << endl; return 0; }
ちなみに,コンパイルに失敗すると次のようなシステムメッセージがタイムラインに流れます.
src.cpp: In function ‘int main()’: src.cpp:7:1: error: expected ‘;’ before ‘}’ token
@compile [Hello.java] [java Hello] javac import java.awt.*; import java.awt.event.*; import javax.swing.*; class Hello { public static void main(String args[]) { JFrame frame = new JFrame("Java on mikutter"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.setSize(250,100); JButton button = new JButton("Exit"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); frame.add(new JLabel("Java on mikutter"), BorderLayout.NORTH); JPanel panel = new JPanel(); panel.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 20)); panel.add(button); frame.add(panel, BorderLayout.CENTER); frame.setVisible(true); } }
Google検索とか
ブラウザとの連携もできます.Googleでmikutterを検索するには
@google mikutter
東京から京都までの電車を調べるには
@maps 東京から京都
特定のURLを開くには
@openurl http://bit.ly/teokure
のように入力して
式展開機能
Rubyでは,文字列中等で#{...}のような括弧の中に式を記述すると,内部の式が先に評価されます.シェルスクリプトでいうバッククォートと似ていますね.このプラグインをインストールすると,式展開機能が使えるようになります.
次のように入力して
2 ** 10 = #{2 ** 10} 2 ** 20 = #{2 ** 20} 2 ** 30 = #{2 ** 30} 2 ** 40 = #{2 ** 40} 2 ** 50 = #{2 ** 50}
すると,#{2 ** 10}が展開され,次のようになります.
2 ** 10 = 1024 2 ** 20 = 1048576 2 ** 30 = 1073741824 2 ** 40 = 1099511627776 2 ** 50 = 1125899906842624
mikutterにEmacsっぽい機能を提供するプラグイン: gtk_emacslike_textview
gtk_emacslike_textviewとは
mikutterの投稿ボックスを強化する,Emacsユーザのためのmikutterプラグインです.投稿ボックスでツイートを編集する際に思わず
インストール方法
mikutterのバージョンが0.2.1.1119以上である必要があります.それ以前のバージョンでは動作しません.また,Gtk::SourceViewが必要になります.以下のコマンドでインストールして下さい.
gem install gtksourceview2
git_emacslike_textviewのインストールは以下のコマンドで行ってください.コマンドを実行したら,mikutterを再起動すれば投稿ボックスがEmacsっぽくなっていると思います.
mkdir -p ~/.mikutter/plugin cd ~/.mikutter/plugin git clone git://github.com/penguin2716/gtk_emacslike_textview.git
機能紹介
Emacsっぽいキーバインド
次のコマンドが利用できます.キーバインドがユーザ定義となっているものはmikutterの設定画面から設定して下さい.
キーバインド | 動作内容 |
C-[fbnpae] | 文字単位のカーソルの移動 |
C-[dh] | 文字の削除 |
C-SPC | 選択のトグル |
C-[/z] | 戻る |
C-w | 選択領域のカット |
C-k | 行末までカット |
C-y | カーソル位置に貼り付け |
C-g | 選択のトグルをOFF |
C-A | C-aの全選択がなくなったのでC-Aで全選択にしてみた |
M-[fb] | 単語単位でのカーソル移動 |
M-[ae] | バッファの先頭/末尾に移動 |
M-w | 選択領域のコピー |
M-[dh] | 単語単位の削除 |
M-[np] | グローバルスタックの読み出し |
ユーザ定義 | ハイライトする言語の変更(下記参照) |
ユーザ定義 | snippetを展開(下記参照) |
以下では説明のため,「ハイライトする言語の変更」を
キーワードハイライト
投稿ボックスに次の内容を入力し,
@@ruby
同様に,次の内容で
@@java
ハイライトしないモードに戻す場合は,次のように存在しない言語を@@に続けて入力し,
@@nil
snippet
snippet機能は,特定のキーワードのあとにコマンドを呼ぶと,そのキーワードがあらかじめ定義された内容に展開される機能です.試しに,以下の内容を投稿ボックスに入力して
chello
すると,以下のような内容が展開されます.
#include <stdio.h> int main(int argc, char **argv) { printf("Hello, world!\n"); return 0; }
このように,自分でsnippetを追加すれば,簡単に文章を作れるようになります.
snippetはsnippetsディレクトリ以下に保存されており,ユーザによる追加が可能です.新しくsnippetを登録する場合は,キーワードをファイル名とし,展開したい内容をファイルに記述して下さい.その際,ファイルの中に'$0'という文字列が含まれている場合は,展開後にその位置にカーソルが挿入されます.ファイルを追加したら,mikutterを再起動すると新しいsnippetが読み込まれます.
CloudStackで研究室内プライベートクラウドを作ってみた
この記事は CloudStack Advent Calendar jp:2012 のために書かれたものです.
研究室内にプライベートクラウドをつくる意味
研究を行う上で,サーバ環境を構築するのは面倒で悩ましいことです.
研究室内にプライベートクラウドを構築することで,研究が劇的に加速します.
以下ではプライベートクラウドを構築することによる利点をいくつか紹介します.
究極の使い捨て環境として
「研究に失敗はつきものです.失敗したら捨てればいいんです」
ある日,僕は研究室内Wikiを作ろうと思いましたが,どのWikiがいいのかわかりません.
結局いくつかインストールしてみましたが,気に入らないものもありました.
アンインストールするにも,綺麗な環境に戻るかどうかはわかりません.
また,インストールログを取ろうとした場合には,新しい環境でもう一度作業が必要になります.
またOSのインストールに手戻りするのは面倒なことこの上ありません.
そこで,テンプレートから新しい仮想マシンを作れば,再インストールの手間なしに15秒で環境が用意できます.
失敗したり不要になったりしたら,気軽に捨てて新しいのを用意すれば良いのです.
複数のサーバをCloudStackでまとめる
「1つの機能しか提供していないサーバが複数ありませんか?」
サーバを利用する際に,単純な機能しか提供しないなら,それは消費電力の無駄です.
サーバが待機していても電気は延々と使っているのですから,それが削減できればどれだけ省電力なことでしょう.
研究用のサーバが瞬時に用意できる
「大量のマシンが必要だけど用意が面倒?テンプレートを使えば一瞬ですね」
複数のマシンを使った並列計算の実験環境を用意するのは骨が折れるもの.インストールも面倒です.
そんなとき,1台だけ共通の設定を行い,テンプレートを作りましょう.
必要な分だけ仮想マシンを使えば量産は瞬時に完了します.
Linuxやその他のOSを気軽に利用できる
「Linuxとか使ってみたいけど自分のマシンにはインストールしたくない?」
仮想マシンを利用すれば自分の環境は汚さなくても良いのです.
VirtualBoxやVMwareさえもインストールの必要はありません.
お気に入りのブラウザを開いてCloudStackにアクセスするだけで,綺麗なWebUIから気軽にアクセスできますね.
構成
研究室内プライベートクラウドを構築した際の環境を示します.
下記の構成で現在も絶賛安定稼働中です.
CloudStackのバージョンは4.0を使用しました.
機器構成
- 管理サーバ:ThinkPadEdge E430 を使用
- コンピューティングノード(自作マシン3台)
- プライマリ/セカンダリストレージ(1台構成)
ネットワーク構成
研究室毎にクラスCのIPアドレス空間が割り当てられており,1つの研究室あたり255個のIPアドレスしかありません.
そのうちのほとんどは既に利用・予約されているため,新たにクラスBのLANを構築しました.
既存LAN内のIPアドレス消費数は1ですが,65000以上のIPアドレスが使えるようになりました.
利用用途と利用傾向
現在,研究室の15-20名程度がCloudStackのアカウントを作成し,仮想マシンを起動しています.
以下では,多く見られる利用用途と利用傾向について紹介します.
VNCでデスクトップを表示して利用している人が多い
VNCがすぐに利用できるよう設定済みのUbuntuテンプレートをこちらで用意しました.
普段はWindowsやMacを利用している人が多いため,CUI画面には抵抗があるようです.
うちの研究室にはMac利用者が多いため,VNCが標準で利用できます.
WebUIからアクセスするよりも直接VNCでアクセスしたほうが快適に利用できるため,
GUIを使いたい場合はWebUIからのアクセスよりもVNCでのアクセスをおすすめしています.
膨大な実験データが出力される人の解析用
研究によっては膨大な実験データを解析する必要があります.
そんな人は,複数の仮想マシンを用意して,そこにデータを分割して投げています.
解析中に自分のマシンが使えない,あるいはCPU使用率が専有されることがなくなるため,
データの解析中でも他のタスクにマシンを使えます.
ファイルサーバとして
実験データを保存する用途に限らず,ファイルサーバは便利なものです.
USBメモリにデータを保存するよりも,仮想マシン上のファイルサーバに保存したほうが楽ではないでしょうか.
あとはネットワーク経由でアクセスできるため,USBの上下を間違えてイライラすることもありません.
また,自分のマシンはRAID構成にしていなくても,仮想マシンはRAID構成になっているので安心です.
ファイルサーバとして利用している人は100GBから200GBくらいストレージを確保する傾向があるようなので,
ファイルサーバ用途が多く出そうな環境では,ストレージ容量はある程度多めに見積もった方が良いでしょう.
MPIやHadoopを使ったプログラムのテスト
OpenMPIを使った並列計算環境を物理マシンで用意しようとすると,
マシンの確保だけでなく,OSのインストールもしんどいものです.
テスト環境をつくるだけなら,テンプレートを使って並列計算の実験環境が瞬時に構築できます.
早急に実験環境を用意しなければならなかった人がいたのですが,
テンプレートを活用することで,実験環境の構築が一瞬で終わりました.
今後の展望:究極のシンクライアント環境
ライブマイグレーションとVNCやX11Forwarding等のデスクトップ転送技術を組み合わせれば,
どこにいても高速アクセス可能な究極のシンクライアント環境が実現できます.
必要な要素は以下の通りです.
クラウド上の仮想マシンにメイン環境を用意する
普段利用するPC環境をクラウド上の仮想マシンに用意します.
もうディスクが壊れる心配をしなくて良いだけでなく,世界中どこからでもアクセスできるようになります.
認証方法を工夫すれば,IC社員証をFelicaポートに載せるだけで
瞬時に自分の環境にログインできるようなシステムが構築できます.
いつでも,どこでも,アクセスできるユビキタスクラウド環境が目標です.
おわりに
僕が研究室で構築したプライベートクラウド環境の概要と,
今後実現できそうな(?)クラウドの利用用途を紹介しました.
クラウド環境を利用することによるメリットはかなり大きいと思うので,
様々な場面で利用できるようなユースケースがあると良いですね.
!追記!
コミックマーケット83では「俺のクラウドがこんなに安いわけがない」というタイトルで薄い本を販売します!
研究室内のメンバー数人で記事を寄せ集めたものです.
すべての記事がクラウドに関連しているわけではありませんが,楽しんで読んで頂けるのではないかと思います.
予価500円です!
-
-
- -
-
期日:12月31日(月)
会場:東京ビッグサイト
サークル名:らぼちっく;げーと
出展位置:東2ホール Y-17a
URL:http://www.laboticgate.net/
-
-
- -
-
にて販売しておりますので,ぜひお立ち寄り下さい.
お待ちしております!