pincerna 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|