How to call Jekyll commands from Ruby - ruby-on-rails

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)

Related

Implementing Rest API into rails. Where to put the code?

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
)

Using url_helpers inside lib class in rails 4

In my rails app a lib class is used to recieve emails. The email reciever class parses the email and post it to a application controller using a HTTP response. The code for this is as shown :
uri = 'http://localhost:3000/incoming_mail'
body = {'from'=> from, 'to' => to, 'subject' => subject, 'message' => message}
response = Net::HTTP::post_form(URI.parse(uri), body)
The problem is I don't want to specify the complete URL. Is there any way to use 'incoming_mail_path' instead of 'localhost:3000/incoming_mail' ?
I tried to add :
include Rails.application.routes.url_helpers
But this is not working and gives the following error :
<class:EmailReceiver>': uninitialized constant EmailReceiver::Rails (NameError)
Can anyone please suggest a solution for this.
I am posting the entire class here(Updated the class with include statement):
require 'mail'
require 'net/https'
require 'net/http'
require 'uri'
class EmailReceiver
include Rails.application.routes.url_helpers
attr_accessor :url
def initialize
end
def submit(content)
mail = Mail.read_from_string(content)
body = mail.body.decoded
from = mail.from.first
to = mail.to.first
subject = mail.subject
if mail.multipart?
part = mail.parts.select { |p| p.content_type =~ /text\/plain/ }.first rescue nil
unless part.nil?
message = part.body.decoded
end
else
message = mail.decoded
end
unless message.nil?
uri = incoming_mail_url
#uri = 'http://localhost:3000/incoming_mail'
body = {'from'=> from, 'to' => to, 'subject' => subject, 'message' => message}
response = Net::HTTP::post_form(URI.parse(uri), body)
end
end
end
handler = EmailReceiver.new
handler.submit(STDIN.read)
Debugging:
After reading your comments, i figured out that you are running it as a ruby script which does't even recognize Rails.
Before figuring out how to include all the requirements to the file. I tried to run the file through rails environment (while the server was already running) by:
cat sample.email | bundle exec rails runner "eval(File.read 'lib/email_receiver.rb')"
I got the error for the incoming_mail_url:
Missing host to link to! Please provide the :host parameter,
set default_url_options[:host], or set :only_path to true (ArgumentError)
While the incoming_mail_path successfully executed /incoming_mail (which is not what you need).
Conclusion:
This means that what ever you do, as long as you don't run the file from the server (ex: initializers) then the host will never exist.
When you run this module from your server, its going to recogize the route through the url_helpers that you have included.
Alternative suggestion:
Griddler gem is a Rails engine that provides an endpoint for services that convert incoming emails to HTTP POST requests. It parses these POSTs and hands off a built email object to a class implemented by you.
To solve your immediate problem you may want to try to unscope your include statement. Right now it seems that interpreter thinks that "Rails" is a class within the EmailReceiver namespace. if you Add :: to the include statement it should reference "Rails" at the top level which is what you want.
It should work, but there maybe something else wrong in your setup that is causing you to have use this otherwise unnecessary workaround
Edit
What I meant by add "::" just to clarify
include ::Rails.application.routes.url_helpers

Initializing Gem-Specific variables information in Ruby On Rails

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(...

Rails3: HOWTO Override/Reopen a class within a Gem and the Rails initialization process

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
?

Cucumber default_url_options[:host] everytime "www.example.com" even if specified in environtemnts/test.rb

I specified the default_url_options in my environments/test.rb with
config.action_mailer.default_url_options = { :host => "www.xyu.at" }
This is quite ok and in my cucumber story, where i test registration of users,
the user-activation link gets generated right
invitation_activation_url(1)
=> "www.xyu.at/signup/1231hj23jh23"
But when I try to follow the link provided in the email with following code in features/steps/user_steps.rb (using email-rspec from http://github.com/bmabey/email-spec/tree/master):
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_url(1) + '" in the email'
end
Here the url gets created with the default-host:
invitation_activation_url(1)
=> "www.example.com/signup/1231hj23jh23"
Can anybody help me? I don't get what I'm doing wrong....
Thanks!
EDIT:
It seems to do with the method
current_url
but I dont know where it comes from..?
EDIT:
And I have the right environment specified in my features/support/env.rb
ENV["RAILS_ENV"] ||= "test"
EDIT:
My temporary solution is, what edbond said,
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
but I dont want to name the domain explicit this way
(i specified it already in my environments/test.rb file - that way it wouldnt be dry)
Use :host option in your url.
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
EDIT:
You can parse email body and get link
mail = YourMailer.deliveries.last
email_html = Nokogiri::HTML mail.body.to_s
approve_link = email_html.at_css("a")["href"]
I know its years since this was posted.. but I had this issue and took me hours to decipher until I got it figured out.
You should use instead
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_path(1) + '" in the email'
end
the _url generates the URL path; whilst the _path generates the URI path.
web_steps.rb uses the URI to determine the current_url which it uses to work out the host.
from http://api.rubyonrails.org/classes/ActionDispatch/Routing.html
Routes can be named by passing an :as option, allowing for easy
reference within your source as name_of_route_url for the full URL and
name_of_route_path for the URI path.
from web_steps.rb
Then /^(?:|I )should be on (.+)$/ do |page_name| | # end
current_path = URI.parse(current_url).path | #
if current_path.respond_to? :should | # collection do
current_path.should == path_to(page_name) | # get 'sold'
else | # end
assert_equal path_to(page_name), current_path | # end
end |
end
You say that you edited config/environments/test.rb. Are you sure that your Cucumber features are actually executing in the 'test' environment?
I recently added Cucumber to a project I'm working on, and it seems to set itself up to use a 'cucumber' environment by default.
In features/support/env.rb in my project there is this:
ENV["RAILS_ENV"] ||= "cucumber"
So if your project is similar, then you will need to customize config/environments/cucumber.rb as well.
I'm not terribly familiar with Cucumber, so I can't say with certainty where exactly you'll have to apply this fix. But the problem is that the default_url_options is not set in another place where you're trying to generate your url...
So my advice is to first find out in what context the faulty url is being generated. Before or after it, just output self.class. That's the class you'll have to monkey-patch. For the example, let's say 'ClassName' was printed out.
When you have that, in your config/environments/test.rb, just add the attribute accessor and then set it to what you want:
class ClassName
cattr_accessor :default_url_options
# or mattr_ if it's a module
end
and then set it the same way as your actionmailer
ClassName.default_url_options = { :host => "www.xyu.at" }
This whole process can be useful as well when you want to generate urls in models or in other esoteric places (then you'll also need to include ActionController::UrlWriter).
One solution (based on info here) is to have a step definition like
Given /^the domain "(.+?)"$/ do |domain|
host! domain
end
and use it like
Given the domain "www.foo.com"
in features.
With that, though, I ran into issues where redirects were not followed. I tried applying this patch but had no luck.
I ended up using a very simple workaround in Cucumber's env.rb file:
# There is a bug in internal_redirect? when used with explicit (non-example.com) domains.
# This is a simple workaround but may break if we start specing external redirects.
# https://webrat.lighthouseapp.com/projects/10503/tickets/140-current_url-should-be-fully-qualified
class Webrat::Session
alias internal_redirect? redirect?
end
As mentioned in the comment, this may of course break with external redirects, but we have none.

Resources