Module: Earthquake::Core

Included in:
Earthquake
Defined in:
lib/earthquake/core.rb

Instance Method Summary collapse

Instance Method Details

#__init(options) ⇒ Object



113
114
115
116
117
# File 'lib/earthquake/core.rb', line 113

def __init(options)
  config.merge!(options)
  _init
  _once
end

#_initObject



38
39
40
41
42
43
# File 'lib/earthquake/core.rb', line 38

def _init
  load_config
  load_plugins
  inits.each { |block| class_eval(&block) }
  inits.clear
end

#_onceObject



34
35
36
# File 'lib/earthquake/core.rb', line 34

def _once
  onces.each { |block| class_eval(&block) }
end

#async(&block) ⇒ Object



238
239
240
241
242
243
244
245
246
# File 'lib/earthquake/core.rb', line 238

def async(&block)
  Thread.start do
    begin
      block.call
    rescue Exception => e
      error e
    end
  end
end

#browse(url) ⇒ Object



261
262
263
# File 'lib/earthquake/core.rb', line 261

def browse(url)
  Launchy.open(url)
end

#configObject



6
7
8
# File 'lib/earthquake/core.rb', line 6

def config
  @config ||= {}
end

#default_configObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/earthquake/core.rb', line 56

def default_config
  consumer = YAML.load_file(File.expand_path('../../../consumer.yml', __FILE__))
  dir = config[:dir] || File.expand_path('~/.earthquake')
  {
    dir:             dir,
    time_format:     Time::DATE_FORMATS[:short],
    plugin_dir:      File.join(dir, 'plugin'),
    file:            File.join(dir, 'config'),
    prompt:          '',
    consumer_key:    consumer['key'],
    consumer_secret: consumer['secret'],
    output_interval: 1,
    history_size:    1000,
    api:             { :host => 'userstream.twitter.com', :path => '/2/user.json', :ssl => true },
    confirm_type:    :y,
    expand_url:      false,
    thread_indent:   "  ",
    no_data_timeout: 30
  }
end

#error(e) ⇒ Object



248
249
250
# File 'lib/earthquake/core.rb', line 248

def error(e)
  notify "[ERROR] #{e.message}\n#{e.backtrace.join("\n")}"
end

#init(&block) ⇒ Object



22
23
24
# File 'lib/earthquake/core.rb', line 22

def init(&block)
  inits << block
end

#initsObject



18
19
20
# File 'lib/earthquake/core.rb', line 18

def inits
  @inits ||= []
end

#invoke(command, options = {}) ⇒ Object



119
120
121
122
# File 'lib/earthquake/core.rb', line 119

def invoke(command, options = {})
  __init(options)
  input(command)
end

#item_queueObject



14
15
16
# File 'lib/earthquake/core.rb', line 14

def item_queue
  @item_queue ||= []
end

#load_configObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/earthquake/core.rb', line 77

def load_config
  config.reverse_update(default_config)

  [config[:dir], config[:plugin_dir]].each do |dir|
    unless File.exists?(dir)
      FileUtils.mkdir_p(dir)
    end
  end

  if File.exists?(config[:file])
    load config[:file]
  else
    File.open(config[:file], mode: 'w', perm: 0600).close
  end

  config.update(preferred_config) do |key, cur, new|
    if Hash === cur and Hash === new
      cur.merge(new)
    else
      new
    end
  end

  get_access_token unless self.config[:token] && self.config[:secret]
end

#load_pluginsObject



103
104
105
106
107
108
109
110
111
# File 'lib/earthquake/core.rb', line 103

def load_plugins
  Dir[File.join(config[:plugin_dir], '*.rb')].each do |lib|
    begin
      require_dependency lib
    rescue Exception => e
      error e
    end
  end
end

#mutexObject



228
229
230
# File 'lib/earthquake/core.rb', line 228

def mutex
  @mutex ||= Mutex.new
end

#notify(message, options = {}) ⇒ Object Also known as: n



252
253
254
255
256
257
258
# File 'lib/earthquake/core.rb', line 252

def notify(message, options = {})
  args = {:title => 'earthquake'}.update(options)
  title = args.delete(:title)
  message = message.is_a?(String) ? message : message.inspect
  # FIXME: Escaping should be done at Notify.notify
  Notify.notify title, message.e
end

#once(&block) ⇒ Object



30
31
32
# File 'lib/earthquake/core.rb', line 30

def once(&block)
  onces << block
end

#oncesObject



26
27
28
# File 'lib/earthquake/core.rb', line 26

def onces
  @once ||= []
end

#preferred_configObject



10
11
12
# File 'lib/earthquake/core.rb', line 10

def preferred_config
  @preferred_config ||= {}
end

#reconnectObject



163
164
165
166
# File 'lib/earthquake/core.rb', line 163

def reconnect
  item_queue.clear
  start_stream(config[:api])
end

#reloadObject



45
46
47
48
49
50
51
52
53
54
# File 'lib/earthquake/core.rb', line 45

def reload
  Gem.refresh
  loaded = ActiveSupport::Dependencies.loaded.dup
  ActiveSupport::Dependencies.clear
  loaded.each { |lib| require_dependency lib }
rescue Exception => e
  error e
ensure
  _init
end

#restore_historyObject



215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/earthquake/core.rb', line 215

def restore_history
  history_file = File.join(config[:dir], 'history')
  begin
    File.read(history_file, :encoding => "BINARY").
      encode!(:invalid => :replace, :undef => :replace).
      split(/\n/).
      each { |line| Readline::HISTORY << line }
  rescue Errno::ENOENT
  rescue Errno::EACCES => e
    error(e)
  end
end

#start(options = {}) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/earthquake/core.rb', line 124

def start(options = {})
  __init(options)
  restore_history

  EM.run do
    Thread.start do
      while buf = Readline.readline(config[:prompt], true)
        unless Readline::HISTORY.count == 1
          Readline::HISTORY.pop if buf.empty? || Readline::HISTORY[-1] == Readline::HISTORY[-2]
        end
        sync {
          reload
          store_history
          input(buf.strip)
        }
      end
      # unexpected
      stop
    end

    EM.add_periodic_timer(config[:output_interval]) do
      if @last_data_received_at && Time.now - @last_data_received_at > config[:no_data_timeout]
        begin
          reconnect
        rescue EventMachine::ConnectionError => e
          # ignore
        end
      end
      if Readline.line_buffer.nil? || Readline.line_buffer.empty?
        sync { output }
      end
    end

    reconnect unless options[:'no-stream'] == true

    trap('INT') { stop }
  end
end

#start_stream(options) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/earthquake/core.rb', line 168

def start_stream(options)
  stop_stream

  options = {
    :oauth => config.slice(:consumer_key, :consumer_secret).merge(
      :access_key => config[:token], :access_secret => config[:secret],
      :proxy => ENV['http_proxy']
    )
  }.merge(options)

  @stream = ::Twitter::JSONStream.connect(options)

  @stream.each_item do |item|
    @last_data_received_at = Time.now # for reconnect when no data
    item_queue << JSON.parse(item)
  end

  @stream.on_error do |message|
    notify "error: #{message}"
  end

  @stream.on_reconnect do |timeout, retries|
    notify "reconnecting in: #{timeout} seconds"
  end

  @stream.on_max_reconnects do |timeout, retries|
    notify "Failed after #{retries} failed reconnects"
  end
end

#stopObject



202
203
204
205
# File 'lib/earthquake/core.rb', line 202

def stop
  stop_stream
  EM.stop_event_loop
end

#stop_streamObject



198
199
200
# File 'lib/earthquake/core.rb', line 198

def stop_stream
  @stream.stop if @stream
end

#store_historyObject



207
208
209
210
211
212
213
# File 'lib/earthquake/core.rb', line 207

def store_history
  history_size = config[:history_size]
  File.open(File.join(config[:dir], 'history'), 'w') do |file|
    lines = Readline::HISTORY.to_a[([Readline::HISTORY.size - history_size, 0].max)..-1]
    file.print(lines.join("\n"))
  end
end

#sync(&block) ⇒ Object



232
233
234
235
236
# File 'lib/earthquake/core.rb', line 232

def sync(&block)
  mutex.synchronize do
    block.call
  end
end