Get Jekyll Configuration Inside Plugin - ruby

I'd like to make changes to the Jekyll Only First Paragraph plugin to make the generation of a 'read more ' link a configurable option.
To do this I'd need to be able to access the Jekyll site config inside the plugin's AssetFilter. With the configuration available I can make the change. I don't know how to make the site configuration available to the plugin.
The code below demonstrates where I'd like site.config available:
require 'nokogiri'
module Jekyll
module AssetFilter
def only_first_p(post)
# site.config needs to be available here to modify the output based on the configuration
output = "<p>"
output << Nokogiri::HTML(post["content"]).at_css("p").inner_html
output << %{</p><a class="readmore" href="#{post["url"]}">Read more</a>}
output
end
end
end
Liquid::Template.register_filter(Jekyll::AssetFilter)
Can this be achieved?

Overview
You can access Jekyll config options in plugins with:
Jekyll.configuration({})['KEY_NAME']
If the config key contains nested levels, the format is:
Jekyll.configuration({})['KEY_LEVEL_1']['KEY_LEVEL_2']
Example
If a _config.yml contains:
testvar: new value
custom_root:
second_level: sub level data
A basic example that simply outputs those values would look like:
require 'nokogiri'
module Jekyll
module AssetFilter
def only_first_p(post)
#c_value = Jekyll.configuration({})['testvar']
#c_value_nested = Jekyll.configuration({})['custom_root']['second_level']
output = "<p>"
### Confirm you got the config values
output << "<br />"
output << "c_value: " + #c_value + "<br />"
output << "c_value_nested: " + #c_value_nested + "<br />"
output << "<br />"
###
output << Nokogiri::HTML(post["content"]).at_css("p").inner_html
output << %{</p><a class="readmore" href="#{post["url"]}">Read more</a>}
output
end
end
end
Liquid::Template.register_filter(Jekyll::AssetFilter)
Of course, you would want to put checks in that verify that the config key/values are defined before trying to use them. That's left as an exercise for the reader.
Another Possible Option
The "Liquid filters" section of the Jekyll Plugins Wiki Page contains the following:
In Jekyll you can access the site object through registers. As an example, you can access the global configuration (_config.yml) like this: #context.registers[:site].config['cdn'].
I haven't spent the time to get that to work, but it might be worth checking out as well.

Jekyll.configuration({})['KEY_NAME'] will break the --config command line option because it will always load the configurations from the _config.yml file. Another bad side effect is that it will read the _config.yml file again.
context.registers[:site].config['KEY_NAME'] is the correct answer because it will get the key from the configurations already loaded by Jekyll.

If you are working with Generators (which are also plugins), it is possible to get the configuration like this:
class MyPlugin < Jekyll::Generator
def generate(site)
puts site.config["max_posts"] # max_posts as defined in _config.yml
You'll get the site as an argument, and the .config is accessible as an hash.

Related

How to use YAML files with Vagrant?

I'm trying to improve my YAML file for my Vagrant project. According to this post, if I have something like this:
en:
site_name: "Site Name"
static_pages:
company:
description: ! '%{site_name} is an online system'
I should be able to print "Site Name is an online system", but I don't know how to use it inside my Vagrantfile.
I tried so far but I couldn't print it out properly, just this:
%{site_name} is an online system
This is how I'm using it:
require 'yaml'
set = YAML.load_file(ENV['DEVOPS_HOME'] + '/vagrant/server/settings.yml')
puts set['en']['static_pages']['company']['description']
as they say in the answer of the post
and then call in the appropriate view with the site name as a parameter
so you don't get directly after you load the yaml file the expected string, you need to match the parameter. one way you could work this in your Vagrantfile is
require 'yaml'
set = YAML.load_file(ENV['DEVOPS_HOME'] + '/vagrant/server/settings.yml')
str = set['en']['static_pages']['company']['description']
arg = {:site_name=> set['en']['site_name']}
p str % arg
will print out "Site Name is an online system"
The other way would be to use the answer from mudasobwa which is also detailed in the original post you reference
You might want to use YAML aliases to achieve this functionality:
en:
site_name: &site_name "Site Name" # declaring alias
static_pages:
company:
description:
- *site_name # reference
- "is an online system"
And later on:
puts set['en']['static_pages']['company']['description'].join(' ')

how to reload external file every time?

I have a ruby script where my "config" is in an extra file. It's called ftp_config.rb. Then I have filetrack.rb that downloads files from a ftp server - what files/directories is specified in ftp_config.rb. And finally I got rufus_download.rb that is calling a function from filetrack.rb every day so I get all new files from the server.
Everything works fine just I want to know how to make it so when I edit ftp_config.rb the changes are picked up by the script without the need to restart rufus_download.rb.
currenly
rufus_download.rb contains require_relative 'filetrack'
filetrack.rb contains require_relative 'ftp_config'
Right now if I add new files to be downloaded to ftp_config.rb I need to restart rufus
require_relative returns false if the file you have requested is already loaded to your ruby script and returns true if you haven't
If you want changes to be loaded directly you need to load files
load 'path/to/ftp_config'
every time your script executes it will load / reload the script
EDIT:
you can load by expanding path of the current ruby script:
load ::File.expand_path('../ftp_config.rb', __FILE__)
Assuming that files are in the same folder
EDITEND
hope that helps
You need a gem that monitors filechanges like "sinatra/reloader" for Sinatra and eg filewatcher or listen for desktop apps. After detecting an update you load the script, not require, that only loads a script once.
Here an example of filewatcher.
STDOUT.sync = true
FileWatcher.new(['c:/test/scans']).watch() do |filename, event|
puts filename
if(event == :changed)
puts "File updated: " + filename
end
if(event == :delete)
puts "File deleted: " + filename
end
if(event == :new)
puts "Added file: " + filename
end
end

How to add a page to RDoc?

Noob here trying to figure out the simple task of adding a page to my RDoc documentation. If I do rake doc:app and view a page, then there is a sidebar that says "Pages" with README_FOR_APP listed. All I want to do is create a page named "Cheatsheet" that will appear under "Pages." How do I create a documentation page and tell RDoc to include the new page?
IMO it's a bit convoluted. Here's the references I used:
http://rake.rubyforge.org/classes/Rake/RDocTask.html
How to Rename or Move Rails's README_FOR_APP
Nutshell version:
Take the "app" task from your rails library directory, for example I tested using:
~/.rvm/gems/ruby-1.9.2-p0#foo/gems/railties-3.0.9/lib/rails/tasks/documentation.rake
The part you need looks (approximately) like this:
namespace :doc do
def gem_path(gem_name)
path = $LOAD_PATH.grep(/#{gem_name}[\w.-]*\/lib$/).first
yield File.dirname(path) if path
end
RDocTaskWithoutDescriptions.new("app2") { |rdoc|
rdoc.rdoc_dir = 'doc/app'
rdoc.template = ENV['template'] if ENV['template']
rdoc.title = ENV['title'] || "Rails Application Documentation"
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.options << '--charset' << 'utf-8'
rdoc.rdoc_files.include('doc/*_FOR_APP') # Modified this line
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
end
Put this in your app's lib/tasks directory in a .rake file, e.g., doc2.rake.
Update the task name if you want to keep the original, e.g., "app2".
Update the "doc/README_FOR_APP" string to a wider pattern, or add new file(s) to process.
Create doc/Cheatsheet_FOR_APP and the new rake doc:app2 task will pick it up.

How can I make Jekyll use a layout without specifying it?

In order to keep some of my Jekyll sites simple, I'm always using the same layout. That is to say, I'm always writing something like. . .
---
layout: default
title: Here's my Title
---
. . . as the YAML Front Matter at the top of my pages.
What I'd rather write, however is only. . .
---
title: Here's my Title
---
. . . and have Jekyll assume that it should use a certain layout, as if I had explicitly written "layout: default" (or whatever), as above.
I don't see a way to specify this behavior in _config.yml. Maybe I could write a Jekyll plugin that would allow this. . . any ideas?
This can be done using Frontmatter defaults:
defaults:
-
scope:
path: "" # empty string for all files
values:
layout: "default"
This setting is available since Jekyll Version 2.0.0.
Shorter and with no monkey-patching:
# _plugins/implicit_layout.rb
module ImplicitLayout
def read_yaml(*args)
super
self.data['layout'] ||= 'post'
end
end
Jekyll::Post.send(:include, ImplicitLayout)
Caveat: GH Pages won't run your plugins.
Here's a Jekyll plugin you can drop in as _plugins/implicit-layout.rb, for example:
# By specifying an implicit layout here, you do not need to
# write, for example "layout: default" at the top of each of
# your posts and pages (i.e. in the "YAML Front Matter")
#
# Please note that you should only use this plugin if you
# plan to use the same layout for all your posts and pages.
# To use the plugin, just drop this file in _plugins, calling it
# _plugins/implicit-layout.rb, for example
IMPLICIT_LAYOUT = 'default'
module Jekyll
module Convertible
def read_yaml(base, name)
self.content = File.read(File.join(base, name))
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = $POSTMATCH
begin
self.data = YAML.load($1)
self.data["layout"] = IMPLICIT_LAYOUT
rescue => e
puts "YAML Exception reading #{name}: #{e.message}"
end
end
self.data ||= {}
end
end
end
From hanging out on #jekyll on freenode, I'm given to understand this is a monkey patch.
As Alan W. Smith commented, being able to put "layout: default" in _config.yml would be a nice improvement to this plugin.
Ideally (from my perspective), this functionality could be incorporated in Jekyll itself so a plugin wouldn't be necessary.
By default, you can't do this. Jekyll needs the YAML to specify layout so it knows where to drop it in at.

command-line ruby scripts accessing a libs folder

I'm trying to create an application that will primarily consist of ruby scripts that will be run from the command-line (cron, specifically). I want to have a libs folder, so I can put encapsulated, reusable classes/modules in there, and be able to access them from any script.
I want to be able to put my scripts into a "bin" folder.
What is the best way to give them access to the libs folder? I know I can add to the load path via command-line argument, or at the top of each command-line script. In PHP, it sometimes made more sense to create a custom .ini file and point the cli to the ini file, so you got them all in one pop.
Anything similar for ruby? Based on your experience, what's the best way to go here?
At the top of each bin/executable, you can put this at the top
#!/usr/bin/env ruby
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')
require 'libfile'
[etc.]
Were you looking for something different?
If you turn your application into a Ruby gem and install the gem on your system, you don't even need to put this stuff at the top. The require statement would suffice in that case.
Sean,
There is no way to not have to require a library, that I know of. I guess if you want to personalize your Ruby so much you could "roll your own" using eval.
The script below basically works as the interpreter. You can add your own functions and include libraries. Give the file executable permissions and put it in /usr/bin if you really want. Then just use
$ myruby <source>
Here's the code for a very minimal one. As an example I've included the md5 digest library and created a custom function called md5()
#!/usr/bin/ruby -w
require 'digest/md5';
def executeCode(file)
handle = File.open(file,'r');
for line in handle.readlines()
line = line.strip();
begin
eval(line);
rescue Exception => e
print "Problem with script '" + file + "'\n";
print e + "\n";
end
end
end
def checkFile(file)
if !File.exists?(file)
print "No such source file '" + file + "'\n";
exit(1);
elsif !File.readable?(file)
print "Cannot read from source file '" + file + "'\n";
exit(1);
else
executeCode(file);
end
end
# My custom function for our "interpreter"
def md5(key=nil)
if key.nil?
raise "md5 requires 1 parameter, 0 given!\n";
else
return Digest::MD5.hexdigest(key)
end
end
if ARGV[0].nil?
print "No input file specified!\n"
exit(1);
else
checkFile(ARGV[0]);
end
Save that as myruby or myruby.rb and give it executable permissions (755). Now you're ready to create a normal ruby source file
puts "I will now generate a md5 digest for mypass using the md5() function"
puts md5('mypass')
Save that and run it as you would a normal ruby script but with our new interpreter. You'll notice I didn't need to include any libraries or write the function in the source code because it's all defined in our interpreter.
It's probably not the most ideal method, but it's the only one I can come up with.
Cheers
There is a RUBYLIB environment variable that can be set to any folder on the system
If you want to use your classes/modules globally, why not just move them to your main Ruby lib directory? eg: /usr/lib/ruby/1.8/ ?
Eg:
$ cat > /usr/lib/ruby/1.8/mymodule.rb
module HelloWorld
def hello
puts("Hello, World!");
end
end
We have our module in the main lib directory - should be able to
require it from anywhere in the system now.
$ irb
irb(main):001:0> require 'mymodule'
=> true
irb(main):002:0> include HelloWorld
=> Object
irb(main):003:0> hello
Hello, World!
=> nil

Resources