class Object

Constants

APIKEY
BIRD_CACHE_PATH
BIRD_URL
BOOT_TIME
CACHE_DIR
CacheWriteThread
DEFAULT_SIZE
DEFAULT_SOUND_DIRECTORY
DEFINED_TIME
FOLLOW_DIR
HYDE

基本的な単位であり、数学的にも重要なマジックナンバーで、至るところで使われる。 これが言語仕様に含まれていないRubyは正直気が狂っていると思う。 ja.uncyclopedia.info/wiki/Hyde

PREFETCH_MONTH
RUBY_VERSION_ARRAY

Rubyのバージョンを配列で。あると便利。

SavedSearch
SerialThread

SerialThreadGroup のインスタンス。 同時実行数は1固定

TABPOS
USER

Public Class Methods

backtrace() click to toggle source
# File core/plugin/bugreport.rb, line 109
def self.backtrace
  "#{crashed_exception.class} #{crashed_exception.to_s}\n" +
    crashed_exception.backtrace.map{ |msg| msg.gsub(FOLLOW_DIR, '{MIKUTTER_DIR}') }.join("\n")
end
boot() click to toggle source
# File core/plugin/bitly/bitly.rb, line 117
def self.boot
  plugin = Plugin::create(:bitly)
  plugin.add_event(:boot){ |service|
    Plugin.call(:regist_url_shrinker_setting, 'bit.ly', main(service)) }
end
crashed_exception() click to toggle source
# File core/plugin/bugreport.rb, line 114
def self.crashed_exception
  @crashed_exception ||= object_get_contents(File.expand_path(File.join(Environment::TMPDIR, 'crashed_exception'))) rescue nil
end
define_periodical_executer(api, interval, count, &success) click to toggle source
# File core/plugin/rest/rest.rb, line 5
def self.define_periodical_executer(api, interval, count, &success)
  counter = UserConfig[interval]
  lambda{ |service|
    counter += 1
    if counter >= UserConfig[interval]
      counter = 0
      service.call_api(api, count: UserConfig[count]){ |messages|
        success.call(service, messages) if messages and not messages.empty? } end } end
defnotify(label, kind) click to toggle source
# File core/plugin/notify.rb, line 8
def self.defnotify(label, kind)
  settings (label) do
    boolean 'ポップアップ', "notify_#{kind}".to_sym
  fileselect('サウンド', "notify_sound_#{kind}".to_sym, DEFAULT_SOUND_DIRECTORY) end end
defun(method_name, *args, &proc) click to toggle source
# File core/utils.rb, line 424
def self.defun(method_name, *args, &proc)
  define_method(method_name, &tclambda(*args, &proc)) end
encode_parameters(params, delimiter = '&', quote = nil) click to toggle source
# File core/plugin/bugreport.rb, line 118
def self.encode_parameters(params, delimiter = '&', quote = nil)
  if params.is_a?(Hash)
    params = params.map do |key, value|
      "#{escape(key)}=#{quote}#{escape(value)}#{quote}"
    end
  else
    params = params.map { |value| escape(value) }
  end
  delimiter ? params.join(delimiter) : params
end
escape(value) click to toggle source
# File core/plugin/bugreport.rb, line 129
def self.escape(value)
  URI.escape(value.to_s, %r[^a-zA-Z0-9\-\.\_\~]/)
end
imsorry() click to toggle source
# File core/plugin/bugreport.rb, line 53
def self.imsorry
  "#{Environment::NAME} が突然終了してしまったみたいで ヽ('ω')ノ三ヽ('ω')ノもうしわけねぇもうしわけねぇ\n"+
    'OKボタンを押したら、自動的に以下のテキストが送られます。これがバグを直すのにとっても'+
    '役に立つんですよ。よかったら送ってくれません?'
end
main(watch) click to toggle source
# File core/plugin/bitly/bitly.rb, line 123
def self.main(watch)
  ft = Mtk.accountdialog_button('bit.ly アカウント設定',
                                :bitly_user, 'ユーザ名',
                                :bitly_apikey, 'APIキー'){ |user, pass|
    if(pass == '' and user == '')
      true
    else
      query = "/v3/validate?x_login=#{user}&x_apiKey=#{pass}&apiKey=#{APIKEY}"+
        "&login=#{USER}&format=json"
      begin
        result = JSON.parse(Net::HTTP.get("api.bit.ly", query))
        notice result.inspect
        result['data']['valid'].to_i == 1
      rescue JSON::ParserError
        nil end end }
  Gtk::VBox.new(false, 8).closeup(Gtk::IntelligentTextview.new(NAVIGATION)).closeup(ft) end
mikutter_error() click to toggle source
# File core/plugin/bugreport.rb, line 104
def self.mikutter_error
  name = File.expand_path(File.join(Environment::TMPDIR, 'mikutter_error'))
  if FileTest.exist?(name)
    file_get_contents(name) end end
notify(user, text) click to toggle source
# File core/plugin/notify.rb, line 81
def self.notify(user, text)
  Plugin.call(:popup_notify, user, text) end
notify_sound(sndfile) click to toggle source
# File core/plugin/notify.rb, line 84
def self.notify_sound(sndfile)
  if sndfile.respond_to?(:to_s) and FileTest.exist?(sndfile.to_s)
    Plugin.call(:play_sound, sndfile) end end
popup() click to toggle source
revision() click to toggle source
# File core/plugin/bugreport.rb, line 98
def self.revision
  begin
    open('|env LC_ALL=C svn info').read.match(%rRevision\s*:\s*(\d+)/)[1]
  rescue
    '' end end
send() click to toggle source
# File core/plugin/bugreport.rb, line 67
def self.send
  Thread.new{
    begin
      exception = crashed_exception
      m = exception.backtrace.first.match(%r(.+?):(\d+)/)
      crashed_file, crashed_line = m[1], m[2]
      Net::HTTP.start('mikutter.hachune.net'){ |http|
        param = {
          'backtrace' => JSON.generate(exception.backtrace.map{ |msg| msg.gsub(FOLLOW_DIR, '{MIKUTTER_DIR}') }),
          'svn' => revision,
          'file' => crashed_file.gsub(FOLLOW_DIR, '{MIKUTTER_DIR}'),
          'line' => crashed_line,
          'exception_class' => exception.class,
          'description' => exception.to_s,
          'ruby_version' => RUBY_VERSION,
          'rubygtk_version' => Gtk::BINDING_VERSION.join('.'),
          'platform' => RUBY_PLATFORM,
          'url' => 'exception',
          'version' => Environment::VERSION }
        console = mikutter_error
        param['stderr'] = console if console
        eparam = encode_parameters(param)
        http.post('/', eparam) }
      File.delete(File.expand_path(File.join(Environment::TMPDIR, 'mikutter_error'))) rescue nil
      File.delete(File.expand_path(File.join(Environment::TMPDIR, 'crashed_exception'))) rescue nil
      Plugin.activity :system, "エラー報告を送信しました。ありがとう♡"
    rescue TimeoutError, StandardError => e
      Plugin.activity :system, "ピャアアアアアアアアアアアアアアアアアアアアアアアwwwwwwwwwwwwwwwwwwwwww"
      Plugin.activity :error, e.to_s, exception: e
    end } end

Public Instance Methods

__write_stderr(msg) click to toggle source
# File core/utils.rb, line 349
def __write_stderr (msg)
  $stderr.write(msg.gsub(FOLLOW_DIR, '{MIKUTTER_DIR}')+"\n")
end
_popup(watch) click to toggle source
# File core/plugin/change_account.rb, line 22
def _popup(watch)
  result = [nil]
  main_windows = Plugin.filtering(:get_windows, Set.new).first
  alert_thread = if(Thread.main != Thread.current) then Thread.current end
  dialog = ::Gtk::Dialog.new(Environment::NAME + " ログイン")
  container, key, request_token = main(watch, dialog)
  dialog.set_size_request(600, 400)
  dialog.window_position = ::Gtk::Window::POS_CENTER
  dialog.vbox.pack_start(container, true, true, 30)
  dialog.add_button(::Gtk::Stock::OK, ::Gtk::Dialog::RESPONSE_OK)
  dialog.default_response = ::Gtk::Dialog::RESPONSE_OK
  quit = lambda{
    dialog.hide_all.destroy
    ::Gtk.main_iteration_do(false)
    main_windows.each{ |w| w.show }
    if alert_thread
      alert_thread.run
    else
      ::Gtk.main_quit end }
  dialog.signal_connect("response") do |widget, response|
    if response == ::Gtk::Dialog::RESPONSE_OK
      begin
        access_token = request_token.get_access_token(:oauth_token => request_token.token,
                                                      :oauth_verifier => key.text)
        UserConfig[:twitter_authenticate_revision] = Environment::TWITTER_AUTHENTICATE_REVISION
        UserConfig[:twitter_token], UserConfig[:twitter_secret] = access_token.token, access_token.secret
        result = [access_token.token, access_token.secret]
        quit.call
      rescue => e
        Mtk.alert("暗証番号が違うみたいです\n\n#{e}")
      end
    else
      quit.call end end
  dialog.signal_connect("destroy") {
    false
  }
  container.show
  dialog.show_all
  main_windows.each{ |w| w.hide }
  if(alert_thread)
    Thread.stop
  else
    ::Gtk::main end
  result end
add_muted_user(user) click to toggle source
# File core/plugin/profile/profile.rb, line 110
def add_muted_user(user)
  type_strict user => User
  atomic{
    muted = (UserConfig[:muted_users] ||= []).melt
    muted << user.idname
    UserConfig[:muted_users] = muted } end
add_tab(saved_search) click to toggle source

タブを保存する

Args

saved_search

saved search

# File core/plugin/saved_search/saved_search.rb, line 40
def add_tab(saved_search)
  type_strict saved_search => SavedSearch
  tab(saved_search.slug, saved_search.name) do
    set_icon MUI::Skin.get("savedsearch.png")
    timeline saved_search.slug end
  rewind_timeline(saved_search)
  register_cache(saved_search)
  timelines[saved_search.id] = saved_search end
addsupport(cond, element_rule = {}, &block) click to toggle source
# File core/plugin/openimg/openimg.rb, line 149
def addsupport(cond, element_rule = {}, &block)
  element_rule.freeze
  if block == nil
    ::Gtk::TimeLine.addopenway(cond){ |shrinked_url, cancel|
      url = MessageConverters.expand_url_one(shrinked_url)
      Delayer.new(Delayer::NORMAL, Thread.new{ imgurlresolver(url, element_rule) }){ |url|
        display(url, cancel)
      }
    }
  else
    ::Gtk::TimeLine.addopenway(cond){ |shrinked_url, cancel|
      url = MessageConverters.expand_url_one(shrinked_url)
      Delayer.new(Delayer::NORMAL, Thread.new{
                    imgurlresolver(url, element_rule){ |url| block.call(url, cancel) }
                  }) {|url|
        display(url, cancel)
      }
    }
  end
end
already_exists_another_instance?() click to toggle source
# File core/chi.rb, line 39
def already_exists_another_instance?
  if FileTest.exist? Environment::PIDFILE then
    open(Environment::PIDFILE){|out|
      pid = out.read
      if pid_exist?(pid) then
        notice "process #{pid} already exist"
        return true
      else
        File::delete(Environment::PIDFILE)
        notice "pid file exist. however, process #{pid} not found"
        return false
      end
    }
    error 'pid file can\t open'
    exit
  else
    notice 'pid file not found'
  end
  return false
end
appear_counter() click to toggle source
# File core/plugin/image_file_cache/image_file_cache.rb, line 36
def appear_counter
  @appear_counter ||= TimeLimitedStorage.new end
appear_limit() click to toggle source

キャッシュする出現回数のしきい値を返す

# File core/plugin/image_file_cache/image_file_cache.rb, line 40
def appear_limit
  UserConfig[:image_file_cache_appear_limit] || 32 end
append_message(source, messages) click to toggle source
# File core/plugin/extract/extract.rb, line 69
def append_message(source, messages)
  type_strict source => String, messages => Enumerable
  tabs = extract_tabs.values.select{ |r| r[:sources] && r[:sources].include?(source) }
  return if tabs.empty?
  messages.each{ |message|
    message = message.retweet_source if message.retweet_source
    table = MIKU::SymbolTable.new(nil,
                                  :user => MIKU::Cons.new(message.idname, nil),
                                  :body => MIKU::Cons.new(message.to_s, nil),
                                  :source => MIKU::Cons.new(message[:sources], nil),
                                  :message => MIKU::Cons.new(message, nil))
    tabs.each{ |record|
      timeline(record[:slug]) << message if miku(record[:sexp], table) } } end
argument_parser() click to toggle source
# File core/chi.rb, line 60
def argument_parser()
  $debug = false
  $learnable = true
  $daemon = false
  $interactive = false
  $quiet = false

  ARGV.each{ |arg|
    case arg
    when '-i' # インタラクティブモード(default:off)
      $interactive = true
    when '--debug' # デバッグモード(default:off)
      $debug = true
      seterrorlevel(:notice)
    when '-d' # デーモンモード(default:off)
      $daemon = true
    when '-l' # タグを学習しない(default:する)
      $learnable = false
    when '-q'
      $quiet = true
    end
  }
end
assert_hasmethods(obj, *methods) click to toggle source

デバッグモードの場合、obj methods に指定されたメソッドをひとつでも持っていない場合、 RuntimeErrorを発生させる。 obj を返す。

# File core/utils.rb, line 317
def assert_hasmethods(obj, *methods)
  if Mopt.debug
    methods.all?{ |m|
      raise RuntimeError, "#{obj.inspect} should have method #{m}" if not obj.methods.include? m
    }
  end
  obj
end
assert_type(type, obj) click to toggle source

デバッグモードの場合、objtype とis_a?関係にない場合、RuntimeErrorを発生させる。 obj を返す。

# File core/utils.rb, line 307
def assert_type(type, obj)
  if Mopt.debug and not obj.is_a?(type)
    raise RuntimeError, "#{obj} should be type #{type}"
  end
  obj
end
atomic() { || ... } click to toggle source

共通のMutexで処理を保護して実行する。 atomicブロックで囲まれたコードは、別々のスレッドで同時に実行されない。

# File core/utils.rb, line 379
def atomic
  # if Thread.current == Thread.main
  #    raise 'Atomic Mutex dont have to block main thread'
  # end
  $atomic.synchronize{ yield }
end
available_lists() click to toggle source

自分がフォローしているリストを返す

Return

自分が作成したリストの配列(TypedArray)

# File core/plugin/list/list.rb, line 169
def available_lists
  @available_lists ||= UserLists.new.freeze end
background_color() click to toggle source
# File core/plugin/profile/profile.rb, line 275
def background_color
  style = ::Gtk::Style.new()
  style.set_bg(::Gtk::STATE_NORMAL, 0xFF ** 2, 0xFF ** 2, 0xFF ** 2)
  style end
bg_system(*args) click to toggle source

コマンドをバックグラウンドで起動することを覗いては system() と同じ

# File core/utils.rb, line 392
def bg_system(*args)
  Process.detach(spawn(*args))
end
biif(a, b, *procs, &last_proc) click to toggle source

複数条件if 条件を二つ持ち、a&b,a&!b,!a&b,!a&!bの4パターンに分岐する procs配列は前から順番に、上記の条件の順番に対応している。 評価されたブロックの戻り値を返す。ブロックがない場合はfalseを返す。 なお、ブロックはa,bを引数に取り呼び出される。 誰得すぎて自分でも使ってないけどどこかで使った気がするなぁ

# File core/utils.rb, line 57
def biif(a, b, *procs, &last_proc)
  procs.push(last_proc)
  num = 0
  if not(a) then
    num += 2
  end
  if not(b) then
    num += 1
  end
  if(procs[num]) then
    procs[num].call(a,b)
  end
end
bool(val) click to toggle source

値が真であるならtrueを返す。遅延評価オブジェクトでも正確に判断することができる。

# File core/lib/lazy.rb, line 101
def bool(val)
  not(val.nil? or val.is_a?(FalseClass)) end
boot() click to toggle source
# File core/chi.rb, line 9
def boot()
  logfile(Environment::TMPDIR + Environment::ACRO)
  argument_parser()
  Service.new(true)
  if(already_exists_another_instance?) then
    error('Already exist another instance')
    exit!
  end
  include File::Constants
  if $daemon then
    WEBrick::Daemon.start{
      main()
    }
  else
    main()
  end
  return true
end
boot!(profile) click to toggle source

イベントの待受を開始する。 profile がtrueなら、プロファイリングした結果を一時ディレクトリに保存する

# File mikutter.rb, line 39
def boot!(profile)
  Gtk.init_add{ Gtk.quit_add(Gtk.main_level){ SerialThreadGroup.force_exit! } }
  if profile
    require 'ruby-prof'
    begin
      notice 'start profiling'
      RubyProf.start
      Gtk.main
    ensure
      result = RubyProf.stop
      printer = RubyProf::CallTreePrinter.new(result)
      profile_out = File.join(File.expand_path(Environment::TMPDIR), 'profile-'+Time.new.strftime('%Y-%m-%d-%H%M%S')+'.out')
      notice "profile: writing to #{profile_out}"
      printer.print(File.open(profile_out, 'w'), {})
      notice "profile: done."
    end
  else
    Gtk.main end
rescue => e
  into_debug_mode(e)
  raise e
rescue Exception => e
  e = Gtk.exception if Gtk.exception
  notice e.class
  raise e
end
boot_event(api, service, created, destroy, followings) click to toggle source
# File core/plugin/followingcontrol/followingcontrol.rb, line 7
def boot_event(api, service, created, destroy, followings)
  unless created.empty?
    Plugin.call("#{api}_created".to_sym, service, created) end
  unless destroy.empty?
    Plugin.call("#{api}_destroy".to_sym, service, destroy) end end
cache_expire() click to toggle source

キャッシュの有効期限を秒単位で返す

# File core/plugin/image_file_cache/image_file_cache.rb, line 44
def cache_expire
  (UserConfig[:image_file_cache_expire] || 7) * 24 * 60 * 60 end
cache_it(image_url) click to toggle source
# File core/plugin/image_file_cache/image_file_cache.rb, line 74
def cache_it(image_url)
  notice "cache image to #{get_local_image_name(image_url)}"
  CacheWriteThread.new {
    raw = Gdk::WebImageLoader.get_raw_data(image_url)
    if(raw)
      notice "broken image. cache failed"
      j_set(image_url)
      image_dir = get_local_dir_name(image_url)
      FileUtils.mkdir_p(image_dir)
      file_put_contents(get_local_image_name(image_url), raw) end } end
caller_util() click to toggle source

utils.rbのメソッドを呼び出した最初のバックトレースを返す

# File core/utils.rb, line 292
def caller_util
  caller.each{ |result|
    return result unless %rutils\.rb/ === result } end
caller_util_all() click to toggle source
# File core/utils.rb, line 296
def caller_util_all
  aflag = false
  result = []
  caller.each{ |c|
    aflag |= %rutils\.rb/ === c
    result << c if aflag }
  result end
changesize(eb, w, url) click to toggle source
# File core/plugin/openimg/openimg.rb, line 13
def changesize(eb, w, url)
  eb.remove(eb.children.first)
  @size = w.window.geometry[2,2].freeze
  eb.add(::Gtk::WebIcon.new(url, *@size).show_all)
  @size end
chi_fatal_alert(msg) click to toggle source

環境や設定の不備で終了する。msgには、何が原因かを文字列で渡す。このメソッドは 処理を返さずにアボートする。

# File core/utils.rb, line 355
def chi_fatal_alert(msg)
  require_if_exist 'gtk2'
  if defined?(Gtk::MessageDialog)
    dialog = Gtk::MessageDialog.new(nil,
                                    Gtk::Dialog::DESTROY_WITH_PARENT,
                                    Gtk::MessageDialog::ERROR,
                                    Gtk::MessageDialog::BUTTONS_CLOSE,
                                    "#{Environment::NAME} エラー")
    dialog.secondary_text = msg.to_s
    dialog.run
    dialog.destroy end
  puts msg.to_s
  abort end
command_exist?(cmd) click to toggle source

UNIXコマンド cmd が存在するか否かを返す。

# File core/utils.rb, line 142
def command_exist?(cmd)
  system("which #{cmd} > /dev/null")
end
confload(file) click to toggle source

YAMLファイルを読み込む。存在しない場合はからのハッシュを返す。 file は、IOオブジェクトかファイル名。

# File core/utils.rb, line 119
def confload(file)
  if(file.is_a?(IO))
    YAML.load(file.read)
  elsif(FileTest.exist?(File.expand_path(file))) then
    YAML.load(file_get_contents(file))
  else
    Hash.new
  end
end
confroot(*path) click to toggle source

Environment::CONFROOT内のファイル名を得る。

confroot(*path)

File::expand_path(File.join(Environment::CONFROOT, *path))

と等価。

# File core/utils.rb, line 46
def confroot(*path)
  File::expand_path(File.join(Environment::CONFROOT, *path))
end
create_pane(i_pane) click to toggle source

ペインを作成

Args

i_pane

ペイン

Return

ペイン(Gtk::Notebook)

# File core/plugin/gtk/gtk.rb, line 474
def create_pane(i_pane)
  notice "create pane #{i_pane.slug.inspect}"
  pane = ::Gtk::Notebook.new
  @slug_dictionary.add(i_pane, pane)
  pane.ssc('key_press_event'){ |widget, event|
    Plugin::GUI.keypress(::Gtk::keyname([event.keyval ,event.state]), i_pane) }
  pane.ssc(:destroy){
    i_pane.destroy if i_pane.destroyed?
    false }
  pane.show_all end
create_pidfile() click to toggle source
# File core/chi.rb, line 28
def create_pidfile()
  begin
    open(Environment::PIDFILE, WRONLY|CREAT|EXCL){ |output|
      output.write(Process.pid)
    }
  rescue Errno::EEXIST
    error('to write pid file failed.')
    exit
  end
end
create_tab(i_tab) click to toggle source

タブを作成する

Args

i_tab

タブ

Return

Tab(Gtk::EventBox)

# File core/plugin/gtk/gtk.rb, line 128
def create_tab(i_tab)
  notice "create tab #{i_tab.slug.inspect}"
  tab = ::Gtk::EventBox.new.tooltip(i_tab.name)
  @slug_dictionary.add(i_tab, tab)
  tab_update_icon(i_tab)
  tab.ssc(:focus_in_event) {
    i_tab.active!(true, true)
    false
  }
  tab.ssc(:key_press_event){ |widget, event|
    Plugin::GUI.keypress(::Gtk::keyname([event.keyval ,event.state]), i_tab) }
  tab.ssc(:button_press_event) { |this, e|
    if e.button == 3
      Plugin::GUI::Command.menu_pop(i_tab) end
    false }
  tab.ssc(:destroy){
    i_tab.destroy
    false }
  tab.show_all end
debugging_wait() click to toggle source

他のスレッドで#into_debug_modeが呼ばれているなら、それが終わるまでカレントスレッドをスリープさせる

# File core/utils.rb, line 208
def debugging_wait
  if $into_debug_mode
    $into_debug_mode << Thread.current
    Thread.stop end end
deferred(&proc) click to toggle source
# File core/lib/deferred/deferred.rb, line 102
def deferred(&proc)
  Deferred.new.next(&proc) end
delete_cache(id) click to toggle source

保存した検索の情報をキャッシュから削除

Args

id

削除するID

# File core/plugin/saved_search/saved_search.rb, line 98
def delete_cache(id)
  cache = at(:cache, {}).melt
  cache.delete(id)
  store(:cache, cache) end
delete_tab(id) click to toggle source

idに対応するタブを削除

Args

id

saved search の ID

# File core/plugin/saved_search/saved_search.rb, line 52
def delete_tab(id)
  type_strict id => Integer
  saved_search = timelines[id]
  tab(saved_search.slug).destroy if saved_search.slug end
display(url, cancel = nil) click to toggle source
# File core/plugin/openimg/openimg.rb, line 26
def display(url, cancel = nil)
  w = ::Gtk::Window.new.set_title("(読み込み中)")
  w.set_size_request(320, 240)
  w.set_default_size(*@size).move(*@position)
  w.signal_connect(:destroy){ w.destroy }
  eventbox = ::Gtk::EventBox.new
  w.add(eventbox)
  size = DEFAULT_SIZE
  Thread.new{
    url = url.value if url.is_a? Thread
    if not(url) or not(url.respond_to?(:to_s))
      Delayer.new{
        unless w.destroyed?
          if cancel
            w.destroy
            cancel.call
          else
            w.set_title("URLの取得に失敗") end end }
    else
      pixbuf = Gdk::WebImageLoader.loading_pixbuf(*@size)
      raw = Gdk::WebImageLoader.get_raw_data(url){ |data|
        if not eventbox.destroyed?
          if data
            begin
              loader = Gdk::PixbufLoader.new
              loader.write data
              loader.close
              pixbuf = loader.pixbuf
            rescue => e
              pixbuf = Gdk::WebImageLoader.notfound_pixbuf(*@size) end
          else
            pixbuf = Gdk::WebImageLoader.notfound_pixbuf(*@size) end
          eventbox.queue_draw_area(0, 0, *eventbox.window.geometry[2,2]) end }
      if raw and raw != :wait
        loader = Gdk::PixbufLoader.new
        loader.write raw
        loader.close
        pixbuf = loader.pixbuf end
      Delayer.new{
        unless w.destroyed?
          w.set_title(url.to_s)
          eventbox.signal_connect("event"){ |ev, event|
            if event.is_a?(Gdk::EventButton) and (event.state.button1_mask?) and event.button == 1
              w.destroy
              cancel.call if cancel
            end
            false }
          eventbox.signal_connect("expose_event"){ |ev, event|
            redraw(eventbox, pixbuf)
            move(w)
            true }
          eventbox.signal_connect(:"size-allocate"){
            if w.window and size != w.window.geometry[2,2]
              redraw(eventbox, pixbuf)
              size = w.window.geometry[2,2] end }
          redraw(eventbox, pixbuf)
          eventbox end } end }
  w.show_all end
entity_unescape(str) click to toggle source

文字列をエンティティデコードする

# File core/utils.rb, line 387
def entity_unescape(str)
  str.gsub(%r&(.{2,3});/){|s| {'gt'=>'>', 'lt'=>'<', 'amp'=>'&'}[$1] }
end
error(msg) click to toggle source

エラーメッセージを表示する。

# File core/utils.rb, line 178
def error(msg)
  log "error", msg if Mopt.error_level >= 1
  abort if Mopt.error_level >= 4
end
everytime(&proc) click to toggle source

毎回評価オブジェクトを作成する

# File core/lib/lazy.rb, line 93
def everytime(&proc)
  EveryTime.new(&proc) end
extract_tabs() click to toggle source

抽出タブオブジェクト。各キーは抽出タブIDで、値は以下のようなオブジェクト

name

タブの名前

sexp

条件式(S式)

source

どこのツイートを見るか(イベント名、配列で複数)

slug

タイムラインとタブのスラッグ

id

抽出タブのID

# File core/plugin/extract/extract.rb, line 10
def extract_tabs
  @extract_tabs ||= {} end
fetch(t) click to toggle source
# File core/plugin/openimg/openimg.rb, line 193
def fetch(t)
  req = URI.parse(t)
  res = Net::HTTP.new(req.host).request_head(req.path)
  case res
  when Net::HTTPSuccess
    t
  when Net::HTTPRedirection
    fetch(res['location'])
  else
    nil
  end
end
fetch_list_of_service(service, cache=:keep) click to toggle source

service が作成した全てのリストを取得する

Args

service

リストのオーナーのServiceオブジェクト

cache

キャッシュの利用方法

Return

deferred

# File core/plugin/list/list.rb, line 74
def fetch_list_of_service(service, cache=:keep)
  type_strict service => Service
  param = {:cache => cache, :user => service.user_obj}
  service.lists(param).next{ |lists|
    if lists
      set_available_lists(lists)
      tab_reflesh{
        lists.each{ |list|
          tab_mark(list) } } end }.terminate
end
fib(n) click to toggle source
# File core/utils.rb, line 762
def fib(n)
  return n if n < 2
  fib(n-1) + fib(n-2)
end
file_get_contents(fn) click to toggle source

ファイルの内容を文字列に読み込む

# File core/utils.rb, line 87
def file_get_contents(fn)
  open(fn, 'r:utf-8'){ |input|
    input.read
  }
end
file_put_contents(fn, body) click to toggle source

文字列をファイルに書き込む

# File core/utils.rb, line 94
def file_put_contents(fn, body)
  File.open(fn, 'w'){ |put|
    put.write body
    body
  }
end
find_implement_widget_by_gtkwidget(widget) click to toggle source

Gtkオブジェクト widget に対応するウィジェットのオブジェクトを返す

Args

widget

Gtkウィジェット

Return

widget に対応するウィジェットオブジェクトまたは偽

# File core/plugin/gtk/gtk.rb, line 537
def find_implement_widget_by_gtkwidget(widget)
  @slug_dictionary.imaginally_by_gtk(widget) end
focus_move_to_latest_widget(postbox) click to toggle source

最後にアクティブだったペインにフォーカスを与える。 親タイムラインがあれば、それにフォーカスを与える。

Args

postbox

基準となるウィジェット

# File core/plugin/command/command.rb, line 273
def focus_move_to_latest_widget(postbox)
  if postbox.parent.is_a? Plugin::GUI::Window
    pane = last_active_pane[postbox.parent.slug]
    pane.active! if pane
  else
    postbox.parent.active! end end
focus_move_to_nearest_postbox(widget) click to toggle source

一番近い postbox にフォーカスを与える

Args

widget

基準となるウィジェット

# File core/plugin/command/command.rb, line 259
def focus_move_to_nearest_postbox(widget)
  notice "called: given widget #{widget.inspect}"
  if widget.is_a? Plugin::GUI::HierarchyParent
    postbox = widget.children.find{ |w| w.is_a? Plugin::GUI::Postbox }
    notice "found postbox: #{postbox.inspect}"
    if postbox
      return postbox.active! end end
  if widget.is_a? Plugin::GUI::HierarchyChild
    focus_move_to_nearest_postbox(widget.parent) end end
focus_move_widget(widget, distance) { |widget, distance| ... } click to toggle source

フォーカスを widget から distance に移動する

Args

widget

起点となるウィジェット

distance

移動距離

# File core/plugin/command/command.rb, line 227
def focus_move_widget(widget, distance)
  type_strict widget => Plugin::GUI::HierarchyParent
  type_strict widget => Plugin::GUI::HierarchyChild
  children = widget.parent.children.select{ |w| w.is_a? widget.class }
  index = children.index(widget)
  if distance > 0 ? (children.size <= (index+distance)) : (0 > (index+distance))
    notice "terminate #{widget}"
    yield(widget, distance) if block_given?
  else
    term = children[index + distance]
    term = term.active_chain.last if term.respond_to? :active_chain
    term.active! if term
    notice "activate #{term} #{widget.class} #{term.class}"
    src_tl = widget.active_chain.last
    if widget.is_a?(Plugin::GUI::Pane) and src_tl.is_a?(Plugin::GUI::Timeline) and term.is_a?(Plugin::GUI::Timeline)
      slide_timeline_focus(src_tl, term) end end end
freezable?() click to toggle source

freezeできるならtrueを返す

# File core/utils.rb, line 428
def freezable?
  true end
freeze_ifn() click to toggle source

freezeできる場合はfreezeする。selfを返す

# File core/utils.rb, line 432
def freeze_ifn
  freeze if freezable?
  self end
gc() click to toggle source
# File core/plugin/api_request_file_cache.rb, line 5
def gc
  notice "apirequest cache was deleted. "+MikuTwitter::Cache.garbage_collect.inspect
  Reserver.new(3600){
    gc }
end
gen_counter(count=0, increment=1) click to toggle source

スレッドセーフなカウンタを返す。 カウンタの初期値は count で、呼び出すたびに値が increment づつ増える。 なお、カウンタが返す値はインクリメント前の値。

# File core/utils.rb, line 78
def gen_counter(count=0, increment=1)
  mutex = Mutex.new
  lambda{
    mutex.synchronize{
      result = count
      count += increment
      result } } end
gen_input(label, dialog, visibility=true, default="") click to toggle source
# File core/plugin/change_account.rb, line 77
def gen_input(label, dialog, visibility=true, default="")
  container = ::Gtk::HBox.new(false, 0)
  input = ::Gtk::Entry.new
  input.text = default
  input.visibility = visibility
  input.signal_connect('activate') { |elm|
    dialog.response(::Gtk::Dialog::RESPONSE_OK) }
  container.pack_start(::Gtk::Label.new(label), false, true, 0)
  container.pack_start(::Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
  return container, input
end
gen_relationship(api, userlist) click to toggle source
# File core/plugin/followingcontrol/followingcontrol.rb, line 13
def gen_relationship(api, userlist)
  count = gen_counter
  retrieve_interval = "retrieve_interval_#{api}".to_sym
  retrieve_count = "retrieve_count_#{api}".to_sym
  add_event_filter(api) { |list| [list + userlist.to_a] }
  lambda{ |service|
    relations = userlist.to_a
    c = count.call
    if (c % UserConfig[retrieve_interval]) == 0
      service.__send__(api, cache: (c==0 ? true : :keep)).next{ |users|
        users = users.select(&ret_nth).reverse!.freeze
        boot_event(api, service, users - relations, relations - users, users) unless relations.empty?
        users
      }.terminate
    end } end
gen_scrollbars(widget) click to toggle source

widget のためのスクロールバーを作って返す

Args

widget

Gtk::TextView

Return

縦スクロールバーと横スクロールバー

# File core/plugin/console/console.rb, line 76
def gen_scrollbars(widget)
  scroll_v = ::Gtk::VScrollbar.new
  scroll_h = ::Gtk::HScrollbar.new
  widget.set_scroll_adjustment(scroll_h.adjustment, scroll_v.adjustment)
  return scroll_v, scroll_h
end
gen_tags(buffer) click to toggle source

タグを作る

Args

buffer

Gtk::TextBuffer

# File core/plugin/console/console.rb, line 86
def gen_tags(buffer)
  type_strict buffer => ::Gtk::TextBuffer
  buffer.create_tag("prompt",
                    foreground_gdk: Gdk::Color.new(0, 0x6666, 0))
  buffer.create_tag("echo",
                    weight: Pango::FontDescription::WEIGHT_BOLD)
  buffer.create_tag("result",
                    foreground_gdk: Gdk::Color.new(0, 0, 0x6666))
  buffer.create_tag("errorclass",
                    foreground_gdk: Gdk::Color.new(0x6666, 0, 0))
  buffer.create_tag("error",
                    weight: Pango::FontDescription::WEIGHT_BOLD,
                    foreground_gdk: Gdk::Color.new(0x9999, 0, 0))
  buffer.create_tag("backtrace",
                    foreground_gdk: Gdk::Color.new(0x3333, 0, 0))
end
gen_xml(msg) click to toggle source
# File core/chi.rb, line 120
def gen_xml(msg)
  xml = REXML::Document.new open('chi_timeline_cachereplies')
  status = xml.root.get_elements('//statuses/status/').first
  status.get_elements('text').first.add_text(msg)
  return xml
end
get_local_dir_name(image_url, image_name = Digest::MD5.hexdigest(image_url)) click to toggle source
# File core/plugin/image_file_cache/image_file_cache.rb, line 89
def get_local_dir_name(image_url, image_name = Digest::MD5.hexdigest(image_url))
  File.expand_path(File.join(Environment::CACHE, 'icon', image_name[0], image_name[1])) end
get_local_image_name(image_url) click to toggle source
# File core/plugin/image_file_cache/image_file_cache.rb, line 85
def get_local_image_name(image_url)
  image_name = Digest::MD5.hexdigest(image_url)
  File.join(get_local_dir_name(image_url, image_name), image_name) end
get_tag_by_attributes(tag) click to toggle source
# File core/plugin/openimg/openimg.rb, line 85
def get_tag_by_attributes(tag)
  attribute = {}
  tag.each_matches(%r([a-zA-Z0-9]+?)=(['"])(.*?)\22//){ |pair, pos|
    key, val = pair[1], pair[3]
    attribute[key] = val }
  attribute.freeze end
get_tagattr(dom, element_rule) click to toggle source
# File core/plugin/openimg/openimg.rb, line 92
def get_tagattr(dom, element_rule)
  element_rule = element_rule.melt
  tag_name = element_rule['tag'] or 'img'
  attr_name = element_rule.has_key?('attribute') ? element_rule['attribute'] : 'src'
  element_rule.delete('tag')
  element_rule.delete('attribute')
  if dom
    attribute = {}
    catch(:imgtag_match){
      dom.gsub("\n", ' ').each_matches(Regexp.new("<#{tag_name}.*?>")){ |str, pos|
        attr = get_tag_by_attributes(str.to_s)
        if element_rule.all?{ |k, v| v === attr[k] }
          attribute = attr.freeze
          throw :imgtag_match end } }
    unless attribute.empty?
      return attr_name ? attribute[attr_name.to_s] : attribute end end
  notice 'not matched'
  nil end
get_window_geometry(slug) click to toggle source
# File core/plugin/gtk/gtk.rb, line 459
def get_window_geometry(slug)
  type_strict slug => Symbol
  geo = UserConfig[:windows_geometry]
  if defined? geo[slug]
    geo[slug]
  else
    size = [Gdk.screen_width/3, Gdk.screen_height*4/5]
    { size: size,
      position: [Gdk.screen_width - size[0], Gdk.screen_height/2 - size[1]/2] } end end
hello(url) click to toggle source
# File core/plugin/change_account.rb, line 89
def hello(url)
  "マスターったら、ツイッターまでみっくみくね!\n\n"+
  "ログインの手順:\n下のリンクをクリックして、ユーザ名などを入れてから許可するボタンを"+
    "押してください(クリックしても開かなかったら、アドレスバーにコピペだ!)。\n"+
    "#{url}\n表示された数字を「暗証番号」に入力してOKボタンを押してください。\n\n"+
    'すると、みっくみくにされます。'
end
imgurlresolver(url, element_rule, limit=5, &block) click to toggle source
# File core/plugin/openimg/openimg.rb, line 111
def imgurlresolver(url, element_rule, limit=5, &block)
  return nil if limit <= 0
  return block.call(url) if block != nil
  res = dom = nil
  begin
    uri = URI.parse(url)
    path = uri.path + (uri.query ? "?"+uri.query : "")
    res = Net::HTTP.new(uri.host).get(path, "User-Agent" => Environment::NAME + '/' + Environment::VERSION.to_s)
    case(res)
    when Net::HTTPSuccess
      address = get_tagattr(res.body, element_rule)
      case address
      when %r^https?:/
        # Complete URL
        result = address
      when %r^\/\//
        # No scheme
        result = "http:" + address
      when %r^\//
        # Absolute path
        result = uri.dup
        result.path = address
      else
        # Relative path
        result = uri.dup
        result.merge!(address)
      end
      notice result.inspect
      result.to_s
    when Net::HTTPRedirection
      return imgurlresolver(res['Location'], element_rule, limit - 1, &block)
    else
      warn "#{res.code} failed"
      nil end
  rescue Timeout::Error, StandardError => e
    warn e
    nil end end
into_debug_mode(exception = nil, bind = binding) click to toggle source

–debug オプション付きで起動されている場合、インタプリタに入る。

Args

exception

原因となった例外

binding

インタプリタを実行するスコープ

Return

インタプリタから復帰したら(インタプリタが起動されたら)true、 デバッグモードではない、pryがインストールされていない等、起動に失敗したらfalse

# File core/utils.rb, line 190
def into_debug_mode(exception = nil, bind = binding)
  if Mopt.debug and not Mopt.testing
    require_if_exist 'pry'
    if binding.respond_to?(:pry)
      log "error", exception if exception
      $into_debug_mode_lock.synchronize {
        begin
          $into_debug_mode = Set.new
          bind.pry
        ensure
          threads = $into_debug_mode
          $into_debug_mode = false
          threads.each &:wakeup end }
      return true end end end
is_fib?(n) click to toggle source
# File core/utils.rb, line 753
def is_fib?(n)
  x = 1
  loop{
    if(fib(x) == n)
      return true
    elsif(fib(x) > n)
      return false end
    x += 1 } end
j_delete(url) click to toggle source

url のキャッシュの日時を削除する。

# File core/plugin/image_file_cache/image_file_cache.rb, line 66
def j_delete(url)
  atomic {
    j_data = at(:journaling_data)
    if j_data and j_data.include?(url)
      j_data = j_data.melt
      j_data.delete(url)
      store(:journaling_data, j_data) end } end
j_get(url) click to toggle source

url からキャッシュを取得した日時を返す。 キャッシュがなければ nil を返す。

# File core/plugin/image_file_cache/image_file_cache.rb, line 49
def j_get(url)
  at(:journaling_data, {})[url] end
j_include?(url) click to toggle source

url のキャッシュがあれば真

# File core/plugin/image_file_cache/image_file_cache.rb, line 53
def j_include?(url)
  j_data = at(:journaling_data)
  j_data.include? url if j_data end
j_set(url, time = Time.now) click to toggle source

url のキャッシュの日時を現在に設定する。

# File core/plugin/image_file_cache/image_file_cache.rb, line 58
def j_set(url, time = Time.now)
  atomic {
    j_data = at(:journaling_data, {}).melt
    j_data[url.freeze] = time.freeze
    store(:journaling_data, j_data)
    Reserver.new(time + cache_expire){ j_delete(url) } } end
last_active_pane() click to toggle source
# File core/plugin/command/command.rb, line 280
def last_active_pane
  @last_active_pane ||= {} end
lazy(&proc) click to toggle source

遅延評価オブジェクトを作成する。ブロックを取って呼ばれた場合は Lazy のインスタンスを、 何も取らずに読んだ場合は MethodCallDelayer のインスタンスを返す。

# File core/lib/lazy.rb, line 86
def lazy(&proc)
  if proc
    Lazy.new(&proc)
  else
    MethodCallDelayer.new end end
list_modify_member(list, cache=:keep) click to toggle source

リストのメンバーを取得する

Args

list

リスト

cache

キャッシュの利用方法

Return

deferred

# File core/plugin/list/list.rb, line 91
def list_modify_member(list, cache=:keep)
  Service.primary.list_members( list_id: list[:id],
                                mode: list[:mode],
                                cache: false).next{ |users|
    list.add_member(users) if users
    Service.primary.list_statuses(:id => list[:id],
                                  :cache => cache).next{ |res|
      if res.is_a? Array
        slug = timelines.keys.find{ |slug| timelines[slug] == list }
        timeline(slug) << res if slug end
    }.terminate
  }.terminate("リスト #{list[:full_name]} (##{list[:id]}) のメンバーの取得に失敗しました") end
list_set_hide(list) click to toggle source

リストを非表示に設定する。すでに非表示にセットされている場合は何もしない

Args

list

非表示にするリスト

Return

self

# File core/plugin/list/list.rb, line 131
def list_set_hide(list)
  type_strict list => UserList
  visible_lists = at(:visible_lists, [])
  if visible_lists.include?(list[:id])
    store(:visible_lists, visible_lists - [list[:id]]) end
  visible_list_obj = at(:visible_list_obj, {}).melt
  visible_list_obj.delete(list[:id])
  store(:visible_list_obj, visible_list_obj)
  self end
list_set_visibility(list) click to toggle source

リストを表示可能に設定する。すでに表示可能にセットされている場合は何もしない

Args

list

表示可能にするリスト

Return

self

# File core/plugin/list/list.rb, line 119
def list_set_visibility(list)
  type_strict list => UserList
  visible_lists = at(:visible_lists, [])
  if not visible_lists.include?(list[:id])
    store(:visible_lists, (visible_lists + [list[:id]]).uniq) end
  self end
list_set_visibility!(list, visibility) click to toggle source

list の表示可否状態を visibility にして、実際に表示/非表示を切り替える

Args

list

リスト

visibility

trueなら表示、falseなら非表示

Return

self

# File core/plugin/list/list.rb, line 147
def list_set_visibility!(list, visibility)
  type_strict list => UserList
  if visibility
    list_set_visibility(list)
    tab_open(list)
  else
    list_set_hide(list)
    tab_close(list) end
  self end
list_visible?(list) click to toggle source

そのタブが表示する設定になっているかどうかを返す

Args

list

リスト

Return

タブが表示する設定になっているなら真

# File core/plugin/list/list.rb, line 162
def list_visible?(list)
  type_strict list => UserList
  visible_list_ids.include? list[:id] end
localfile(url) click to toggle source
# File core/plugin/aspectframe/aspectframe.rb, line 44
def localfile(url)
  File.expand_path(File.join(Environment::CACHE, "af", "#{Digest::MD5.hexdigest(url)[0,2].upcase}.png"))
end
log(prefix, object) click to toggle source

エラーログに記録する。 内部処理用。外部からは呼び出さないこと。

# File core/utils.rb, line 328
def log(prefix, object)
  debugging_wait
  begin
    msg = "#{prefix}: #{caller_util}: #{object}"
    msg += "\nfrom " + object.backtrace.join("\nfrom ") if object.is_a? Exception
    unless $daemon
      if msg.is_a? Exception
        __write_stderr(msg.to_s)
        __write_stderr(msg.backtrace.join("\n"))
      else
        __write_stderr(msg) end
      if logfile
        FileUtils.mkdir_p(File.expand_path(File.dirname(logfile + '_')))
        File.open(File.expand_path("#{logfile}#{Time.now.strftime('%Y-%m-%d')}.log"), 'a'){ |wp|
          wp.write("#{Time.now.to_s}: #{msg}\n") } end end
  rescue Exception => e
    __write_stderr("critical!: #{caller(0)}: #{e.to_s}")
  end
end
logfile(fn = nil) click to toggle source

ログファイルを取得設定

# File core/utils.rb, line 370
def logfile(fn = nil)
  if(fn) then
    $logfile = fn
  end
  $logfile or nil
end
main() click to toggle source
# File core/chi.rb, line 84
def main()
  create_pidfile
  notice Environment::VERSION

  if $debug then
    notice '-- ロードされたプラグイン一覧'
    Plugin::Ring.avail_plugins.each_pair{|name, insts|
      inst = insts.map{|inst| inst.class }.join(', ')
      notice "#{name}: #{inst}"
    }
    notice '--'
  end

  watch = Watch.instance

  if($interactive) then
    loop{
      print '> '
      input = STDIN.gets.chomp
      case (input)
      when 'q'
        exit
      when 'help'
        puts '"q" とタイプすると終了します'
      else
        watch.action(Message.new(input, :user => Hash[:id, 0, :idname, 'toshi_a']))
      end
    }
  else
    loop{
      watch.action
      sleep 60
    }
  end
end
mainthread?() click to toggle source

カレントスレッドがメインスレッドかどうかを返す

# File core/utils.rb, line 268
def mainthread?
  Thread.main == Thread.current end
mainthread_only() click to toggle source

メインスレッド以外で呼び出されたらThreadErrorを投げる

# File core/utils.rb, line 272
def mainthread_only
  unless mainthread?
    raise ThreadError.new('The method can calls only main thread. but called by another thread.') end end
melt() click to toggle source

freezeされていない同じ内容のオブジェクトを作って返す。 メルト 溶けてしまいそう (実装が)dupだなんて 絶対に 言えない

# File core/utils.rb, line 438
def melt
  if frozen? then dup else self end end
menu_widget(widgets_dict) click to toggle source
miku(node, scope=MIKU::SymbolTable.new) click to toggle source
# File core/miku/miku.rb, line 15
def miku(node, scope=MIKU::SymbolTable.new)
  if(node.is_a? MIKU::Node) then
    begin
      node.miku_eval(scope)
    rescue MIKU::MikuException => e
      warn e
    rescue Exception => e
      warn "[MIKU Bug] fatal error on code #{node.inspect}"
      raise e
    end
  else
    node end end
miku_stream(stream, scope) click to toggle source
# File core/miku/miku.rb, line 28
def miku_stream(stream, scope)
  begin
    while(not stream.eof?) do
      miku(MIKU.parse(stream), scope) end
  rescue MIKU::EndofFile
  rescue MIKU::MikuException => e
    warn e end end
miquire(*args) click to toggle source

ミクってかわいいよねぇ。 ツインテールいいよねー。 どう良いのかを書くとコードより長くなりそうだから詳しくは書かないけどいいよねぇ。 ふたりで寒い時とかに歩いてたら首にまいてくれるんだよー。 我ながらなんてわかりやすい説明なんだろう。 (訳: Miquire.miquire のエイリアス)

# File core/miquire.rb, line 15
def miquire(*args)
  Miquire.miquire(*args)
end
modify_extract_tabs() click to toggle source
# File core/plugin/extract/extract.rb, line 65
def modify_extract_tabs
  UserConfig[:extract_tabs] = extract_tabs.values
end
move(window) click to toggle source
# File core/plugin/openimg/openimg.rb, line 10
def move(window)
  @position = window.position.freeze end
mute?(params) click to toggle source

そのイベントをミュートするかどうかを返す(trueなら表示しない)

# File core/plugin/activity/activity.rb, line 89
def mute?(params)
  mute_kind = UserConfig[:activity_mute_kind]
  if mute_kind.is_a? Array
    return true if mute_kind.include? params[:kind].to_s end
  mute_kind_related = UserConfig[:activity_mute_kind_related]
  if mute_kind_related
    return true if mute_kind_related.include?(params[:kind].to_s) and !params[:related] end
  false end
mutebutton(user) click to toggle source
# File core/plugin/profile/profile.rb, line 97
def mutebutton(user)
  changer = lambda{ |new, widget|
    if new === nil
      UserConfig[:muted_users] and UserConfig[:muted_users].include?(user.idname)
    elsif new
      add_muted_user(user)
    else
      remove_muted_user(user)
    end
  }
  btn = Mtk::boolean(changer, 'ミュート')
end
no_mainthread() click to toggle source

メインスレッドで呼び出されたらThreadErrorを投げる

# File core/utils.rb, line 277
def no_mainthread
  if mainthread?
    raise ThreadError.new('The method can not calls main thread. but called by main thread.') end end
notice(msg) click to toggle source

一般メッセージを表示する。

# File core/utils.rb, line 168
def notice(msg)
  log "notice", msg if Mopt.error_level >= 3
end
object_get_contents(fn) click to toggle source

ファイル fn の内容からオブジェクトを読み込む。 fn は、#object_put_contents() で保存されたファイルでなければならない。

# File core/utils.rb, line 103
def object_get_contents(fn)
  File.open(fn, 'r'){ |input|
    Marshal.load input
  }
end
object_put_contents(fn, body) click to toggle source

オブジェクト body をファイル fn に書き込む。 body は、Marshalizeできるものでなければならない。

# File core/utils.rb, line 111
def object_put_contents(fn, body)
  File.open(fn, 'w'){ |put|
    Marshal.dump body, put
  }
end
open_user(user) click to toggle source
# File core/plugin/followingcontrol/followingcontrol.rb, line 4
def open_user(user)
  Plugin.call(:show_profile, Service.primary, user) end
pane_order_delete(i_pane) click to toggle source

ペインを順序リストから削除する

Args

i_pane

ペイン

# File core/plugin/gtk/gtk.rb, line 511
def pane_order_delete(i_pane)
  order = UserConfig[:ui_tab_order].melt
  i_window = i_pane.parent
  order[i_window.slug] = order[i_window.slug].melt
  order[i_window.slug].delete(i_pane.slug)
  UserConfig[:ui_tab_order] = order
end
parallel(&proc) click to toggle source

平行評価オブジェクトを作成する

# File core/lib/lazy.rb, line 97
def parallel(&proc)
  Parallel.new(&proc) end
pid_exist?(pid) click to toggle source

プロセスID pid が存在するか否かを返す。

# File core/utils.rb, line 130
def pid_exist?(pid)
  if FileTest.exist? '/proc' then
    FileTest.exist? "/proc/#{pid}"
  else
    begin
      Process.kill(0, pid.to_i)
    rescue Errno::ESRCH
      false
    else
      true end end end
popup(watch, method = nil, url = nil, options = nil, res = nil) click to toggle source
profile_head(user) click to toggle source

ユーザのプロフィールのヘッダ部を返す

Args

user

表示するUser

Return

ヘッダ部を表すGtkコンテナ

# File core/plugin/profile/profile.rb, line 210
def profile_head(user)
  eventbox = ::Gtk::EventBox.new
  eventbox.ssc('visibility-notify-event'){
    eventbox.style = background_color
    false }
  eventbox.add(::Gtk::VBox.new(false, 0).
               add(::Gtk::HBox.new(false, 16).
                   closeup(::Gtk::WebIcon.new(user.profile_image_url_large, 128, 128).top).
                   closeup(::Gtk::VBox.new.closeup(user_name(user)).closeup(profile_table(user)))))
  scrolledwindow = ::Gtk::ScrolledWindow.new
  scrolledwindow.height_request = 128 + 24
  scrolledwindow.set_policy(::Gtk::POLICY_AUTOMATIC, ::Gtk::POLICY_NEVER)
  scrolledwindow.add_with_viewport(eventbox)
end
profile_table(user) click to toggle source

プロフィールの上のところの格子になってる奴をかえす

Args

user

表示するUser

Return

プロフィールのステータス部を表すGtkコンテナ

# File core/plugin/profile/profile.rb, line 249
def profile_table(user)
  w_tweets = ::Gtk::Label.new(user[:statuses_count].to_s)
  w_favs = ::Gtk::Label.new(user[:favourites_count].to_s)
  w_faved = ::Gtk::Label.new("...")
  w_followings = ::Gtk::Label.new(user[:friends_count].to_s)
  w_followers = ::Gtk::Label.new(user[:followers_count].to_s)
  user.count_favorite_by.next{ |favs|
    w_faved.text = favs.to_s
  }.terminate("ふぁぼが取得できませんでした").trap{
    w_faved.text = '-' }
  ::Gtk::Table.new(2, 5).
    attach(w_tweets.right, 0, 1, 0, 1).
    attach(::Gtk::Label.new("tweets").left, 1, 2, 0, 1).
    attach(w_favs.right, 0, 1, 1, 2).
    attach(::Gtk::Label.new("favs").left, 1, 2, 1, 2).
    attach(w_faved.right, 0, 1, 2, 3).
    attach(::Gtk::Label.new("faved").left, 1, 2, 2, 3).
    attach(w_followings.right, 0, 1, 3, 4).
    attach(::Gtk::Label.new("followings").left, 1, 2, 3, 4).
    attach(w_followers.right, 0, 1, 4, 5).
    attach(::Gtk::Label.new("followers").left, 1, 2, 4, 5).
    set_row_spacing(0, 4).
    set_row_spacing(1, 4).
    set_column_spacing(0, 16)
end
redraw(eb, pb) click to toggle source
# File core/plugin/openimg/openimg.rb, line 19
def redraw(eb, pb)
  ew, eh = eb.window.geometry[2,2]
  return if(ew == 0 or eh == 0)
  pb = pb.dup
  pb = pb.scale(*Gdk::WebImageLoader.calc_fitclop(pb, Gdk::Rectangle.new(0, 0, ew, eh)))
  eb.window.draw_pixbuf(nil, pb, 0, 0, (ew - pb.width)/2, (eh - pb.height)/2, -1, -1, Gdk::RGB::DITHER_NORMAL, 0, 0) end
refresh(cache=:keep) click to toggle source

saved search を取得する

Args

cache

キャッシュの利用方法

Return

deferred

# File core/plugin/saved_search/saved_search.rb, line 72
def refresh(cache=:keep)
  Service.primary.saved_searches(cache: cache).next{ |res|
    if res
      saved_searches = {}
      res.each{ |record|
        saved_searches[record[:id]] = SavedSearch.new(record[:id], URI.decode(record[:query]), URI.decode(record[:name]), "savedsearch_#{record[:id]}".to_sym) }
      new_ids, old_ids = saved_searches.keys, timelines.keys
      (new_ids - old_ids).each{ |id| add_tab(saved_searches[id]) }
      (old_ids - new_ids).each{ |id| delete_tab(id) } end }.terminate end
register_cache(saved_search) click to toggle source

保存した検索の情報をキャッシュに登録する

Args

saved_search

保存した検索

# File core/plugin/saved_search/saved_search.rb, line 85
def register_cache(saved_search)
  type_strict saved_search => SavedSearch
  cache = at(:cache, {}).melt
  cache[saved_search.id] = {
    id: saved_search.id,
    query: saved_search.query,
    name: saved_search.name,
    slug: saved_search.slug }
  store(:cache, cache) end
relation_bar(user) click to toggle source

フォロー関係を表示する

Args

user

対象となるユーザ

Return

リレーションバーのウィジェット(Gtk::VBox)

# File core/plugin/profile/profile.rb, line 129
def relation_bar(user)
  icon_size = Gdk::Rectangle.new(0, 0, 32, 32)
  arrow_size = Gdk::Rectangle.new(0, 0, 16, 16)
  container = ::Gtk::VBox.new(false, 4)
  Service.all.each{ |me|
    following = followed = nil
    w_following_label = ::Gtk::Label.new("関係を取得中")
    w_followed_label = ::Gtk::Label.new("")
    w_eventbox_image_following = ::Gtk::EventBox.new
    w_eventbox_image_followed = ::Gtk::EventBox.new
    relation = if me.user_obj == user
                 ::Gtk::Label.new("それはあなたです!")
               else
                 ::Gtk::HBox.new.
                   closeup(w_eventbox_image_following).
                   closeup(w_following_label) end
    relation_container = ::Gtk::HBox.new(false, icon_size.width/2)
    relation_container.closeup(::Gtk::WebIcon.new(me.user_obj[:profile_image_url], icon_size).tooltip("#{me.user}(#{me.user_obj[:name]})"))
    relation_container.closeup(::Gtk::VBox.new.
                               closeup(relation).
                               closeup(::Gtk::HBox.new.
                                       closeup(w_eventbox_image_followed).
                                       closeup(w_followed_label)))
    relation_container.closeup(::Gtk::WebIcon.new(user[:profile_image_url], icon_size).tooltip("#{user.idname}(#{user[:name]})"))
    if me.user_obj != user
      followbutton = ::Gtk::Button.new
      followbutton.sensitive = false
      # フォローしている状態の更新
      m_following_refresh = lambda { |new|
        if not w_eventbox_image_following.destroyed?
          following = new
          if not w_eventbox_image_following.children.empty?
            w_eventbox_image_following.remove(w_eventbox_image_following.children.first) end

          w_eventbox_image_following.style = w_eventbox_image_following.parent.style
          w_eventbox_image_following.add(::Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_following.png" : "arrow_notfollowing.png"), arrow_size).show_all)
          w_following_label.text = new ? "フョローしている" : "フョローしていない"
          followbutton.label = new ? "解除" : "フョロー" end }
      # フォローされている状態の更新
      m_followed_refresh = lambda { |new|
        if not w_eventbox_image_followed.destroyed?
          followed = new
          if not w_eventbox_image_followed.children.empty?
            w_eventbox_image_followed.remove(w_eventbox_image_followed.children.first) end
          w_eventbox_image_followed.style = w_eventbox_image_followed.parent.style
          w_eventbox_image_followed.add(::Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_followed.png" : "arrow_notfollowed.png"), arrow_size).show_all)
          w_followed_label.text = new ? "フョローされている" : "フョローされていない" end }
      Service.primary.friendship(target_id: user[:id], source_id: me.user_obj[:id]).next{ |rel|
        if rel and not(w_eventbox_image_following.destroyed?)
          m_following_refresh.call(rel[:following])
          m_followed_refresh.call(rel[:followed_by])
          handler_followings_created = on_followings_created do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(true) end end
          handler_followings_destroy = on_followings_destroy do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(false) end end
          followbutton.ssc(:clicked){
            followbutton.sensitive = false
            event = following ? :followings_destroy : :followings_created
            me.__send__(following ? :unfollow : :follow, user).next{ |msg|
              Plugin.call(event, me, [user])
              followbutton.sensitive = true unless followbutton.destroyed? }.
            terminate.trap{
              followbutton.sensitive = true unless followbutton.destroyed? }
            true }
          followbutton.signal_connect(:destroy){
            detach(:followings_created, handler_followings_created)
            detach(:followings_destroy, handler_followings_destroy)
            false }
          followbutton.sensitive = true end
      }.terminate.trap{
        w_following_label.text = "取得できませんでした" } end
    container.closeup(relation_container.closeup(followbutton)) }
  container end
remove_muted_user(user) click to toggle source
# File core/plugin/profile/profile.rb, line 117
def remove_muted_user(user)
  type_strict user => User
  atomic{
    muted = (UserConfig[:muted_users] ||= []).melt
    muted.delete(user.idname)
    UserConfig[:muted_users] = muted } end
require_if_exist(file) click to toggle source

存在するかわからないrubyファイル file を読み込む。 ただし、file が存在しない場合は例外を投げずにfalseを返す。

# File core/utils.rb, line 148
def require_if_exist(file)
  begin
    require file
    true
  rescue LoadError
    notice "require-if-exist: file not found: #{file}"
    false end end
reset_activity(model) click to toggle source

アクティビティの古い通知を一定時間後に消す

# File core/plugin/activity/activity.rb, line 117
def reset_activity(model)
  notice "reset activity registered"
  Reserver.new(60) {
    Delayer.new {
      if not model.destroyed?
        notice "reset activity start"
        iters = model.to_enum(:each).to_a
        remove_count = iters.size - UserConfig[:activity_max]
        notice "remove count #{remove_count}"
        if remove_count > 0
          iters[-remove_count, remove_count].each{ |mpi|
            notice "activity deleted: #{mpi[2][ActivityView::TITLE]}"
            model.remove(mpi[2]) }
        else
          notice "nothing to remove activity" end
        reset_activity(model) end } }
end
result_strict(must, &block) click to toggle source

blockの評価結果がチェックをパスしなかった場合にabortする

# File core/utils.rb, line 261
def result_strict(must, &block)
  result = block.call
  type_strict(result => must)
  result
end
ret_nth(num=0) click to toggle source

num 番目の引数をそのまま返す関数を返す

# File core/utils.rb, line 72
def ret_nth(num=0)
  lambda { |*arg| arg[num] } end
rewind_timeline(saved_search) click to toggle source

タイムラインを更新する

Args

saved_search

saved search

# File core/plugin/saved_search/saved_search.rb, line 60
def rewind_timeline(saved_search)
  type_strict saved_search => SavedSearch
  Service.primary.search(q: saved_search.query, rpp: 100).next{ |res|
    timeline(saved_search.slug) << res if res.is_a? Array
  }.trap{ |e|
    timeline(saved_search.slug) << Message.new(message: "更新中にエラーが発生しました (#{e.to_s})", system: true) } end
scan(slug, messages) click to toggle source

messagesの中で、タイムライン slug に入れるべきものがあれば入れる

Args

slug

タイムラインスラッグ

messages

入れるMessageの配列

# File core/plugin/smartthread/smartthread.rb, line 13
def scan(slug, messages)
  seeds = @timelines[slug]
  i_timeline = timeline(slug)
  if i_timeline and seeds
    SerialThread.new do
      messages.each{ |message|
        message.each_ancestors { |cur|
          if seeds.include? cur
            i_timeline << message end } } end end end
set_available_lists(newlist) click to toggle source

自分がフォローしているリストを新しく設定する

Args

newlist

新しいリスト(Enumerable)

Return

newlist

# File core/plugin/list/list.rb, line 177
def set_available_lists(newlist)
  created = newlist - available_lists
  deleted = available_lists - newlist
  Plugin.call(:list_created, Service.primary, created.freeze) if not created.empty?
  Plugin.call(:list_destroy, Service.primary, deleted.freeze) if not deleted.empty?
  @available_lists = UserLists.new(newlist).freeze
  Plugin.call(:list_data, Service.primary, @available_lists) if not(created.empty? and deleted.empty?)
  @available_lists end
set_event(api, title) click to toggle source
# File core/plugin/followingcontrol/followingcontrol.rb, line 29
def set_event(api, title)
  userlist = Gtk::UserList.new
  tab(api, title) do
    set_icon MUI::Skin.get("#{api}.png")
    expand
    nativewidget userlist
  end
  proc = gen_relationship(api, userlist)
  onperiod{ |service|
    promise = proc.call(service)
    if promise
      promise.next{ |res|
        if res
          userlist.add(res).show_all end }.terminate end }
  add_event("#{api}_created".to_sym){ |service, users|
    userlist.add(users).show_all }
  add_event("#{api}_destroy".to_sym){ |service, users|
    userlist.remove_if_exists_all(users) }
  userlist.double_clicked = method(:open_user) end
setting_container() click to toggle source

設定のGtkウィジェット

# File core/plugin/list/list.rb, line 55
def setting_container
  tab = Tab.new
  tab.plugin = self
  available_lists.each{ |list|
    iter = tab.model.append
    iter[Tab::VISIBILITY] = list_visible?(list)
    iter[Tab::SLUG] = list[:full_name]
    iter[Tab::LIST] = list
    iter[Tab::NAME] = list[:name]
    iter[Tab::DESCRIPTION] = list[:description]
    iter[Tab::PUBLICITY] = list[:mode] }
  Gtk::HBox.new.add(tab).closeup(tab.buttons(Gtk::VBox)).show_all end
setting_window() click to toggle source
# File core/plugin/settings/settings.rb, line 7
def setting_window
  return @window if defined?(@window) and @window
  record_order = UserConfig[:settings_menu_order] || ["基本設定", "入力", "表示", "通知", "ショートカットキー", "アクティビティ", "アカウント情報"]
  @window = window = ::Gtk::Window.new("設定")
  window.set_size_request(320, 240)
  window.set_default_size(640, 480)
  widgets_dict = {}
  menu = menu_widget(widgets_dict)
  settings = ::Gtk::VBox.new.set_no_show_all(true).show
  scrolled = ::Gtk::ScrolledWindow.new.set_hscrollbar_policy(::Gtk::POLICY_NEVER)
  Plugin.filtering(:defined_settings, []).first.each{ |title, definition, plugin|
    iter = menu.model.append
    iter[0] = title
    iter[1] = (record_order.index(title) || record_order.size)
    widgets_dict[title] = box = Plugin::Settings.new
    box.instance_eval(&definition)
    settings.closeup(box) }
  window.ssc(:destroy) {
    @window = nil
    false }

  scrolled_menu = ::Gtk::ScrolledWindow.new.set_policy(::Gtk::POLICY_NEVER, ::Gtk::POLICY_AUTOMATIC)

  window.add(::Gtk::HPaned.new.add1(scrolled_menu.add_with_viewport(menu)).add2(scrolled.add_with_viewport(settings))) end
show_once(event, *ids) click to toggle source

このIDの組み合わせが出現したことがないなら真

Args

event

イベント名

ids

ID

Return

初めて表示するキーなら真

# File core/plugin/activity/activity.rb, line 104
def show_once(event, *ids)
  @show_once ||= Hash.new{ |h, k| h[k] = [] }
  result = []
  ids.each_with_index{ |id, index|
    storage = @show_once[event][index] ||= Set.new
    if storage.include? id
      result << true
    else
      storage << id
      result << false end }
  not result.all?(&ret_nth) end
slide_timeline_focus(src, dest) click to toggle source

タイムライン src で選択されているディスプレイ上のy座標が同じ dest のツイートに フォーカスを移動する

Args

src

フォーカスを取得するタイムライン

dest

フォーカスを設定するタイムライン

# File core/plugin/command/command.rb, line 249
def slide_timeline_focus(src, dest)
  type_strict src => Plugin::GUI::Timeline, dest => Plugin::GUI::Timeline
  y = Plugin.filtering(:gui_timeline_cursor_position, src, nil).last
  notice "y = #{y}"
  if y
    Plugin.call(:gui_timeline_move_cursor_to, dest, y) end end
spec_generate(dir) click to toggle source
# File core/boot/shell/spec.rb, line 74
def spec_generate(dir)
  specfile = File.join(dir, "spec")
  spec = if FileTest.exist?(specfile)
           YAML.load_file(specfile)
         else
           user = UserConfig[:verify_credentials] || {}
           idname = user[:idname]
           {"slug" => File.basename(dir).to_sym, "depends" => {"mikutter" => Environment::VERSION.to_s}, "version" => "1.0", "author" => idname} end
  slug = spec["slug"].to_sym
  basefile = File.join(dir, "#{slug}.rb")
  unless FileTest.exist? basefile
    puts "file #{basefile} notfound. select plugin slug."
    expects = Dir.glob(File.join(dir, "*.rb")).map{ |filename| File.basename(filename, '.rb') }
    if expects.empty?
      puts "please create #{basefile}."
    end
    expects.each_with_index{ |filename, index|
      puts "[#{index}] #{filename}"
    }
    print "input number or slug [q:quit, s:skip]> "
    number = STDIN.gets.chomp
    case number
    when %rq/
      abort
    when %rs/
      return
    when %r^[0-9]+$/
      slug = expects[number.to_i].to_sym
    else
      slug = number.to_sym end
    spec["slug"] = slug
    basefile = File.join(dir, "#{slug}.rb") end
  source = File.open(basefile){ |io| io.read }

  if not spec.has_key?("name")
    print "#{slug}: name> "
    spec["name"] = STDIN.gets.chomp end
  if not spec.has_key?("description")
    print "#{slug}: description> "
    spec["description"] = STDIN.gets.chomp end
  spec["depends"] = {"version" => "1.0", "plugin" => []} if not spec.has_key?("depends")
  spec["depends"]["plugin"] = [] if not spec["depends"].has_key?("plugin")
  depend = Depend.new(source).set_spec(spec)
  depend.parse
  content = YAML.dump(depend.spec)
  File.open(specfile, "w"){ |io| io.write content }
  puts content
end
start(service) click to toggle source
# File core/plugin/rest/rest.rb, line 24
def start(service)
  notice "boot period"
  @crawlers.each{ |s| s.call(service) }
  Reserver.new(60){
    start(service) } end
streamerror(e) click to toggle source
# File core/plugin/streaming/filter.rb, line 65
def streamerror(e)
  @success_flag = false
  @fail.notify(e) end
tab_close(list) click to toggle source

list のためのタブを閉じる。タブがない場合は何もしない。

Args

list

リスト

Return

self

# File core/plugin/list/list.rb, line 235
def tab_close(list)
  type_strict list => UserList
  slug = timelines.keys.find{ |slug| timelines[slug] == list }
  return self if not timelines.has_key? slug
  if slug
    timelines.delete(slug)
    tab(slug).destroy end
  self end
tab_mark(list) click to toggle source

list に対応するタブにマークをつける。

Args

list

リスト

# File core/plugin/list/list.rb, line 205
def tab_mark(list)
  type_strict list => UserList
  if list_visible?(list)
    @mark << list end end
tab_open(list) click to toggle source

list のためのタブを開く。タブがすでに有る場合は何もしない。

Args

list

リスト

Return

self

# File core/plugin/list/list.rb, line 215
def tab_open(list)
  type_strict list => UserList
  slug = "list_#{list[:full_name]}".to_sym
  return self if timelines.has_key? slug
  timelines[slug] = list
  tab(slug, list[:full_name]) do
    set_icon MUI::Skin.get("list.png")
    timeline slug end
  list_modify_member(list, true)
  visible_list_obj = at(:visible_list_obj, {}).melt
  visible_list_obj[list[:id]] = list.to_hash
  visible_list_obj[list[:id]][:user] = list[:user].to_hash
  store(:visible_list_obj, visible_list_obj)
  self end
tab_reflesh() { || ... } click to toggle source

このブロック中で _add_tab(list)_ を呼ばれなかったリストは、ブロックを出た時に全て削除される。 また、新たにマークを付けられたタブは、タブが作成される。

Return

ブロックの戻り値

# File core/plugin/list/list.rb, line 190
def tab_reflesh
  @tab_reflesh ||= Mutex.new
  @tab_reflesh.synchronize do
    @mark = Set.new
    result = yield
    available_lists.each{ |list|
      if @mark.include? list
        tab_open(list)
      else
        tab_close(list) end }
    result end end
tab_update_icon(i_tab) click to toggle source
# File core/plugin/gtk/gtk.rb, line 448
def tab_update_icon(i_tab)
  type_strict i_tab => Plugin::GUI::TabLike
  tab = widgetof(i_tab)
  if tab
    tab.remove(tab.child) if tab.child
    if i_tab.icon.is_a?(String)
      tab.add(::Gtk::WebIcon.new(i_tab.icon, 24, 24).show)
    else
      tab.add(::Gtk::Label.new(i_tab.name).show) end end
  self end
tclambda(*args, &proc) click to toggle source

#type_checkで型をチェックしてからブロックを評価する無名関数を生成して返す

# File core/utils.rb, line 282
def tclambda(*args, &proc)
  lambda{ |*a|
    if proc.arity >= 0
      if proc.arity != a.size
        raise ArgumentError.new("wrong number of arguments (#{a.size} for #{proc.arity})") end
    elsif -(proc.arity+1) > a.size
      raise ArgumentError.new("wrong number of arguments (#{a.size} for #{proc.arity})") end
    proc.call(*a) if type_check(a.slice(0, args.size).zip(args)) } end
tcor(*types) click to toggle source

types のうちいずれかとis_a?関係ならtrueを返すProcオブジェクトを返す

# File core/utils.rb, line 248
def tcor(*types)
  lambda{ |v| types.any?{ |c| v.is_a?(c) } } end
the_day?() click to toggle source
# File core/plugin/aspectframe/aspectframe.rb, line 48
def the_day?
  time = Time.new
  4 == time.month and 1 == time.day end
timelines() click to toggle source

表示中のタイムライン/タブのスラッグとリストオブジェクトの連想配列

# File core/plugin/list/list.rb, line 105
def timelines
  @timelines ||= {} end
transform(url) click to toggle source
# File core/plugin/aspectframe/aspectframe.rb, line 40
def transform(url)
  "http://toshia.dip.jp/img/api/#{Digest::MD5.hexdigest(url)[0,2].upcase}.png"
end
twitter_account() click to toggle source
# File core/initialize.rb, line 22
def twitter_account
  print 'User name? >'
  $stdout.flush
  user = gets().chomp
  print 'password? >'
  $stdout.flush
  pass = gets().chomp
  print 'connecting... '
  twitter = Twitter.new(user, pass)
  tl = nil
  while(not tl.is_a?(Net::HTTPResponse))
    tl = twitter.friends_timeline()
  end
  if(tl.code != '200') then
    puts 'Authentication failed. Username and Password is valid?'
    return twitter_account()
  end
  puts 'Accepted.'
  return user, pass
end
type_check(args, &proc) click to toggle source

引数のチェックをすべてパスした場合のみブロックを実行する チェックに引っかかった項目があればwarnを出力してブロックは実行せずにnilを返す。 チェックはassocできる配列か、Hashで定義する。

type_check(value => nil,              # チェックしない(常にパス)
           value => Module,           # その型とis_a?関係ならパス
           value => [:method, *args], # value.method(*args)が真を返せばパス
           value => lambda{ |x| ...}) # xにvalueを渡して実行し、真を返せばパス

チェックをすべてパスしたかどうかを真偽値で返す。 ブロックが指定されていれば、それを実行してブロックの実行結果を返す メモ: いずれかのタイプに一致するチェックを定義するにはtcorを使う

type_check object => tcor(Array, Hash)
# File core/utils.rb, line 224
def type_check(args, &proc)
  check_function = lambda{ |val, check|
    if not check
      true
    elsif check.respond_to?(:call)
      check.call(val)
    elsif check.is_a? Array
      val.__send__(*check)
    elsif check.is_a? Symbol
      val.respond_to?(check)
    elsif check.is_a?(Class) or check.is_a?(Module)
      val.is_a?(check) end }
  error = args.find{ |a| not(check_function.call(*a)) }
  if(error)
    warn "argument error: #{error[0].inspect} is not passed #{error[1].inspect}"
    warn "in #{caller_util}"
    false
  else
    if proc
      proc.call
    else
      true end end end
type_strict(args, &proc) click to toggle source

#type_checkと同じだが、チェックをパスしなかった場合にabortする #type_checkの戻り値を返す

# File core/utils.rb, line 253
def type_strict(args, &proc)
  result = type_check(args, &proc)
  if not result
    into_debug_mode(binding)
    raise ArgumentError.new end
  result end
user_name(user) click to toggle source

ユーザ名を表示する

Args

user

表示するUser

Return

ユーザの名前の部分のGtkコンテナ

# File core/plugin/profile/profile.rb, line 230
def user_name(user)
  w_screen_name = ::Gtk::Label.new.set_markup("<b><u><span foreground=\"#0000ff\">#{Pango.escape(user[:idname])}</span></u></b>")
  w_ev = ::Gtk::EventBox.new
  w_ev.modify_bg(::Gtk::STATE_NORMAL, Gdk::Color.new(0xffff, 0xffff, 0xffff))
  w_ev.ssc(:realize) {
    w_ev.window.set_cursor(Gdk::Cursor.new(Gdk::Cursor::HAND2))
    false }
  w_ev.ssc(:button_press_event) { |this, e|
    if e.button == 1
      ::Gtk.openurl("http://twitter.com/#{user[:idname]}")
      true end }
  ::Gtk::HBox.new(false, 16).closeup(w_ev.add(w_screen_name)).closeup(::Gtk::Label.new(user[:name]))
end
visible_list_ids() click to toggle source

表示設定されているリストのIDを返す

Return

表示できるリストのIDの配列(TypedArray)

# File core/plugin/list/list.rb, line 111
def visible_list_ids
  at(:visible_lists, []).freeze end
wakachigaki(str, ret_io=false) click to toggle source
# File core/utils.rb, line 396
def wakachigaki(str, ret_io=false)
  if(ret_io)
    IO.popen('mecab -Owakati', 'r+').tap{ |io|
      io.write(str)
      io.close_write }
  else
    IO.popen('mecab -Owakati', 'r+'){ |io|
      io.write(str)
      io.close_write
      io.read } end end
warn(msg) click to toggle source

警告メッセージを表示する。

# File core/utils.rb, line 173
def warn(msg)
  log "warning", msg if Mopt.error_level >= 2
end
where_should_insert_it(insertion, src, order) click to toggle source

insertion を、 src の挿入するべき場所のインデックスを返す。 order は順番を表す配列で、 src 内のオブジェクトの前後関係を表す。 order 内に insertion が存在しない場合は一番最後のインデックスを返す

# File core/utils.rb, line 159
def where_should_insert_it(insertion, src, order)
  if(order.include?(insertion)) then
    src.dup.push(insertion).sort_by{|a|
      order.index(a) or 65536
    }.index(insertion)
  else
    src.size end end
widget_join_tab(i_tab, widget) click to toggle source

タブ tabwidget を入れる

Args

i_tab

タブ

widget

Gtkウィジェット

# File core/plugin/gtk/gtk.rb, line 425
def widget_join_tab(i_tab, widget)
  tab = widgetof(i_tab)
  return false if not tab
  i_pane = i_tab.parent
  return false if not i_pane
  pane = widgetof(i_pane)
  return false if not pane
  notice "widget_join_tab: #{widget} join #{i_tab}"
  container_index = pane.get_tab_pos_by_tab(tab)
  if container_index
    container = pane.get_nth_page(container_index)
    if container
      return container.pack_start(widget, i_tab.pack_rule[container.children.size]) end end
  if tab.parent
    raise Plugin::Gtk::GtkError, "Gtk Widget #{tab.inspect} of Tab(#{i_tab.slug.inspect}) has parent Gtk Widget #{tab.parent.inspect}" end
  container = ::Gtk::TabContainer.new(i_tab).show_all
  container.ssc(:key_press_event){ |w, event|
    Plugin::GUI.keypress(::Gtk::keyname([event.keyval ,event.state]), i_tab) }
  container.pack_start(widget, i_tab.pack_rule[container.children.size])
  pane.insert_page_menu(where_should_insert_it(i_tab, pane.children.map(&:i_tab), i_pane.children), container, tab)
  pane.set_tab_reorderable(container, true).set_tab_detachable(container, true)
  true end
widgetof(cuscadable) click to toggle source

cuscadable に対応するGtkオブジェクトを返す

Args

cuscadable

ウィンドウ、ペイン、タブ、タイムライン等

Return

対応するGtkオブジェクト

# File core/plugin/gtk/gtk.rb, line 524
def widgetof(cuscadable)
  type_strict cuscadable => :slug
  result = @slug_dictionary.get(cuscadable)
  if result and result.destroyed?
    nil
  else
    result end end
window_order_save_request(i_window) click to toggle source

ウィンドウ内のペイン、タブの現在の順序を設定に保存する

Args

i_window

ウィンドウ

# File core/plugin/gtk/gtk.rb, line 488
def window_order_save_request(i_window)
  notice "window_order_save_request: #{i_window.inspect}"
  type_strict i_window => Plugin::GUI::Window
  Delayer.new do
    panes_order = {}
    i_window.children.each{ |i_pane|
      if i_pane.is_a? Plugin::GUI::Pane
        tab_order = []
        pane = widgetof(i_pane)
        if pane
          pane.n_pages.times{ |page_num|
            i_widget = find_implement_widget_by_gtkwidget(pane.get_tab_label(pane.get_nth_page(page_num)))
            tab_order << i_widget.slug if i_widget } end
        panes_order[i_pane.slug] = tab_order if not tab_order.empty? end }
    ui_tab_order = (UserConfig[:ui_tab_order] || {}).melt
    ui_tab_order[i_window.slug] = panes_order
    UserConfig[:ui_tab_order] = ui_tab_order
  end
end
yamlisp(node) click to toggle source
# File core/miku/yamlisp.rb, line 5
def yamlisp(node)
  if(node.is_a? YamLisp::Node) then
    node.yamlisp_eval
  else
    node
  end
end