I am a beginner programmer. I recently built an application that uses the iex-ruby-client gem to pull stock quotes for me that I enter into a webpage form. It worked perfectly.
However, in early June, IEX changed their API so that you have to have a publishable token from the IEX cloud console. I got my publishable token from IEX cloud console.
The updated gem docs (https://github.com/dblock/iex-ruby-client) say that I have to "Configure" the application now. I simply don't know how or where I would implement the configuration code. Here is the suggested code from the gem documentation. I just don't know where to put it.
Configure IEX::Api.configure do |config|
config.publishable_token = 'token' # defaults to
ENV['IEX_API_PUBLISHABLE_TOKEN']
config.endpoint = 'https://sandbox.iexapis.com/v1' # defaults to
'https://cloud.iexapis.com/v1'
end
The documents also state, "You can also configure an instance of a client directly."
client = IEX::Api::Client.new(
publishable_token: 'token',
endpoint: 'https://sandbox.iexapis.com/v1'
)
I am adding extra code to clarify what I have done based on the response here. Here is my new config/initializers/iex-ruby-client.rb file (token info isn't the real one).
IEX::Api.configure do |config|
config.publishable_token = 'pk_3b38fsdadfsafjsdalfjdsakfjlda12f519'
config.endpoint = 'https://sandbox.iexapis.com/v1'
end
Here is the relevant method in the controller where I require the library:
def index
require 'iex-ruby-client'
if params[:id] == ""
#nothing = "You forgot to enter a symbol ;)."
elsif
if params[:id]
begin
#stock = IEX::Resources::Quote.get(params[:id])
#company = IEX::Resources::Company.get(params[:id])
rescue StandardError
#error = "That stock symbol doesn't seem to exist. Please enter
another symbol."
end
end
end
end
So I have created the config file and required the gem at the top of the method, but I am still getting an error. I'm sure there is some flaw in my implementation of this token requirement. If you have any additional suggestions, I welcome them. But if this is too much to ask on Stack Overflow, I understand. Thanks.
Well, you clearly have two choices:
use initializer by creating a config file(i.e: iex_client.rb) under the directory /config/initializers and add:
Configure IEX::Api.configure do |config|
config.publishable_token = 'token' # defaults to
ENV['IEX_API_PUBLISHABLE_TOKEN']
config.endpoint = 'https://sandbox.iexapis.com/v1' # defaults to
'https://cloud.iexapis.com/v1'
end
just use the client object wherever you want like this:
client = IEX::Api::Client.new(
publishable_token: 'token',
endpoint: 'https://sandbox.iexapis.com/v1'
)
You probably need to replace token with a correct one. You also need to make sure to require the library wherever you wanna use it.
After unsuccessfully attempting to configure the IEX-ruby-client gem (as described in my question here on stack overflow), I switched over to the stock_quote gem. That gem is built off of the same IEX API, and I had no problems configuring the app with a stock_quote.rb file saved inside config/initializers.
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.
rails version 5.2
I have a scenario where I need to access the public URL of Rails Active Storage with Amazon S3 storage to make a zip file with Sidekiq background job.
I am having difficulty getting the actual file URL. I have tried rails_blob_url but it gives me following
http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBZUk9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--9598613be650942d1ee4382a44dad679a80d2d3b/sample.pdf
How do I access the real file URL through Sidekiq?
storage.yml
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
development:
service: S3
access_key_id: 'xxxxx'
secret_access_key: 'xxxxx'
region: 'xxxxx'
bucket: 'xxxxx'
development.rb
config.active_storage.service = :development
I can access fine these on web interface but not within Sidekiq
Use ActiveStorage::Blob#service_url. For example, assuming a Post model with a single attached header_image:
#post.header_image.service_url
Update: Rails 6.1
Since Rails 6.1 ActiveStorage::Blob#service_url is deprecated in favor of ActiveStorage::Blob#url.
So, now
#post.header_image.url
is the way to go.
Sources:
Link to the corresponding PR.
Link to source.
If you need all your files public then you must make public your uploads:
In file config/storage.yml
amazon:
service: S3
access_key_id: zzz
secret_access_key: zzz
region: zzz
bucket: zzz
upload:
acl: "public-read"
In the code
attachment = ActiveStorage::Attachment.find(90)
attachment.blob.service_url # returns large URI
attachment.blob.service_url.sub(/\?.*/, '') # remove query params
It will return something like:
"https://foo.s3.amazonaws.com/bar/buz/2yoQMbt4NvY3gXb5x1YcHpRa"
It is public readable because of the config above.
My use case was to upload images to S3 which would have public access for ALL images in the bucket so a job could pick them up later, regardless of request origin or URL expiry. This is how I did it. (Rails 5.2.2)
First, the default for new S3 bucked is to keep everything private, so to defeat that there are 2 steps.
Add a wildcard bucket policy. In AWS S3 >> your bucket >> Permissions >> Bucket Policy
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
In your bucket >> Permissions >> Public Access Settings, be sure Block public and cross-account access if bucket has public policies is set to false
Now you can access anything in your S3 bucket with just the blob.key in the url. No more need for tokens with expiry.
Second, to generate that URL you can either use the solution by #Christian_Butzke: #post.header_image.service.send(:object_for, #post.header_image.key).public_url
However, know that object_for is a private method on service, and if called with public_send would give you an error. So, another alternative is to use the service_url per #George_Claghorn and just remove any params with a url&.split("?")&.first. As noted, this may fail in localhost with a host missing error.
Here is my solution or an uploadable "logo" stored on S3 and made public by default:
#/models/company.rb
has_one_attached :logo
def public_logo_url
if self.logo&.attachment
if Rails.env.development?
self.logo_url = Rails.application.routes.url_helpers.rails_blob_url(self.logo, only_path: true)
else
self.logo_url = self.logo&.service_url&.split("?")&.first
end
end
#set a default lazily
self.logo_url ||= ActionController::Base.helpers.asset_path("default_company_icon.png")
end
Enjoy ^_^
I had a few problems getting this working. Thought I'd document them for posterity.
In rails 6.0 use #post.header_image.service_url
In rails >= 6.1 use #post.header_image.url as #GeorgeClaghorn recommends.
I got this error:
error: uninitialized constant Analyzable
It's a weird bug in rails 6.0, which is fixed by placing this in config/application.rb
config.autoloader = :classic
I then see this error:
URI::InvalidURIError (bad URI(is not URI?): nil) Active Storage service_url
Fix it by simply adding this to your application_controller.rb
include ActiveStorage::SetCurrent
Now something like #post.image.blob.service_url will work as you expect =)
Using the service_url method combined with striping the params to get a public URL was good idea, thanks #genkilabs and #Aivils_Štoss!
There is however a potential scaling issue involved if you are using this method on large number of files, eg. if you are showing a list of records that have files attached. For each call to service_url you will in your logs see something like:
DEBUG -- : [8df9220c-e8c9-45b7-a1ee-b746e623ca1b] S3 Storage (1.4ms) Generated URL for file at key: ...
You can't eager load these calls either, so you can potentially have a large number of calls to S3 Storage to generate those URLs for each record you are showing.
I worked around it by creating a Presenter like this:
class FilePresenter < SimpleDelegator
def initialize(obj)
super
end
def public_url
return dev_url if Rails.env.development? || Rails.env.test? || assest_host.nil?
"#{assest_host}/#{key}"
end
private
def dev_url
Rails.application.routes.url_helpers.rails_blob_url(self, only_path: true)
end
def assest_host
#assest_host ||= ENV['ASSET_HOST']
end
end
Then I set an ENV variable ASSET_HOST with this:
https://<your_app_bucket>.s3.<your_region>.amazonaws.com
Then when I display the image or just the file link, I do this:
<%= link_to(image_tag(company.display_logo),
FilePresenter.new(company.logo).public_url, target: "_blank", rel:"noopener") %>
<a href=<%= FilePresenter.new(my_record.file).public_url %>
target="_blank" rel="noopener"><%= my_record.file.filename %></a>
Note, you still need to use display_logo for images so that it will access the variant if you are using them.
Also, this is all based on setting my AWS bucket public as per #genkilabs step #2 above, and adding the upload: acl: "public-read" setting to my 'config/storage.yml' as per #Aivils_Štoss!'s suggestion.
If anyone sees any issues or pitfalls with this approach, please let me know! This seemed to work great for me in allowing me to display a public URL but not needing to hit the S3 Storage for each record to generate that URL.
Also see public access in rails active storage. This was introduced in Rails 6.1.
Specify public: true in your app's config/storage.yml. Public services will always return a permanent URL.
Just write this if You are using minio or aws S3 to get attachment url on server.
#post.header_image&.service_url&.split("?")&.first
A bit late, but you can get the public URL also like this (assuming a Post model with a single attached header_image as in the example above):
#post.header_image.service.send(:object_for, #post.header_image.key).public_url
Update 2020-04-06
You need to make sure, that the document is saved with public ACLs (e.g. setting the default to public)
rails_blob_url is also usable. Requests will be served by rails, however, those requests will be probably quite slow, since a private URL needs to be generated on each request.
(FYI: outside the controller you can generate that URL also like this: Rails.application.routes.url_helpers.rails_blob_url(#post, only_path: true))
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'])