I'm trying to implement zoom.us Rest API into my rails app (example: https://github.com/mllocs/zoomus and https://support.zoom.us/hc/en-us/sections/200305463-API ), but i have no idea where to put this part of the code. Or how to call it. (i know that i'm suppose to change the xxx to my credentials at least.) Is there a specific ruby gem that I should be installing first prior to working with a Rest API?
require 'zoomus'
Zoomus.configure do |c|
c.api_key = 'xxx'
c.api_secret = 'xxx'
end
zoomus_client = Zoomus.new
user_list = zoomus_client.user_list
user_list['users'].each do |user|
user_id = user['id']
puts zoomus_client.meeting_list(:host_id => user_id)
end
begin
user_list = zoomus_client.user_list!
rescue Zoomus::Error => exception
puts 'Something went wrong'
end
I suggest you that all configuration of zoom put into your config->environments->development.rb or productin.rb files like:
Zoomus.configure do |c|
c.api_key = 'xxx'
c.api_secret = 'xxx'
end
and put your other code into files where you want to access or use (api, controller,method,class)
zoomus_client = Zoomus.new
user_list = zoomus_client.user_list
user_list['users'].each do |user|
user_id = user['id']
puts zoomus_client.meeting_list(:host_id => user_id)
end
begin
user_list = zoomus_client.user_list!
rescue Zoomus::Error => exception
puts 'Something went wrong'
end
Hope this will help you.
If you're using Bundler, you don't need to first line. When you add gem 'zoomus' to your Gemfile, bundle, then run Rails, those gems will be required automatically unless explicitly told otherwise (using require: false option)
Normally the configure block goes into config/initializers/zoomus.rb. Any file inside of config/initializers/ will be loaded during the boot process.
The rest of the code really depends on when and where you use this service. My recommendation would be to wrap these calls into plain objects that sit in app/services. Call out to those classes wherever you need to, whether it's an ActiveJob worker, controller, or model.
Really it's impossible to answer your question without context, and even with context you will get different answers from different people. Where to place code has been a hot topic of debate in Rails for many years.
This stuff:
Zoomus.configure do |c|
c.api_key = 'xxx'
c.api_secret = 'xxx'
end
ZoomusClient = Zoomus.new
should be in a file config/initializers/zoomus.rb.
Note that I changed zoomus_client to ZoomusClient so that it is a constant. This is so it will be available in the other files.
Make sure you're not checking in the api credentials into source control, or they might get published to Github. Use dotenv or something for this.
The rest of the code can be called by any method in your rails app.
puts ZoomusClient.meeting_list(
host_id: ZoomusClient.user_list['users'][0].id
)
Related
I have a rails 4.2 multi-tenant app using the Apartment gem which has been awesome.
Each company has their own subdomain. I'm using a custom "elevator" which looks at the full request host to determine which "Tenant" should be loaded.
When I create a new company I have an after_create hook to create the new tenant with the proper request host.
This always seems to require a restart of the server both in development and production otherwise I get a Tenant Not Found error.
It's using sqlite in development and postgres in production.
Do I really have to restart the server each time I create a new tenant? Is there an automated way to do this? Maybe just reloading the initializer will work, but I'm not sure how to do that/if that's possible?
I have been messing around with this for a month and haven't been able to find a solution that works. Please help!
initializers/apartment.rb
require 'apartment/elevators/host_hash'
config.tenant_names = lambda { Company.pluck :request_host }
Rails.application.config.middleware.use 'Apartment::Elevators::HostHash', Company.full_hosts_hash
initializers/host_hash.rb
require 'apartment/elevators/generic'
module Apartment
module Elevators
class HostHash < Generic
def initialize(app, hash = {}, processor = nil)
super app, processor
#hash = hash
end
def parse_tenant_name(request)
if request.host.split('.').first == "www"
nil
else
raise TenantNotFound,
"Cannot find tenant for host #{request.host}" unless #hash.has_key?(request.host)
#hash[request.host]
end
end
end
end
end
Company Model
after_create :create_tenant
def self.full_hosts_hash
Company.all.inject(Hash.new) do |hash, company|
hash[company.request_host] = company.request_host
hash
end
end
private
def create_tenant
Apartment::Tenant.create(request_host)
end
What ended up working
I changed the elevator configuration to get away from the HostHash one that's in the apartment gem and used a completely custom one. Mostly based off of an issue on the apartment gem github: https://github.com/influitive/apartment/issues/280
initializers/apartment.rb
Rails.application.config.middleware.use 'BaseSite::BaseElevator'
app/middleware/base_site.rb
require 'apartment/elevators/generic'
module BaseSite
class BaseElevator < Apartment::Elevators::Generic
def parse_tenant_name(request)
company = Company.find_by_request_host(request.host)
return company.request_host unless company.nil?
fail StandardError, "No website found at #{request.host} not found"
end
end
end
I think the problem could be that your host_hash.rb lives in the initializers directory. Shouldn't it be in a folder called "middleware"?, as per the Apartment gem ReadME you referenced in your comment. In that example they used app/middleware/my_custom_elevator.rb. Perhaps yours might look like app/middleware/host_hash.rb?
Right now the file is in initializers, so it's loading from there. But your apartment.rb references it by Rails.application.config.middleware.use. Just a hunch but in addition to loading it initially, it may be looking for it in a nonexistent middleware folder. I'd go ahead and create app/middleware, put the file in there instead, and see what happens. Not sure but you might need to alter require paths too.
Let us know if that helps.
I am working right now on a Rails 4.0 application (using Ruby 2.0.0).
I would like to interact with Jenkins using jenkins_api_client gem, from multiple pages of my Rails application.
This gem generally using a #client parameter, which is initialized to contain the credentials and other information of the Jenkins server.
This parameter in initialized using something like this:
#client = JenkinsApi::Client.new(:server_ip => '0.0.0.0',
:username => 'somename', :password => 'secret password')
Once initialized, I would like to access this parameter and run multiple sub-routines on it.
This initialization takes time, and I really want to avoid doing this process every time one of the clients would like to use this gem functionality, such as:
# Get a filtered list of jobs from the server
jobs_to_filter = "^test_job.*"
jobs = #client.job.list(jobs_to_filter)
So, I hope to do this only once- when the rails server starts.
I would like to use this parameter from multiple pages of my app, possibly with threaded solution further down the road (not critical at the moment).
Can anyone recommend how to achieve this?
I'd appreciate an answer which is consistent with Rails convention.
Thanks a lot!
as example you could create something like that:
module JenkinsApi
class Client
class << self
attr_reader :instance, :config
def configure(&block)
#config = OpenStruct.new
block.call #config
end
def instance
#instance ||= JenkinsApi::Client.new #config
end
end
end
end
which allow you write in initializer:
JenkinsApi::Client.configure do |config|
config.server_ip = '0.0.0.0'
config.username = 'somename'
config.password = 'secret password'
end
and then use it like: JenkinsApi::Client.instance.job.list(...
I have a Rails app which creates/builds some Jekyll sites on the same server. Right now, I'm calling Jekyll commands with backticks like this:
def build_jekyll
result = `jekyll build -s /some/source/path -d /some/dest/path`
end
This works fine but feels a little un-ruby like. If the jekyll gem is in my Rails Gemfile, is there a way I can build a jekyll site using ruby?
(from the docs, it looks like I would call Jekyll::Commands::Build.build but I'm not sure how to initialize the site parameter).
TL;DR
require 'jekyll'
conf = Jekyll.configuration({
'source' => 'path/to/source',
'destination' => 'path/to/destination'
})
Jekyll::Site.new(conf).process
But how did you find out?
I figured this out by looking at the source code. When you run jekyll build, you enter into the source file bin/jekyll. The interesting part here is
command :build do |c|
# ommitted
c.action do |args, options|
options = normalize_options(options.__hash__)
options = Jekyll.configuration(options)
Jekyll::Commands::Build.process(options)
end
end
Hm, looks like the actual work is done in Jekyll::Commands::Build.process, so let's take a look into that method in lib/jekyll/commands/build.rb:
def self.process(options)
site = Jekyll::Site.new(options)
self.build(site, options)
# other stuff
end
Again, the actual magic happens somewhere else, namely in Jekyll::Commands::Build.build, also in lib/jekyll/commands/build.rb
def self.build(site, options)
# some logging going on here
self.process_site(site)
end
This in turn calls a class method called process_site, which comes from the superclass Jekyll::Command defined in lib/jekyll/command.rb
def self.process_site(site)
site.process
rescue Jekyll::FatalException => e
# some error handling
end
So we actually want to call process on a Jekyll::Site. One thing we have yet to find out is how to specify options for the Jekyll::Site instance. Let's take a closer look at lib/jekyll/site.rb
def initialize(config)
# more options ...
self.source = File.expand_path(config['source'])
self.dest = File.expand_path(config['destination'])
# more options ...
end
So apparently we need to supply a hash with the 'source' and 'destination' keys pointing to the desired directories. The rest of the configuration will be generated by Jekyll with the Jekyll.configuration method which we saw earlier in bin/jekyll. That's about it. Now, the only thing left to do is putting the pieces together ;-)
Updating this because it looks like the syntax changed, this works now
require "jekyll"
options = {
"source" => './',
"destination" => './_site',
"watch" => true,
"verbose" => true
}
Jekyll::Commands::Build.process(options)
I am currently using the mixpanel_client gem to access the mixpanel API. I would like to be able to do this in one place:
config = {'api_key' => 'changeme', 'api_secret' => 'changeme'}
client = Mixpanel::Client.new(config)
and then access it anywhere throughout the app. Is there an idiomatic (or framework-matic) way to go about this? It seems like doing this everytime I want to make a request is a waste of resources and not very DRY to boot.
Thanks!
There are a few ways to do it, create an initializer, under initializer folder, so that it will be loaded once after rails is loaded, then
config = {'api_key' => 'changeme', 'api_secret' => 'changeme'}
CLIENT = Mixpanel::Client.new(config)
Then the CLIENT constant will be available anywhere in your app.
Else you can create a class
class MixPanelClient
cattr_accessor: client
def self.client
client ||= begin
config = {'api_key' => 'changeme', 'api_secret' => 'changeme'}
Mixpanel::Client.new(config)
end
end
end
MixPanelClient.client would only create that client once.
My question is very similar to this one: How do I add a method to a ruby gem without editing the gem source?. However, this question is almost a year old and the solution that was chosen isn't the cleanest, not to me at least.
The person who provided the answer offered 3 suggestions. The first suggestion was chosen as the answer, but I would really like to figure out how to do it the second way.
I need to override an instance method of a class that is defined by a Gem. More specifically, it is the SessionSerializer class in 1.1.2 Devise. The issue is that Devise doesn't respect non-standard primary key names. It always uses id. You can see that in warden_compat.rb on Line 30, it uses the following to find a model by it's ID:
klass.constantize.find(:first, :conditions => { :id => id })
In my case, the name of my id column is application_user_id, so it is obvious that this won't work. Devise has fixed this issue in 1.1.3, however, I cannot use 1.1.3 because the Devise LDAP Authenticatable plugin does not support 1.1.3.
So here's what I've done instead. I should mention first that I tested this fix by editing the Gem source directly, so now I simply want to move it into my project.
Created a session_serializer.rb file in lib/warden/ (i.e., lib/warden/session_serializer.rb), reopened the Warden::SessionSerializer class, and redefined the deserialize method.
Modified application.rb to include lib/ in config.autoload_paths
config.autoload_paths += ["#{config.root}/lib"]
However, this doesn't seem to do the trick. It is still using the same code that is defined in the Gem source. So I have couple questions that I hope that can be answered:
Questions
What am I doing wrong here?
Does Rails load files of the paths defined in config.autoload_paths before Gems, or is it the other way around?
Thanks for the help in advance!
lib/warden/session_serializer.rb
module Warden
class SessionSerializer
def deserialize(keys)
klass, id = keys
if klass.is_a?(Class)
raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
"you can fix it by changing one character in your cookie secret, forcing all previous " <<
"cookies to expire, or cleaning up your database sessions if you are using a db store."
end
# NOTE: Original line code. Notice that it uses an :id symbol. It doesn't respect the primary key that explicity defined in the model
# klass.constantize.find(:first, :conditions => { :id => id })
# NOTE: THIS IS THE FIX
klass.constantize.find(:first, :conditions => { :application_user_id => id })
rescue NameError => e
if e.message =~ /uninitialized constant/
Rails.logger.debug "Trying to deserialize invalid class #{klass}"
nil
else
raise
end
end
end
end
I would create a file called warden.rb in initializers directory and put the monkey patch code inside the file. I use this technique often in my projects to patch a gem.
To put the patch under the lib directory, do the following:
config.autoload_paths += ["#{config.root}/lib/warden"]
PS: I know you have tried this, but it looks like your path is not correct.
PPS To understand the Rails 2.3 load sequence refer to this code.
Have you read:
http://guides.rubyonrails.org/configuring.html
?