-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.rb
142 lines (112 loc) · 3.41 KB
/
app.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
require 'sinatra'
require 'json'
require 'factbook'
require 'redis'
require 'logger'
logger = Logger.new(STDOUT)
logger.level = Logger::INFO
# connect to the local Redis instance
@@redis = Redis.new
countries_index = {}
# executed only once, at startup
configure do
# to prevents errors due to Rack::Protection::JsonCsrf
disable :protection
# build a quick index country_code => country_name
Factbook.codes.each do |code|
countries_index[code.code] = code.name
end
logger.info("Index created, ready to handle requests")
end
# executed before processing any http request
before do
# Set the response headers
cache_control :public, :must_revalidate, :max_age => 86400
content_type 'application/json'
end
after do
# pretty print the JSON response
if response.body[0] != nil
response.body[0] = JSON.pretty_generate(JSON.load(response.body[0]))
end
end
# return the list of countries codes
get '/factbook/codes' do
Factbook.codes.to_json
end
# return the list of supported attributes
get '/factbook/attributes' do
File.read("data/attributes.json")
end
get '/factbook/:code' do
code = params['code']
# validate that the country code exists
if not countries_index.key?(code)
halt 404 # return 404 not found
end
factbook_page = fetch_factbook_page(code)
# create the response
response = { 'country-code' => code, 'country-name' => countries_index[code] }
response['data'] = factbook_page
JSON.generate(response)
end
get '/factbook/:code/:attribute' do
code = params['code']
attribute = params['attribute']
# validate that the country code exists
if not countries_index.key?(code)
halt 404 # return 404 not found
end
factbook_page = fetch_factbook_page(code)
# validate that the attribute exists
attribute_function = attribute.gsub("-","_")
if !factbook_page.respond_to? attribute_function
halt 404 # return 404 not found
end
# get the data for this specific attribute
text = factbook_page.public_send(attribute_function).to_s
# create the response
response = { 'country-code' => code, 'country-name' => countries_index[code] }
response[attribute] = { 'text' => text, 'numbers' => extract_numbers(text)}
JSON.generate(response)
end
# fetches data from the CIA World Factbook for a specific country
#
# it seems the website is updated weekly, so once fetched the page will
# be saved in Redis with an expiration of
#
def fetch_factbook_page(country_code)
logger.info("Fetching data for country code : #{country_code}")
key = "factbook.#{country_code}"
cached_data = @@redis.get(key)
if cached_data != nil
logger.info("Found data in the cache")
page = Marshal.load(cached_data)
return page
else
logger.info("No data in the cache, connecting to the CIA World Factbook...")
page = Factbook::Page.new(country_code)
page_json = Marshal.dump(page)
one_week = 604800
logger.info("Saving the data in the cache")
@@redis.setex(key, one_week, page_json)
return page
end
end
# extracts numbers from a string and return them as an array
# ex : "7,491 km" => [7491.0]
# ex : "0.75% (2016 est.)" => [0.75, 2016.0]
#
def extract_numbers(string)
results = []
numbers = string.scan(/(\d+[.?,?\d*]*\s?)/i)
if numbers != nil
numbers.each do |number|
puts number[0]
formatted_number = number[0].gsub(",", "")
final_number = formatted_number.gsub(" ", "").to_f
results << final_number
end
end
results
end