housing_misc 0.5.4
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/README.md +125 -0
- data/app/controllers/housing_misc/application_controller.rb +11 -0
- data/app/controllers/housing_misc/cache_controller.rb +11 -0
- data/app/controllers/housing_misc/logs_controller.rb +25 -0
- data/app/controllers/housing_misc/model_reflections_controller.rb +172 -0
- data/app/controllers/housing_misc/sidekiq_queue_check_controller.rb +62 -0
- data/app/middleware/request_tracer.rb +19 -0
- data/config/routes.rb +6 -0
- data/lib/helpers/configuration.rb +27 -0
- data/lib/housing_misc.rb +31 -0
- data/lib/housing_misc/aerospike_cache_extension.rb +100 -0
- data/lib/housing_misc/api_helper.rb +239 -0
- data/lib/housing_misc/boolean_evaluator.rb +7 -0
- data/lib/housing_misc/diff_checker.rb +30 -0
- data/lib/housing_misc/distance_unit.rb +140 -0
- data/lib/housing_misc/encrypt_decrypt.rb +21 -0
- data/lib/housing_misc/engine.rb +10 -0
- data/lib/housing_misc/json_slice_render.rb +44 -0
- data/lib/housing_misc/models_reflections_helper.rb +111 -0
- data/lib/housing_misc/net_http_generic_request.rb +14 -0
- data/lib/housing_misc/railtie.rb +24 -0
- data/lib/housing_misc/slice_helper.rb +43 -0
- data/lib/housing_misc/version.rb +3 -0
- metadata +200 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Configuration
|
2
|
+
|
3
|
+
def configuration
|
4
|
+
yield self
|
5
|
+
end
|
6
|
+
|
7
|
+
def define_setting(name, default = nil)
|
8
|
+
class_variable_set("@@#{name}", default)
|
9
|
+
|
10
|
+
define_class_method "#{name}=" do |value|
|
11
|
+
class_variable_set("@@#{name}", value)
|
12
|
+
end
|
13
|
+
|
14
|
+
define_class_method name do
|
15
|
+
class_variable_get("@@#{name}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def define_class_method(name, &block)
|
22
|
+
(class << self; self; end).instance_eval do
|
23
|
+
define_method name, &block
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/housing_misc.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require 'helpers/configuration'
|
3
|
+
module HousingMisc
|
4
|
+
extend Configuration
|
5
|
+
|
6
|
+
define_setting :aws_access_token
|
7
|
+
define_setting :aws_access_secret
|
8
|
+
define_setting :region
|
9
|
+
define_setting :bucket
|
10
|
+
define_setting :origin
|
11
|
+
define_setting :credentials_flag
|
12
|
+
define_setting :expose_headers
|
13
|
+
define_setting :max_age
|
14
|
+
define_setting :sidekiq_custom_threshold, {}
|
15
|
+
define_setting :sidekiq_message_url, "https://hooks.slack.com/services/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"
|
16
|
+
define_setting :app_name
|
17
|
+
|
18
|
+
|
19
|
+
require 'housing_misc/api_helper'
|
20
|
+
require 'housing_misc/engine'
|
21
|
+
require 'housing_misc/slice_helper'
|
22
|
+
require 'housing_misc/json_slice_render'
|
23
|
+
require 'housing_misc/boolean_evaluator'
|
24
|
+
require 'housing_misc/railtie'
|
25
|
+
require 'housing_misc/distance_unit'
|
26
|
+
require 'housing_misc/models_reflections_helper'
|
27
|
+
require 'housing_misc/aerospike_cache_extension'
|
28
|
+
require 'housing_misc/encrypt_decrypt'
|
29
|
+
require 'housing_misc/net_http_generic_request'
|
30
|
+
require 'housing_misc/diff_checker'
|
31
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'connection_pool'
|
2
|
+
require 'active_support/cache/aerospike_store'
|
3
|
+
module HousingMisc
|
4
|
+
class AerospikeCacheExtension < ActiveSupport::Cache::AerospikeStore
|
5
|
+
READ_WRITE_BATCH_SIZE = 10
|
6
|
+
def initialize(options = {})
|
7
|
+
super
|
8
|
+
aerospike_connection_policy = ::Aerospike::ClientPolicy.new(::JSON.parse(::Figaro.env.aerospike_connection_policy).with_indifferent_access)
|
9
|
+
@conn_pool = ConnectionPool.new(size: 20) { ::Aerospike::Client.new(::JSON.parse(Housing.aerospike_hosts).collect {|host_port| host_port.join(':') }.join(','), policy: aerospike_connection_policy)}
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def read_multi(*names)
|
14
|
+
results = {}
|
15
|
+
aerospike_cache_config = ::JSON.parse(::Figaro.env.aerospike_cache_config)
|
16
|
+
key_list = names.map {|key| Aerospike::Key.new(aerospike_cache_config['namespace'], aerospike_cache_config['set'], key) }
|
17
|
+
@conn_pool.with do |conn|
|
18
|
+
unformatted_rslt = []
|
19
|
+
key_list.each_slice(READ_WRITE_BATCH_SIZE) do |batch_names|
|
20
|
+
unformatted_rslt += conn.batch_get(batch_names)
|
21
|
+
end
|
22
|
+
names.each_with_index do |name, i|
|
23
|
+
if unformatted_rslt[i] != nil
|
24
|
+
results[name] = decrypt_data(unformatted_rslt[i].bins['cache_entry']).value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return results
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch(key, options=nil)
|
32
|
+
if block_given?
|
33
|
+
if !options.nil? and options[:force]
|
34
|
+
result = yield
|
35
|
+
write(key, result, options)
|
36
|
+
return result
|
37
|
+
end
|
38
|
+
|
39
|
+
cached_object = read(key, options)
|
40
|
+
return cached_object if cached_object != nil
|
41
|
+
|
42
|
+
result = yield
|
43
|
+
write(key, result, options)
|
44
|
+
return result
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def fetch_multi(*names)
|
50
|
+
options = names.extract_options!
|
51
|
+
options = merged_options(options)
|
52
|
+
results = read_multi(*names, options)
|
53
|
+
names.map do |name|
|
54
|
+
results.fetch(name) do
|
55
|
+
value = yield name
|
56
|
+
write(name, value, options)
|
57
|
+
results[name] = value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
results
|
61
|
+
end
|
62
|
+
|
63
|
+
def write_multi(hash)
|
64
|
+
hash.each { |key, value| write(key, value.to_json) }
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def read_entry(key, options)
|
70
|
+
if value = internal_read_entry(key, options)
|
71
|
+
# if it is not raw it is a marshalled ActiveSupport::Cache::Entry
|
72
|
+
value = options[:raw]? ActiveSupport::Cache::Entry.new(value) : decrypt_data(value)
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def write_entry(key, entry, options)
|
79
|
+
options[:expiration] ||= options[:expires_in] if options[:expires_in]
|
80
|
+
options[:record_exists_action] ||= options[:unless_exist]? Aerospike::RecordExistsAction::CREATE_ONLY : Aerospike::RecordExistsAction::REPLACE
|
81
|
+
value = options[:raw]? entry.value : Marshal.dump(entry)
|
82
|
+
begin
|
83
|
+
@client.put(as_key(key, options), {options[:bin] => Zlib::Deflate.deflate(value)}, options)
|
84
|
+
rescue Aerospike::Exceptions::Aerospike => e
|
85
|
+
raise unless (e.result_code == Aerospike::ResultCode::KEY_EXISTS_ERROR)
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def decrypt_data(data)
|
92
|
+
begin
|
93
|
+
return nil if data == nil
|
94
|
+
return Marshal.load(Zlib::Inflate.inflate(data))
|
95
|
+
rescue Zlib::DataError => e
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
module HousingMisc::ApiHelper
|
2
|
+
require 'openssl'
|
3
|
+
MOBILE_REQUEST_TIME_THRESHOLD = 300 #in seconds
|
4
|
+
|
5
|
+
def get_request_attribute_value(headers, params, key)
|
6
|
+
headers["HTTP_#{key.upcase.tr('-', '_')}"] || params[key]
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter_parameters(params)
|
10
|
+
filters = Rails.application.config.filter_parameters rescue []
|
11
|
+
f = ActionDispatch::Http::ParameterFilter.new filters
|
12
|
+
return f.filter(params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_request_internal?(request)
|
16
|
+
Rails.env != "production" || request.host.include?("internal")
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_request_from_mobile?(request, params, velocity_salt)
|
20
|
+
source = get_request_attribute_value(request.headers, params, "source") || ""
|
21
|
+
time_stamp = get_request_attribute_value(request.headers, params, "ts")
|
22
|
+
signed_param = get_request_attribute_value(request.headers, params, "sp")
|
23
|
+
|
24
|
+
if time_stamp.blank? || signed_param.blank?
|
25
|
+
if ["android", "ios"].include?(source.downcase) # To be removed once all the users moves to latest app version which includes ts-sp thing
|
26
|
+
log_file ||= Logger.new("#{Rails.root}/log/app_requests_with_missing_params.log")
|
27
|
+
log_invalid_csrf_request(log_file, request, cookies, params)
|
28
|
+
return true
|
29
|
+
elsif params["app_name"] == "locon.com.datacollection" # To be removed once dc app implements ts-sp thing
|
30
|
+
log_file ||= Logger.new("#{Rails.root}/log/dc_app_requests_with_missing_params.log")
|
31
|
+
log_invalid_csrf_request(log_file, request, cookies, params)
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
else
|
35
|
+
return ((Time.now.to_i - time_stamp.to_i) <= MOBILE_REQUEST_TIME_THRESHOLD) && (Digest::MD5.hexdigest(velocity_salt + time_stamp) == signed_param)
|
36
|
+
end
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_request_csrf_valid?(request, cookies, params, csrf_encryption_key, velocity_salt)
|
41
|
+
if request.get? || is_request_internal?(request) || is_request_from_mobile?(request, params, velocity_salt)
|
42
|
+
return true
|
43
|
+
else
|
44
|
+
csrf_id = cookies["cuid"] || ""
|
45
|
+
csrf_token = request.headers["HTTP_X_CSRF_TOKEN_V2"]
|
46
|
+
return OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), csrf_encryption_key, csrf_id) == csrf_token
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def internal_host_check
|
51
|
+
unless is_request_internal?(request)
|
52
|
+
log_file ||= Logger.new("#{Rails.root}/log/unauthorized_api_requests.log")
|
53
|
+
log_file.info "Internal host check violated | remote_ip:#{request.remote_ip}, host:#{request.host}, port:#{request.port}, params:#{filter_parameters(params)}"
|
54
|
+
render :json => {:message => "You are not authorized to make this call"}, status: 401
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def csrf_check(csrf_encryption_key, velocity_salt)
|
59
|
+
unless is_request_csrf_valid?(request, cookies, params, csrf_encryption_key, velocity_salt)
|
60
|
+
log_file ||= Logger.new("#{Rails.root}/log/unauthorized_api_requests.log")
|
61
|
+
log_invalid_csrf_request(log_file, request, cookies, params)
|
62
|
+
render :json => {:message => "You are not authorized to make this call", :error => "CSRF_CHECK_FAILED"}, status: 401
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def log_invalid_csrf_request(log_file, request, cookies, params)
|
67
|
+
log_file.info "CSRF check violated | new_csrf_token:#{request.headers["HTTP_X_CSRF_TOKEN_V2"]} | new_csrf_id:#{cookies["cuid"]} | remote_ip:#{request.remote_ip} | host:#{request.host} | port:#{request.port} | method:#{request.method} | referrer:#{request.referrer} | User-Agent:#{request.headers["User-Agent"]} | source:#{get_request_attribute_value(request.headers, params, "source")} | time_stamp: #{get_request_attribute_value(request.headers, params, "ts")} | signed_param:#{get_request_attribute_value(request.headers, params, "sp")} | params:#{filter_parameters(params)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_generic_mail(template_slug, template_version, users, base_vars, merge_vars, high_priority=false, attachments=[], deferred=false, send_time=nil, google_analytics_campaign=nil)
|
71
|
+
base_vars = base_vars.with_indifferent_access
|
72
|
+
merge_vars = merge_vars.with_indifferent_access
|
73
|
+
email_template_hash = get_email_template_from_mail_service(template_slug, template_version).with_indifferent_access
|
74
|
+
email_template_hash = add_base_vars(email_template_hash, base_vars)
|
75
|
+
if google_analytics_campaign.present?
|
76
|
+
email_template_hash[:params_json].merge!(google_analytics_campaign: google_analytics_campaign)
|
77
|
+
end
|
78
|
+
email_template_hash = add_user_emails(email_template_hash, users)
|
79
|
+
email_template_hash = add_global_merge_vars(email_template_hash, merge_vars)
|
80
|
+
email_template_hash = add_merge_vars(email_template_hash, merge_vars)
|
81
|
+
email_template_hash = add_attachments(email_template_hash, attachments)
|
82
|
+
if deferred
|
83
|
+
response = Net::HTTP.post_form( URI( "#{Housing.mail_service_url}v1/email/send_deferred" ), "email_request"=> email_template_hash.to_json, "high_priority"=>high_priority, "send_time" => send_time)
|
84
|
+
else
|
85
|
+
response = Net::HTTP.post_form( URI( "#{Housing.mail_service_url}v1/email/send" ), "email_request"=> email_template_hash.to_json, "high_priority"=>high_priority)
|
86
|
+
end
|
87
|
+
return response.code.to_i, response.body
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_base_vars(email_template_hash, base_vars)
|
91
|
+
email_template_hash[:params_json].merge!(base_vars)
|
92
|
+
email_template_hash[:test_email] = Housing.mail_test_id
|
93
|
+
return email_template_hash
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_user_emails(email_template_hash, users)
|
97
|
+
email_template_hash[:params_json][:users] = []
|
98
|
+
users.each do |user_email|
|
99
|
+
user_email = user_email.with_indifferent_access
|
100
|
+
email_template_hash[:params_json][:users].push({name: user_email[:name], email: user_email[:email]})
|
101
|
+
end
|
102
|
+
return email_template_hash
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_global_merge_vars(email_template_hash, vars)
|
106
|
+
email_template_hash[:params_json][:global_merge_vars][:modules].each do |mod_name|
|
107
|
+
mod_name[:merge_vars].first.each do |key, value|
|
108
|
+
mod_name[:merge_vars].first[key] = vars[key] if value.nil?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return email_template_hash
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_merge_vars(email_template_hash, vars)
|
115
|
+
email_template_hash[:params_json][:merge_vars] = []
|
116
|
+
email_template_hash[:params_json][:users].each do |user_email|
|
117
|
+
email = user_email[:email]
|
118
|
+
email_template_hash[:params_json][:merge_vars].push(email_template_hash[:params_json][:global_merge_vars].merge({email: email}))
|
119
|
+
end
|
120
|
+
if email_template_hash[:params_json][:global_merge_vars][:template_merge_vars].present?
|
121
|
+
email_template_hash[:params_json][:global_merge_vars][:template_merge_vars].each do |key, value|
|
122
|
+
email_template_hash[:params_json][:global_merge_vars][:template_merge_vars][key] = vars[key]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
return email_template_hash
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_attachments(email_template_hash, attachments)
|
129
|
+
email_template_hash[:params_json][:attachments] = attachments if attachments.present?
|
130
|
+
return email_template_hash
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def get_email_template_from_mail_service(template_slug, template_version)
|
135
|
+
template_name = template_slug.gsub("-","_")
|
136
|
+
uri = URI.parse("#{Housing.mail_service_url}v1/email/request_format?template_name=#{template_name}&template_version=#{template_version}")
|
137
|
+
response = (Oj.load(Net::HTTP.get(uri))["data"]) rescue {}
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_past_channel_details(profiles_hash, start_datetime, end_datetime, channel, template_details)
|
141
|
+
response = {}
|
142
|
+
query_params = {
|
143
|
+
template_name: template_details[:template_name],
|
144
|
+
end_datetime: end_datetime
|
145
|
+
}
|
146
|
+
query_params[:template_version] = template_details[:template_version] if channel == "email"
|
147
|
+
|
148
|
+
profiles_date_hash = {}
|
149
|
+
profiles_hash.each do |key, value|
|
150
|
+
date = value["last_activity_time"] || start_datetime
|
151
|
+
date = Date.strptime(date.to_s,'%s').to_time.to_i
|
152
|
+
(profiles_date_hash[date] ||= []) << key
|
153
|
+
end
|
154
|
+
|
155
|
+
profiles_date_hash.each do |date, email_or_numbers|
|
156
|
+
# increasing batch size would lead to exception 414 URI too large, Please don't increase batch size
|
157
|
+
email_or_numbers.each_slice(100) do |batch|
|
158
|
+
query_params[identifier[channel.to_sym]] = batch.join(',')
|
159
|
+
query_params[:start_datetime] = date
|
160
|
+
query = query_params.to_query
|
161
|
+
url = send("#{channel}_report_url".to_sym) + query
|
162
|
+
batch_response = (Oj.load(get_api_call(url))["data"])
|
163
|
+
response.merge!(batch_response)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
response
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_api_call(url, request_timeout = 15.0)
|
170
|
+
parsed_url = URI.parse(url)
|
171
|
+
http = Net::HTTP.new(parsed_url.host, parsed_url.port)
|
172
|
+
http.read_timeout = request_timeout
|
173
|
+
request = Net::HTTP::Get.new(parsed_url.request_uri)
|
174
|
+
begin
|
175
|
+
response = http.request(request)
|
176
|
+
raise "request failed :: #{response}" if response.code != "200"
|
177
|
+
request_response = response.body
|
178
|
+
rescue => exception
|
179
|
+
logging_file.error("CampaignError :: #{exception}")
|
180
|
+
raise "CampaignError for url: #{url}"
|
181
|
+
end
|
182
|
+
return request_response
|
183
|
+
end
|
184
|
+
|
185
|
+
def logging_file
|
186
|
+
@log_file ||= Logger.new("#{Rails.root}/log/campaign_errors.log")
|
187
|
+
end
|
188
|
+
|
189
|
+
def send_generic_sms(phone_number, template_name, send_time, sms_params)
|
190
|
+
post_args ={
|
191
|
+
"template_name" => template_name,
|
192
|
+
"sms_request[number]" => phone_number,
|
193
|
+
"sms_request[test_number]" => Housing.sms_test_number,
|
194
|
+
"send_time" => send_time,
|
195
|
+
"sms_request[params_json]" => sms_params.to_json
|
196
|
+
}
|
197
|
+
response = Net::HTTP.post_form( URI("#{Housing.sms_service_url}/v0/sms/send_deferred"), post_args)
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_shortened_url(url)
|
201
|
+
shortened_url = ""
|
202
|
+
uri = URI.parse("#{Housing.url_shortener}/shorten")
|
203
|
+
response = Net::HTTP.post_form(uri, {url: url})
|
204
|
+
if response.code.to_i == 200
|
205
|
+
shortened_url = Housing.short_url_domain + JSON.parse(response.body)["id"]
|
206
|
+
end
|
207
|
+
shortened_url
|
208
|
+
end
|
209
|
+
|
210
|
+
def upload_log_to_s3(file_path)
|
211
|
+
s3_key = Rails.root.to_s.split('/')[-1] + '/' + file_path.split('/')[-1]
|
212
|
+
s3_client.bucket(HousingMisc.bucket).object(s3_key).upload_file(file_path)
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def email_report_url
|
218
|
+
"#{Housing.mail_service_url}/api/v1/email-report?"
|
219
|
+
end
|
220
|
+
|
221
|
+
def sms_report_url
|
222
|
+
"#{Housing.sms_service_url}/v0/sms-report?"
|
223
|
+
end
|
224
|
+
|
225
|
+
def identifier
|
226
|
+
{
|
227
|
+
email: :emails,
|
228
|
+
sms: :phone_numbers
|
229
|
+
}
|
230
|
+
end
|
231
|
+
|
232
|
+
def s3_client
|
233
|
+
@s3_client ||= Aws::S3::Resource.new(
|
234
|
+
credentials: Aws::Credentials.new(HousingMisc.aws_access_token, HousingMisc.aws_access_secret),
|
235
|
+
region: HousingMisc.region
|
236
|
+
)
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module BooleanEvaluator
|
2
|
+
def self.eval(val)
|
3
|
+
return true if val == true || val.to_s =~ (/^(true|t|yes|y|1|on)$/i)
|
4
|
+
return false if val == false || val.nil? || val.to_s =~ (/^(false|f|no|n|0|off)$/i)
|
5
|
+
raise ArgumentError.new("invalid value for Boolean: \"#{val}\"")
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
# DiffChecker module is used to compare diff among
|
7
|
+
# application.yml application.yml.sample and default.rb.
|
8
|
+
module DiffChecker
|
9
|
+
|
10
|
+
def self.check_diff(deployment_file_path, sample_file_path, default_rb_file)
|
11
|
+
content = YAML.safe_load(File.read(deployment_file_path))
|
12
|
+
content_sample = YAML.safe_load(File.read(sample_file_path))
|
13
|
+
raise "#{sample_file_path} cant be empty" if (content_sample.nil? || content_sample.empty?)
|
14
|
+
missing_keys = content.nil? ? content_sample.keys : content_sample.keys - content.keys
|
15
|
+
unless missing_keys.empty?
|
16
|
+
raise "Some keys are missing in application.yml, but present in application.yml.sample. missing_keys : #{missing_keys}"
|
17
|
+
end
|
18
|
+
default_content_type1 = File.readlines(default_rb_file)
|
19
|
+
.map{ |line| line.match(/Figaro.env.[a-z0-9\_\-]+/).to_s.split('.')[2]}.reject{|line| line.nil?}
|
20
|
+
default_content_type2 = File.readlines(default_rb_file)
|
21
|
+
.map{ |line| line.match(/Figaro.env[^\.\s]+/).to_s.match(/[\"\'][a-z]*[^\]]+/)
|
22
|
+
.to_s.gsub!(/\A"|"\Z/, '')}.reject{|line| line.nil?}
|
23
|
+
default_file_keys = default_content_type1 + default_content_type2
|
24
|
+
missing_keys = content.nil? ? default_file_keys : default_file_keys - content.keys
|
25
|
+
unless missing_keys.empty?
|
26
|
+
raise "Some keys are missing in application.yml, but present in default.rb. missing_keys : #{missing_keys}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
METRIC_PREFIXES = {milli: 10**-3, centi: 10**-2, deci: 10**-1, '': 1, deka: 10**1, hecto: 10**2, kilo: 10**3}
|
2
|
+
IMPERIAL_UNITS = {inches: 12**-1, feet: 1, yards: 3, miles: 5280}
|
3
|
+
DIMENSIONS = {'': 1, square_: 2, cubic_: 3}
|
4
|
+
FEET_TO_METRES_CONVERSION = 0.3048
|
5
|
+
|
6
|
+
require 'active_support/inflector'
|
7
|
+
|
8
|
+
Numeric.class_eval do
|
9
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
10
|
+
METRIC_PREFIXES.keys.each do|prefix|
|
11
|
+
define_method("#{dimension_prefix}#{prefix}metres") do
|
12
|
+
distance_ob = MetricDistance.new(self, prefix.intern, dimension, dimension_prefix)
|
13
|
+
return distance_ob
|
14
|
+
end
|
15
|
+
alias_method "#{dimension_prefix}#{prefix}metre".intern, "#{dimension_prefix}#{prefix}metres".intern
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
20
|
+
IMPERIAL_UNITS.keys.each do |base_unit|
|
21
|
+
define_method("#{dimension_prefix}#{base_unit}") do
|
22
|
+
distance_ob = ImperialDistance.new(self, base_unit, dimension, dimension_prefix)
|
23
|
+
return distance_ob
|
24
|
+
end
|
25
|
+
alias_method "#{dimension_prefix}#{base_unit.to_s.singularize}".intern, "#{dimension_prefix}#{base_unit}".intern
|
26
|
+
end
|
27
|
+
alias_method "#{dimension_prefix}foot".intern, "#{dimension_prefix}feet".intern
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class DistanceUnit < Numeric
|
33
|
+
|
34
|
+
attr_reader :dimension, :dimension_prefix
|
35
|
+
attr_accessor :value, :base_unit
|
36
|
+
|
37
|
+
def initialize value, base_unit, dimension, dimension_prefix
|
38
|
+
@value = value
|
39
|
+
@base_unit = base_unit
|
40
|
+
@dimension = dimension
|
41
|
+
@dimension_prefix = dimension_prefix
|
42
|
+
end
|
43
|
+
|
44
|
+
def format_display
|
45
|
+
display_string = ''
|
46
|
+
base_distance = self.to_metres
|
47
|
+
if base_distance.value >= 1000
|
48
|
+
display_string = "#{base_distance.to_kilometres.value.round(1)} km"
|
49
|
+
else
|
50
|
+
display_string = "#{base_distance.value.round(0)} m"
|
51
|
+
end
|
52
|
+
return display_string
|
53
|
+
end
|
54
|
+
|
55
|
+
def unit
|
56
|
+
return "#{self.dimension_prefix}#{self.base_unit}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def value
|
60
|
+
return @value.to_f
|
61
|
+
end
|
62
|
+
|
63
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
64
|
+
METRIC_PREFIXES.each do |prefix, scale|
|
65
|
+
define_method("to_#{dimension_prefix}#{prefix}metres") do
|
66
|
+
raise "Dimension Error" if self.dimension != dimension
|
67
|
+
base_imperial = self.send("to_#{dimension_prefix}feet")
|
68
|
+
metric_value = base_imperial.value * (FEET_TO_METRES_CONVERSION ** dimension)
|
69
|
+
base_metric = MetricDistance.new(metric_value, '', dimension, dimension_prefix)
|
70
|
+
metric_distance = base_metric.send("to_#{dimension_prefix}#{prefix}metres")
|
71
|
+
end
|
72
|
+
alias_method "to_#{dimension_prefix}#{prefix}metre".intern, "to_#{dimension_prefix}#{prefix}metres".intern
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
77
|
+
IMPERIAL_UNITS.each do |base_unit, scale|
|
78
|
+
define_method("to_#{dimension_prefix}#{base_unit}") do
|
79
|
+
raise "Dimension Error" if self.dimension != dimension
|
80
|
+
base_metric = self.send("to_#{dimension_prefix}metres")
|
81
|
+
imperial_value = base_metric.value * (FEET_TO_METRES_CONVERSION ** -dimension)
|
82
|
+
base_imperial = ImperialDistance.new(imperial_value, :feet, dimension, dimension_prefix)
|
83
|
+
imperial_distance = base_imperial.send("to_#{dimension_prefix}#{base_unit}")
|
84
|
+
end
|
85
|
+
alias_method "to_#{dimension_prefix}#{base_unit.to_s.singularize}".intern, "to_#{dimension_prefix}#{base_unit}".intern
|
86
|
+
end
|
87
|
+
alias_method "to_#{dimension_prefix}foot".intern, "to_#{dimension_prefix}feet".intern
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
class MetricDistance < DistanceUnit
|
93
|
+
|
94
|
+
attr_accessor :metric_prefix
|
95
|
+
|
96
|
+
def initialize value, metric_prefix, dimension, dimension_prefix
|
97
|
+
@metric_prefix = metric_prefix
|
98
|
+
base_unit = "#{metric_prefix}metres".intern
|
99
|
+
super(value, base_unit, dimension, dimension_prefix)
|
100
|
+
end
|
101
|
+
|
102
|
+
def metric_prefix= metric_prefix
|
103
|
+
self.metric_prefix = metric_prefix
|
104
|
+
self.base_unit = "#{metric_prefix}metres".intern
|
105
|
+
end
|
106
|
+
|
107
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
108
|
+
METRIC_PREFIXES.each do |prefix, scale|
|
109
|
+
define_method("to_#{dimension_prefix}#{prefix}metres") do
|
110
|
+
raise "Dimension Error" if self.dimension != dimension
|
111
|
+
current_value = self.value
|
112
|
+
current_prefix = self.metric_prefix
|
113
|
+
new_value = current_value * ((METRIC_PREFIXES[current_prefix.intern] * (scale**-1)) ** dimension)
|
114
|
+
return MetricDistance.new(new_value, prefix.intern, dimension, dimension_prefix)
|
115
|
+
end
|
116
|
+
alias_method "to_#{dimension_prefix}#{prefix}metre".intern, "to_#{dimension_prefix}#{prefix}metres".intern
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
class ImperialDistance < DistanceUnit
|
123
|
+
|
124
|
+
DIMENSIONS.each do |dimension_prefix, dimension|
|
125
|
+
IMPERIAL_UNITS.each do |base_unit, scale|
|
126
|
+
define_method("to_#{dimension_prefix}#{base_unit}") do
|
127
|
+
raise "Dimension Error" if self.dimension != dimension
|
128
|
+
current_value = self.value
|
129
|
+
current_unit = self.base_unit
|
130
|
+
new_value = current_value * ((IMPERIAL_UNITS[current_unit.intern] * (scale**-1)) ** dimension)
|
131
|
+
return ImperialDistance.new(new_value, base_unit, dimension, dimension_prefix)
|
132
|
+
end
|
133
|
+
alias_method "to_#{dimension_prefix}#{base_unit.to_s.singularize}".intern, "to_#{dimension_prefix}#{base_unit}".intern
|
134
|
+
end
|
135
|
+
alias_method "to_#{dimension_prefix}foot".intern, "to_#{dimension_prefix}feet".intern
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
|