All,
I am trying to use cache to avoid request over http when the request is made for a specific area.
For example, your are in Los Angeles and 3 persons around you ( appx 1miles) make a google search about gas station nearby.
Instead of requesting each time, it's faster to allow people who are close to you and making the same search to get the result already cached.
In a previous method, I am caching using Redis and build a key using the parameters but to re-use it, you need an exact match as the key created was based on "gas_station__"
def set_cache key, val
return if blank?( key ) || blank?( val )
connection.set key, val
connection.expire key, EXPIRY
end
def get_cache key
connection.get( key ) if present?( key )
end
now I have used the Ruby Gem geocode api and when giving a coordinate and distance, it give me back a range of lat/lon
Geocoder::Calculations.bounding_box(location, distance)
and using the api below:
def isLocationInRange(location, area)
if location[0].between?(area[0], area[2]) && location[1].between?(area[1], area[3])
return true
end
false
end
I am able to know if the location gave in isLocationInRange is inside the "area"
the issue now is to connect this logic to Redis and use it to re-use the cache.
the best should be to generate a Redis key and look for it but it's not easy as I do not want to parse each key stored and check one by one the lat/lon params defined to see if that match an already known range of location.
Gemfile
gem 'redis'
Run bundle install
config/redis.yml
default: &default
host: localhost
port: 6379
db: 15
development:
<<: *default
test:
<<: *default
production:
<<: *default
config/initializers/redis.rb
redis_config = HashWithIndifferentAccess.new(Rails.application.config_for(:redis))
REDIS = Redis.new(host: redis_config[:host], port: redis_config[:port], db: redis_config[:db])
Now REDIS variable is available throughout your application since initializer files are loaded on app load.
REDIS.set("gas_station__#{some_location}", latitude_values here)
REDIS.get("gas_station_#{some_location}")
References: https://github.com/redis/redis-rb
Related
I have an application which calls an API and authenticates using a token. I need to store this token and refresh it every so often, so I am using Rails.cache.fetch to do so in a custom class for handling the API calls. This works great on my local dev machine, but in production it is erroring. I am running a Mac for my dev machine and production is on Ubuntu 18. Here is the code that raises the error:
def authenticate
Rails.cache.fetch(#token, expires_in: 2.hours.to_i) do
login_uri = #base_uri + "auth/login"
auth_response = HTTParty.post(login_uri, body: { username: ENV["API_USERNAME"], password: ENV["API_PASSWORD"] } )
#token = auth_response.parsed_response["token"]
end
end
Here is the error I am getting:
Errno::ENOTDIR (Not a directory # rb_file_s_rename -
(/var/www/myapp/releases/20190424134348/tmp/cache/.00020190424-1954-lgpvq5,
/var/www/myapp/releases/20190424134348/tmp/cache/001/000/)):
It looks like Rails is attempting to rename or move the cache file for some reason. Looking at the server the /001 directory is there, but the subdirectory /000 does not exist.
Not used to asking questions On stack, Apologies if the format makes it hard to respond.
Anyway, I'm trying to develop an app using Ruby on Rails with the Napster API. I am currently stuck on setting up the client object that will allow me to make meta data calls.
I am setting up the client in config/initializers as napster.rb. Here is my code
require 'napster'
client_hash = {
api_key: ENV["NAPSTER_API_KEY"],
api_secret: ENV["NAPSTER_API_SECRET"],
username: ENV["NAPSTER_USER"],
password: ENV["NAPSTER_PW"]
}
client = Napster::Client.new(client_hash)
client.access_token
client.authentication.access_token # => returns access_token
client.authentication.refresh_token
client.authentication.expires_in
Now whenever I try to run rails c in the console I get this error
config/initializers/napster.rb:14:in <main>: undefined method authentication' for #<Napster::Client:0x0000559185ef3cf8>
The ENV variables are stored in config/application.yml. I'm not sure what's going on, Here is the #<Napster::Client:0x0000559185ef3cf8>
#<Napster::Client:0x0000559185ef3cf8
#api_key=--Omitted--,
#api_secret=--Omitted--,
#username=--Omitted--, #password=--Omitted--,
#request=#<Napster::Request:0x0000559185ef3b40 #faraday=#
<Faraday::Connection:0x0000559185ef3a28 #parallel_manager=nil,
#headers={"Authorization"=>"Basic
Tm1KbVpHRXlOV0l0WVRJNFppMDBPVEkwTFdJM1l
6WXRPR1ExTTJSaE16WXpORE5tOllXWmlNek5oT0RFdE
5UaG1PUzAwWlRWakxXSXpNRFF0WVRJeU56bG1abUkzTmpJMA==", "User-
Agent"=>"Faraday v0.9.2"}, #params={}, #options=#
<Faraday::RequestOptions (empty)>, #ssl=#<Faraday::SSLOptions
verify=true>, #default_parallel_manager=nil, #builder=#
<Faraday::RackBuilder:0x0000559185ef3758 #handlers=
[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp], #app=#
<Faraday::Request::UrlEncoded:0x0000559185efd050 #app=#
<Faraday::Adapter::NetHttp:0x0000559185efd0c8 #app=#
<Proc:0x0000559185efd1b8#/home/leo/.rbenv/versions/
2.4.4/lib/ruby/gems/2.4.0/gems/faraday-
0.9.2/lib/faraday/rack_builder.rb:152 (lambda)>>>>, #url_prefix=#
<URI::HTTPS https://api.napster.com/>, #proxy=nil>>,
#access_token=--Omitted--,
#refresh_token=--Omitted--,
#expires_in=86399>
I omitted the api and access token stuff for obvious security reasons. Any thoughtful input is appreciated, thanks.
In my rails application I have 2 users with 2 different API tokens, I need to switch back to user2 token seamlessly on application startup?
config/tokens.yml
development: &development
# token belongs to user1
api_token: 'token1'
# token belongs to user2
# api_token: 'token2'
protocol: 'http'
host: 'localhost'
port: '3000'
How should I modify yaml file, so that selection between these users can be done easily.
You can do something like
api_users: user1,user2
api_tokens: token1,token2
Then when parsing the YAML, assuming the hash is stored in the tokens variable
def token_for_user(user)
users = tokens[:api_users].split(",")
tokens = tokens[:api_tokens].split(",")
tokens[users.index(user)]
end
token_for_user("user1")
# => token1
Implementing in rails and only running locally for the time being.
Using I have a google API server key for google places that is... lets say... "abc123"
When I use a url just to see with a url like:
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=abc123
it pulls information.
When I type env from mac terminal I have a value listed that is :
PLACES_API=abc123
when I run the code filling in the literal key:
#client = GooglePlaces::Client.new("abc123")
it works fine.
HOWEVER, when I try and pull this in using
#client = GooglePlaces::Client.new(ENV['PLACES_API'])
it errors out and when I try to puts ENV['PLACES_API'] it is blank.
I am assuming I am not using the env variable correctly, but now I want to know what I am doing wrong and how to use the environmental variable.
OPTION 1
If you are using ENV['PLACES_API'] in your code then before you start rails server you have to export the key. In your terminal run export PLACES_API="api key" and then start the server.
OPTION 2 (A better way to handle secret keys )
create a file gmap.yaml inside config directory with the following code
development:
secret: "api key"
test:
secret: "api key"
production:
secret: "api key"
Now create a new file gmap.rb inside config/initializars directory with the following code
PLACES_API = YAML.load_file("#{::Rails.root}/config/gmap.yml")[::Rails.env]
Now you can access the key with
#client = GooglePlaces::Client.new(PLACES_API['secret'])
In a Rails 3.2 app I have a facebook.yml, twitter.yml, etc containing relevant tokens for development, production, staging environments.
I then have methods such as
CONFIG = YAML.load_file(Rails.root.join("config/facebook.yml"))[Rails.env]
FB_APP_ID = CONFIG['app_id']
FB_SECRET = CONFIG['secret_key']
FB_NAMESPACE = CONFIG['name_space']
How and where should I set up these methods so that I can access FB_APP_ID from anywhere else in my app: controllers, models, views, etc?
Is this where modules come in?
Thanks
In the end I created a config.yml file
development:
facebook:
app_id: #####
secret: ###
twitter:
--
production:
---
etc
Added initializers/config.rb
CONFIG = YAML.load_file(Rails.root.join("config/config.yml"))[Rails.env]
module Facebook
APP_ID = FB_CONFIG["facebook"]['app_id']
SECRET = FB_CONFIG["facebook"]['secret']
end
module Twitter
--
end
Now I can access these values anywhere using
Facebook::APP_ID
Not sure if this is the best approach, but its working for now.