pincerna 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +77 -0
- data/.travis-gemfile +17 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +23 -0
- data/LICENSE.md +21 -0
- data/README.md +211 -0
- data/Rakefile +45 -0
- data/bin/pincernad +56 -0
- data/docs/Pincerna.html +130 -0
- data/docs/Pincerna/Base.html +3051 -0
- data/docs/Pincerna/Bookmark.html +523 -0
- data/docs/Pincerna/Cache.html +767 -0
- data/docs/Pincerna/ChromeBookmark.html +308 -0
- data/docs/Pincerna/CurrencyConversion.html +589 -0
- data/docs/Pincerna/FirefoxBookmark.html +328 -0
- data/docs/Pincerna/Ip.html +1017 -0
- data/docs/Pincerna/Map.html +399 -0
- data/docs/Pincerna/SafariBookmark.html +308 -0
- data/docs/Pincerna/Server.html +673 -0
- data/docs/Pincerna/Translation.html +517 -0
- data/docs/Pincerna/UnitConversion.html +1042 -0
- data/docs/Pincerna/Version.html +189 -0
- data/docs/Pincerna/Vpn.html +561 -0
- data/docs/Pincerna/Weather.html +837 -0
- data/docs/_index.html +298 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +57 -0
- data/docs/css/style.css +338 -0
- data/docs/file.README.html +327 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +28 -0
- data/docs/index.html +327 -0
- data/docs/js/app.js +214 -0
- data/docs/js/full_list.js +178 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +389 -0
- data/docs/top-level-namespace.html +112 -0
- data/icon.png +0 -0
- data/images/chrome.png +0 -0
- data/images/currency.png +0 -0
- data/images/firefox.png +0 -0
- data/images/map.png +0 -0
- data/images/network.png +0 -0
- data/images/safari.png +0 -0
- data/images/translate.png +0 -0
- data/images/unit.png +0 -0
- data/images/vpn.png +0 -0
- data/images/weather.png +0 -0
- data/info.plist +961 -0
- data/it.cowtech.pincernad.plist +19 -0
- data/lib/pincerna.rb +30 -0
- data/lib/pincerna/base.rb +258 -0
- data/lib/pincerna/bookmark.rb +80 -0
- data/lib/pincerna/cache.rb +61 -0
- data/lib/pincerna/chrome_bookmark.rb +40 -0
- data/lib/pincerna/currency_conversion.rb +134 -0
- data/lib/pincerna/firefox_bookmark.rb +92 -0
- data/lib/pincerna/ip.rb +135 -0
- data/lib/pincerna/map.rb +30 -0
- data/lib/pincerna/safari_bookmark.rb +40 -0
- data/lib/pincerna/server.rb +85 -0
- data/lib/pincerna/translation.rb +67 -0
- data/lib/pincerna/unit_conversion.rb +120 -0
- data/lib/pincerna/version.rb +24 -0
- data/lib/pincerna/vpn.rb +74 -0
- data/lib/pincerna/weather.rb +188 -0
- data/pincerna.alfredworkflow +0 -0
- data/pincerna.gemspec +36 -0
- data/pincerna.sh +9 -0
- data/spec/cassettes/Pincerna_CurrencyConversion/_perform_filtering/should_return_valid_values.yml +38 -0
- data/spec/cassettes/Pincerna_Ip/_get_local_addresses/should_return_a_list_of_addresses.yml +47 -0
- data/spec/cassettes/Pincerna_Ip/_get_public_address/should_return_public_IP_address.yml +44 -0
- data/spec/cassettes/Pincerna_Translation/_perform_filtering/should_default_from_English_to_the_given_language_when_only_one_is_present.yml +124 -0
- data/spec/cassettes/Pincerna_Translation/_perform_filtering/should_query_Google_Translate_for_sentences_returning_no_alternatives.yml +51 -0
- data/spec/cassettes/Pincerna_Translation/_perform_filtering/should_query_Google_Translate_for_single_words.yml +71 -0
- data/spec/cassettes/Pincerna_Weather/_get_forecast/should_append_name.yml +177 -0
- data/spec/cassettes/Pincerna_Weather/_get_forecast/should_get_correct_forecasts.yml +339 -0
- data/spec/cassettes/Pincerna_Weather/_lookup_places/should_return_an_existing_WOEID_without_making_any_request.yml +56 -0
- data/spec/cassettes/Pincerna_Weather/_lookup_places/should_search_for_places.yml +189 -0
- data/spec/cassettes/Pincerna_Weather/_perform_filtering/should_get_forecast.yml +56 -0
- data/spec/coverage_helper.rb +44 -0
- data/spec/pincerna/base_spec.rb +166 -0
- data/spec/pincerna/bookmark_spec.rb +65 -0
- data/spec/pincerna/cache_spec.rb +88 -0
- data/spec/pincerna/chrome_bookmark_spec.rb +114 -0
- data/spec/pincerna/currency_conversion_spec.rb +46 -0
- data/spec/pincerna/firefox_bookmark_spec.rb +46 -0
- data/spec/pincerna/ip_spec.rb +194 -0
- data/spec/pincerna/map_spec.rb +24 -0
- data/spec/pincerna/safari_bookmark_spec.rb +237 -0
- data/spec/pincerna/server_spec.rb +108 -0
- data/spec/pincerna/translation_spec.rb +55 -0
- data/spec/pincerna/unit_conversion_spec.rb +98 -0
- data/spec/pincerna/vpn_spec.rb +68 -0
- data/spec/pincerna/weather_spec.rb +131 -0
- data/spec/spec_helper.rb +48 -0
- metadata +283 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>Label</key>
|
6
|
+
<string>it.cowtech.pincernad</string>
|
7
|
+
<key>KeepAlive</key>
|
8
|
+
<true/>
|
9
|
+
<key>RunAtLoad</key>
|
10
|
+
<true/>
|
11
|
+
<key>EnableGlobbing</key>
|
12
|
+
<true/>
|
13
|
+
<key>ProgramArguments</key>
|
14
|
+
<array>
|
15
|
+
<string>~/.rvm/bin/pincernad</string>
|
16
|
+
<string>-e production</string>
|
17
|
+
</array>
|
18
|
+
</dict>
|
19
|
+
</plist>
|
data/lib/pincerna.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the pincerna gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
|
4
|
+
# Licensed under the MIT license, which can be found at https://choosealicense.com/licenses/mit.
|
5
|
+
#
|
6
|
+
|
7
|
+
require "open-uri"
|
8
|
+
require "strscan"
|
9
|
+
require "cgi"
|
10
|
+
require "fileutils"
|
11
|
+
require "nokogiri"
|
12
|
+
require "oj"
|
13
|
+
require "yahoo_weatherman"
|
14
|
+
require "ruby-units"
|
15
|
+
require "plist"
|
16
|
+
require "daybreak"
|
17
|
+
require "em-synchrony"
|
18
|
+
require "em-synchrony/em-http"
|
19
|
+
|
20
|
+
require "pincerna/version" if !defined?(Pincerna::Version)
|
21
|
+
require "pincerna/base"
|
22
|
+
require "pincerna/cache"
|
23
|
+
require "pincerna/currency_conversion"
|
24
|
+
require "pincerna/ip"
|
25
|
+
require "pincerna/map"
|
26
|
+
require "pincerna/translation"
|
27
|
+
require "pincerna/unit_conversion"
|
28
|
+
require "pincerna/vpn"
|
29
|
+
require "pincerna/weather"
|
30
|
+
require "pincerna/bookmark"
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the pincerna gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
|
4
|
+
# Licensed under the MIT license, which can be found at https://choosealicense.com/licenses/mit.
|
5
|
+
#
|
6
|
+
|
7
|
+
# A bunch of useful Alfred 2 workflows.
|
8
|
+
module Pincerna
|
9
|
+
# Base class for all filter.
|
10
|
+
#
|
11
|
+
# @attribute [r] output
|
12
|
+
# @return [String] The output of filtering.
|
13
|
+
# @attribute [r] format
|
14
|
+
# @return [Symbol] The format of output. Can be `:xml` (default) or `:yml`.
|
15
|
+
# @attribute [r] format_content_type
|
16
|
+
# @return [String] The content type of the format. Can be `text/xml` (default) or `text/x-yaml`.
|
17
|
+
class Base
|
18
|
+
# The expression to match.
|
19
|
+
MATCHER = /^(?<all>.*)$/i
|
20
|
+
|
21
|
+
# Relevant groups in the match.
|
22
|
+
RELEVANT_MATCHES = {
|
23
|
+
"all" => ->(_, value) { value }
|
24
|
+
}
|
25
|
+
|
26
|
+
# Recognized types of filtering
|
27
|
+
TYPES = {
|
28
|
+
"unit_conversion" => /^(convert|unit|c)$/,
|
29
|
+
"currency_conversion" => /^(currency|cc)$/,
|
30
|
+
"translation" => /^(translate|t)$/,
|
31
|
+
"map" => /^(map|m)$/,
|
32
|
+
"weather" => /^(forecast|weather)$/,
|
33
|
+
"ip" => /^ip$/,
|
34
|
+
"vpn" => /^vpn$/,
|
35
|
+
"chrome_bookmark" => /^(chrome-bookmark|bc)$/,
|
36
|
+
"safari_bookmark" => /^(safari-bookmark|bs)$/,
|
37
|
+
"firefox_bookmark" => /^(firefox-bookmark|bf)$/
|
38
|
+
}
|
39
|
+
|
40
|
+
# The full name of the gem
|
41
|
+
FULL_NAME = "it.cowtech.pincerna"
|
42
|
+
|
43
|
+
# The root of the pincerna gem
|
44
|
+
ROOT = File.expand_path(File.dirname(__FILE__) + "/../../")
|
45
|
+
|
46
|
+
# The root of the cache
|
47
|
+
CACHE_ROOT = File.expand_path("~/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/#{FULL_NAME}")
|
48
|
+
|
49
|
+
# The root of alfred workflows
|
50
|
+
WORKFLOW_ROOT = File.expand_path("~/Library/Application Support/Alfred 2/Alfred.alfredpreferences/workflows/#{FULL_NAME}")
|
51
|
+
|
52
|
+
attr_reader :output, :format, :format_content_type
|
53
|
+
|
54
|
+
# Executes a filtering query.
|
55
|
+
#
|
56
|
+
# @param type [Symbol] The type of the query.
|
57
|
+
# @param query [String] The argument of the query.
|
58
|
+
# @param format [String] The format to use. Valid values are `:xml` (default) and `:yml`.
|
59
|
+
# @param debug [String] The debug mode.
|
60
|
+
# @return [String] The result of the query.
|
61
|
+
def self.execute!(type, query, format = :xml, debug = nil)
|
62
|
+
instance = nil
|
63
|
+
|
64
|
+
type = catch(:type) do
|
65
|
+
TYPES.each do |file, matcher|
|
66
|
+
throw(:type, file) if type =~ matcher
|
67
|
+
end
|
68
|
+
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
if type
|
73
|
+
instance = find_class(type).new(query, format, debug)
|
74
|
+
instance.filter
|
75
|
+
end
|
76
|
+
|
77
|
+
instance
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a new query.
|
81
|
+
#
|
82
|
+
# @param query [String] The argument of the query.
|
83
|
+
# @param requested_format [String] The format to use. Valid values are `xml` (default), `yaml` or `yml`.
|
84
|
+
# @param debug [String] The debug mode.
|
85
|
+
def initialize(query, requested_format = "xml", debug = nil)
|
86
|
+
@query = query.strip.gsub("\\ ", " ")
|
87
|
+
@cache_dir = CACHE_ROOT
|
88
|
+
|
89
|
+
if requested_format =~ /^y(a?)ml$/i then
|
90
|
+
@format = :yml
|
91
|
+
@format_content_type = "text/x-yaml"
|
92
|
+
else
|
93
|
+
@format = :xml
|
94
|
+
@format_content_type = "text/xml"
|
95
|
+
end
|
96
|
+
|
97
|
+
@debug = debug
|
98
|
+
@feedback_items = []
|
99
|
+
end
|
100
|
+
|
101
|
+
# Filters a query.
|
102
|
+
#
|
103
|
+
# @return [String] The feedback items of the query, formatted as XML.
|
104
|
+
def filter
|
105
|
+
# Match the query
|
106
|
+
matches = self.class::MATCHER.match(@query)
|
107
|
+
|
108
|
+
if matches then
|
109
|
+
# Execute the filtering
|
110
|
+
results = execute_filtering(matches)
|
111
|
+
|
112
|
+
# Show results if appropriate
|
113
|
+
process_results(results).each {|r| add_feedback_item(r) } if !results.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
output_feedback
|
117
|
+
end
|
118
|
+
|
119
|
+
# Filters a query.
|
120
|
+
#
|
121
|
+
# @param args [Array] The arguments of the query.
|
122
|
+
# @return [Array] A list of items to process.
|
123
|
+
def perform_filtering(*args)
|
124
|
+
raise ArgumentError.new("Must be overriden by subclasses.")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Processes items to obtain feedback items.
|
128
|
+
#
|
129
|
+
# @param results [Array] The items to process.
|
130
|
+
# @return [Array] The feedback items.
|
131
|
+
def process_results(results)
|
132
|
+
raise ArgumentError.new("Must be overriden by subclasses.")
|
133
|
+
end
|
134
|
+
|
135
|
+
# Adds a feedback items.
|
136
|
+
#
|
137
|
+
# @param item [Array] The items to add.
|
138
|
+
def add_feedback_item(item)
|
139
|
+
@feedback_items << item
|
140
|
+
end
|
141
|
+
|
142
|
+
# Prepares the feedback for output.
|
143
|
+
def output_feedback
|
144
|
+
if format == :xml then
|
145
|
+
@output = Nokogiri::XML::Builder.new { |xml|
|
146
|
+
xml.items do
|
147
|
+
@feedback_items.each do |item|
|
148
|
+
childs, attributes = split_output_item(item)
|
149
|
+
|
150
|
+
xml.item(attributes) do
|
151
|
+
childs.each { |name, value| xml.send(name, value) }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
}.to_xml
|
156
|
+
else
|
157
|
+
@output = @feedback_items.to_yaml
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Rounds a float to a certain precision.
|
162
|
+
#
|
163
|
+
# @param value [Float] The value to convert.
|
164
|
+
# @param precision [Fixnum] The precision to use.
|
165
|
+
# @return [Float] The rounded value.
|
166
|
+
def round_float(value, precision = 3)
|
167
|
+
factor = 10**precision
|
168
|
+
(value * factor).round.to_f / factor
|
169
|
+
end
|
170
|
+
|
171
|
+
# Rounds a float to a certain precision and prints it as a string. Unneeded leading zero are remove.
|
172
|
+
#
|
173
|
+
# @param value [Float] The value to print.
|
174
|
+
# @param precision [Fixnum] The precision to use.
|
175
|
+
# @return [String] The formatted value.
|
176
|
+
def format_float(value, precision = 3)
|
177
|
+
value = round_float(value, precision)
|
178
|
+
value = "%0.#{precision}f" % value
|
179
|
+
value.gsub(/\.?0+$/, "")
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
# Instantiates the new class.
|
184
|
+
#
|
185
|
+
# @param file [String] The file name.
|
186
|
+
def self.find_class(file)
|
187
|
+
Pincerna.const_get(file.capitalize.gsub(/_(.)/) { $1.upcase})
|
188
|
+
end
|
189
|
+
|
190
|
+
# Executes filtering on matched data.
|
191
|
+
#
|
192
|
+
# @param matches [MatchData] The matched data.
|
193
|
+
# @return [Array] The results of filtering.
|
194
|
+
def execute_filtering(matches)
|
195
|
+
# Get relevant matches and arguments.
|
196
|
+
relevant = self.class::RELEVANT_MATCHES
|
197
|
+
args = relevant.map {|key, value| value.call(self, matches[key]) }
|
198
|
+
|
199
|
+
# Now perform the operation
|
200
|
+
begin
|
201
|
+
perform_filtering(*args)
|
202
|
+
rescue => e
|
203
|
+
raise e if debug_mode == :error
|
204
|
+
[]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Converts an array of key-value pairs to an hash.
|
209
|
+
#
|
210
|
+
# @param array [Array] The array to convert.
|
211
|
+
# @return [Hash] The converted hash.
|
212
|
+
def array_to_hash(array)
|
213
|
+
array.reduce({}){ |rv, entry|
|
214
|
+
rv[entry[0]] = entry[1]
|
215
|
+
rv
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
# Gets attributes and children for output.
|
220
|
+
#
|
221
|
+
# @param item [Hash] The output item.
|
222
|
+
# @return [Array] An array with children and attributes.
|
223
|
+
def split_output_item(item)
|
224
|
+
item.partition {|k, _| [:title, :subtitle, :icon].include?(k) }.map {|a| array_to_hash(a) }
|
225
|
+
end
|
226
|
+
|
227
|
+
# Fetches remote JSON resource.
|
228
|
+
#
|
229
|
+
# @param url [String] The URL to get.
|
230
|
+
# @param params [Hash] The parameters to pass to the server.
|
231
|
+
# @param json [Boolean] If the response is a JSON object.
|
232
|
+
# @return [Hash] The server's response.
|
233
|
+
def fetch_remote_resource(url, params, json = true)
|
234
|
+
args = {query: params}
|
235
|
+
args[:head] = {"accept" => "application/json"} if json
|
236
|
+
response = EM::HttpRequest.new(url, {connect_timeout: 5}).get(args).response
|
237
|
+
json ? Oj.load(response) : response
|
238
|
+
end
|
239
|
+
|
240
|
+
# Executes a command and returns its output.
|
241
|
+
#
|
242
|
+
# @param args [Array] The command to execute, with its arguments.
|
243
|
+
# @return [String] The output of the command
|
244
|
+
def execute_command(*args)
|
245
|
+
rv = ""
|
246
|
+
IO.popen(args) {|f| rv = f.read }
|
247
|
+
rv
|
248
|
+
end
|
249
|
+
|
250
|
+
# Returns the current debug mode.
|
251
|
+
#
|
252
|
+
# @return [Boolean|NilClass] The current debug mode.
|
253
|
+
def debug_mode
|
254
|
+
mode = ENV["PINCERNA_DEBUG"] || @debug
|
255
|
+
!mode.nil? ? mode.to_sym : nil
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the pincerna gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
|
4
|
+
# Licensed under the MIT license, which can be found at https://choosealicense.com/licenses/mit.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Pincerna
|
8
|
+
# Show the list of Safari bookmarks.
|
9
|
+
class Bookmark < Base
|
10
|
+
# The expression to match.
|
11
|
+
MATCHER = /^(?<all>.*)$/i
|
12
|
+
|
13
|
+
# The icon to show for each feedback item.
|
14
|
+
ICON = Pincerna::Base::ROOT + "/images/safari.png"
|
15
|
+
|
16
|
+
# The separator for paths.
|
17
|
+
SEPARATOR = "\u2192"
|
18
|
+
|
19
|
+
# Reads the list of Chrome Bookmarks.
|
20
|
+
#
|
21
|
+
# @param query [Array] A query to match against bookmarks names.
|
22
|
+
# @return [Array] A list of boomarks.
|
23
|
+
def perform_filtering(query)
|
24
|
+
Pincerna::Cache.instance.use("bookmarks:#{self.class.to_s}", Pincerna::Cache::EXPIRATIONS[:short]) do
|
25
|
+
# Get bookmarks and then only keep valid ones
|
26
|
+
@bookmarks = []
|
27
|
+
read_bookmarks
|
28
|
+
filter_and_sort(@bookmarks, query)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Processes items to obtain feedback items.
|
33
|
+
#
|
34
|
+
# @param results [Array] The items to process.
|
35
|
+
# @return [Array] The feedback items.
|
36
|
+
def process_results(results)
|
37
|
+
results.map do |result|
|
38
|
+
subtitle = result[:path] ? result[:path].gsub(/^\s\u2192 /, "") : "Action this item to open the URL in the browser ..."
|
39
|
+
{title: result[:name], arg: result[:url], subtitle: subtitle, icon: ICON}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reads the list of bookmarks.
|
44
|
+
def read_bookmarks
|
45
|
+
raise ArgumentError.new("Must be overriden by subclasses.")
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# Filters and sorts bookmarks.
|
50
|
+
#
|
51
|
+
# @param bookmarks [Array] The bookmarks list.
|
52
|
+
# @param query [String] The query to filter.
|
53
|
+
def filter_and_sort(bookmarks, query)
|
54
|
+
# Filtering
|
55
|
+
matcher = !query.empty? ? /^#{Regexp.escape(query.downcase)}/i : nil
|
56
|
+
bookmarks = bookmarks.select {|bookmark| bookmark[:name] =~ matcher } if matcher
|
57
|
+
|
58
|
+
# Sorting
|
59
|
+
bookmarks.sort {|first, second|
|
60
|
+
cmp = first[:name] <=> second[:name]
|
61
|
+
cmp = first[:path] <=> second[:path] if cmp == 0
|
62
|
+
cmp
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
# Adds a bookmarks to the list.
|
68
|
+
#
|
69
|
+
# @param title [String] The title of the bookmark.
|
70
|
+
# @param url [String] The URL of the bookmark.
|
71
|
+
# @param path [String] The id of the parent folder.
|
72
|
+
def add_bookmark(title, url, path)
|
73
|
+
@bookmarks << {name: title, url: url, path: path} if title && !title.empty? && url && !url.empty? && url !~ /^javascript:/
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
require "pincerna/safari_bookmark"
|
79
|
+
require "pincerna/firefox_bookmark"
|
80
|
+
require "pincerna/chrome_bookmark"
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the pincerna gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
|
4
|
+
# Licensed under the MIT license, which can be found at https://choosealicense.com/licenses/mit.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Pincerna
|
8
|
+
# A utility class to handle caching.
|
9
|
+
#
|
10
|
+
# @attribute [r] data
|
11
|
+
# @return [Daybreak::DB] The cache store.
|
12
|
+
class Cache
|
13
|
+
# Expiration of keys.
|
14
|
+
EXPIRATIONS = {short: 1800, long: 2592000} # 30 min, 1 month
|
15
|
+
|
16
|
+
# Location of the cache file
|
17
|
+
FILE = Pincerna::Base::CACHE_ROOT + "/cache.db"
|
18
|
+
|
19
|
+
attr_reader :data
|
20
|
+
|
21
|
+
# Returns the instance of the cache.
|
22
|
+
def self.instance
|
23
|
+
@instance ||= Pincerna::Cache.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates a new cache object.
|
27
|
+
def initialize
|
28
|
+
FileUtils.mkdir_p(File.dirname(FILE))
|
29
|
+
@data = Daybreak::DB.new(FILE)
|
30
|
+
@flusher = EM.add_periodic_timer(5) { Pincerna::Cache.instance.flush }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Closes the cache data.
|
34
|
+
def destroy
|
35
|
+
@flusher.cancel
|
36
|
+
@data.close rescue nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Flush data into disk.
|
40
|
+
def flush
|
41
|
+
@data.flush
|
42
|
+
@data.compact
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use data from cache or fetches new data.
|
46
|
+
#
|
47
|
+
# @param key [String] The key of the data.
|
48
|
+
# @param expiration [Fixnum] Expiration of new data, in seconds.
|
49
|
+
def use(key, expiration)
|
50
|
+
value = @data[key]
|
51
|
+
|
52
|
+
if !value || Time.now.to_f > value[:expiration] then
|
53
|
+
data = yield
|
54
|
+
@data[key] = {data: data, expiration: Time.now.to_f + expiration}
|
55
|
+
data
|
56
|
+
else
|
57
|
+
value[:data]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|