In my Rails 6 app I'm trying to implement functionality which is responsible for fetching the file from a different Github repo. The code should try to fetch file name.json or name.master.json from GitHub (because the file could be either master json or standard json).
Code below:
#lib/github_client.rb
module GithubClient
extend self
def fetch_file(file_name)
if (response = translate(file_name)).success?
response
else
translate(file_name, master: true)
end
end
private
def client
#client ||= Octokit::Client.new(access_token: Rails.application.credentials.github[:access_token])
end
def translate(file_name, master: false)
return client.contents('user/my-repo-name', path: "#{file_name}.master.json") if master == 'true'
client.contents('user/my-repo-name', path: "#{file_name}.json")
end
end
Line if (response = translate(file_name)).success?does not work because if there is no file e.g. book.master.json it will return:
Octokit::NotFound (GET https://api.github.com/repos/user/my-repo-name/book.json: 404 - Not Found // See: https://docs.github.com/rest/reference/repos#get-repository-content)
How can I check the status of this response so that it will search for another file if necessary?
I'm not sure if there's an #exists? method or similar, which would probably be a better solution (I can't see such a method in the documentation!...), but you could always just rescue the exception in order to handle an expected failure gracefully. For example:
client.contents('user/my-repo-name', path: "#{file_name}.json")
rescue Octokit::NotFound
client.contents('user/my-repo-name', path: "#{file_name}.master.json")
Note that your current code is also slightly wrong since you're checking if master == 'true' -- but master is a boolean value (true / false), not a String ("true").
true != "true"
Related
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'm trying to get my rails controller set up to download a file from google drive following the code at - https://developers.google.com/drive/v2/reference/files/get
I have the following rails code
def download_file(client, file)
if file.download_url
result = client.execute(:uri => file.download_url)
if result.status == 200
return result.body
else
puts "An error occurred: #{result.data['error']['message']}"
return nil
end
end
end
def attachExternalResume
# read remote url to file
file_id = params[:fileId]
client = Google::APIClient.new
drive = client.discovered_api('drive', 'v2')
result = client.execute(
:api_method => drive.files.get,
:parameters => { 'fileId' => file_id}
)
if (result.status == 200)
p download_file client, result.data
end
end
This is being called from my javascript front end using the google picker. The user authorizes my app through the google picker and selects a file which results in my angular javascript posting the file id to my rails method. In the rails code I'm getting the following error - Missing access token.
It seems like even though the user has authorized the app on the front end, that authorization isn't making its way through to the rails side. Anyone know how I can get the authorization all the way through the process?
"I'm getting the following error - Missing access token." probably means that you're missing an access token. In the code snippet you posted, I can't see anywhere where you request an access token, so that makes it even more likely. I don't know enough about the Google Ruby library to give you the code you're missing, but look for something that takes scope(s) and application/client ID as arguments.
I'm running an application that uses mechanize to fetch some data every so often from an RSS feed.
It runs as a heroku worker and after a day or so I'm receiving the following error:
Errno::EMFILE: Too many open files - socket(2)
I wasn't able to find a "close" method within mechanize, is there anything special I need to be doing in order to close out my browser sessions?
Here is how I create the browser + read information:
def mechanize_browser
#mechanize_browser ||= begin
agent = Mechanize.new
agent.redirect_ok = true
agent.request_headers = {
'Accept-Encoding' => "gzip,deflate,sdch",
'Accept-Language' => "en-US,en;q=0.8",
}
agent
end
end
And actually fetching information:
response = mechanize_browser.get(url)
And then closing after the response:
def close_mechanize_browser
#mechanize_browser = nil
end
Thanks in advance!
Since you manually can't close each instance of Mechanize, you can try invoking Mechanize as a block. According to the docs:
After the block executes, the instance is cleaned up. This includes closing all open connections.
So, rather than abstracting Mechanize.new into a custom function, try running Mechanize via the start class method, which should automatically close all your connections upon completion of the request:
Mechanize.start do |m|
m.get("http://example.com")
end
I ran into this same issue. The Mechanize start example by #zeantsoi is the answer that I ended up following, but there is also a Mechanize.shutdown method if you want to do this manually without their block.
There is also an option that you can add a lambda on post_connect_hooks
Mechanize.new.post_connect_looks << lambda {|agent, url, response, response_body| agent.shutdown }
I'm using the following code to perform a request on the server from within a rake task:
app = ActionDispatch::Integration::Session.new(Rails.application)
app.host!('localhost:3000')
app.get(path)
This works well.
However, if I call app.get(path) again with the same path, the request is not repeated and the previous result is returned.
Is there a way I can force app.get to repeat the call?
Try to reset the session:
app.reset!
Here is how it works when reset,
def reset!
#https = false
#controller = #request = #response = nil
#_mock_session = nil
#request_count = 0
#url_options = nil
self.host = DEFAULT_HOST
self.remote_addr = "127.0.0.1"
self.accept = "text/xml,application/xml,application/xhtml+xml," +
"text/html;q=0.9,text/plain;q=0.8,image/png," +
"*/*;q=0.5"
unless defined? #named_routes_configured
# the helpers are made protected by default--we make them public for
# easier access during testing and troubleshooting.
#named_routes_configured = true
end
end
Otherwise it will just re-use the last response:
# this is a private method in Session, it will called every time you call `get/post, etc`
def process
......
#request_count += 1
#request = ActionDispatch::Request.new(session.last_request.env)
response = _mock_session.last_response
#response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
#html_document = nil
....
end
Good luck!
I've worked out what's going on.
Basically the observation "the request is not repeated" is Rails' own caching in action. Which makes sense, app.get is treated as any other request, if caching is enabled, the cache is returned, and if it's not, it will repeat (as #henrikhodne claimed). This explains why a puts in the cached controller will not output the second time.
To verify, add a puts in 2 controller methods, but only set the expires_in in the second. The first one will repeat the output, the second will not.
The way to force the request to repeat is to bust the cache by modifying the URL e.g.
app.get("/") becomes app.get("/?r=123456") as you would if using HTTP. It all seems obvious in hindsight, basically app.get is treated exactly as a client request, and all the same rules apply.
I'm querying the Google API to list all files in the drive using the Google API official gem for ruby. I'm using the example given in the Google developers page - https://developers.google.com/drive/v2/reference/files/list
The first request I made returns in the "items" an array of ruby "Hashes". The next requests return in the "items" an array of either "Google::APIClient::Schema::Drive::V2::File" or "Google::APIClient::Schema::Drive::V2::ParentReference" (the reason behind each type also buggs me).
Does anyone know why this happens? At the reference page of "files.list" none is said about changing the type of the results.
def self.retrieve_all_files(client)
drive = client.discovered_api('drive', 'v2')
result = Array.new
page_token = nil
begin
parameters = {}
if page_token.to_s != ''
parameters['pageToken'] = page_token
end
api_result = client.execute(
:api_method => drive.files.list,
:parameters => parameters)
if api_result.status == 200
files = api_result.data
result.concat(files.items)
page_token = files.next_page_token
else
puts "An error occurred: #{result.data['error']['message']}"
page_token = nil
end
end while page_token.to_s != ''
result
end
EDIT:
I couldn't solve the problem yet, but I manage to understand it better:
When the first request to the API is made, after the authorization is granted by the user, the "file.list" returns an array of Hashes at "Items" attribute of the File resource. Each of this Hashes is like a File resource, with all the attributes of the File, the difference is just in the type of the access. For example: the title of the file can be accessed like this "File['title']".
After the first request is made, all the subsequent requests return an array of File resources, that can be accessed like this "File.title".
FYI, this was a bug in the client lib. Using the latest version should fix it.