cboettig
cboettig

Reputation: 12677

Jekyll extension calling an external script

I've written a simple Jekyll plugin to pull in my tweets using the twitter gem (see below). I'd like to keep the ruby script for the plugin on my open Github site, but following recent changes to the twitter API, the gem now requires authentication credentials.

require 'twitter'   # Twitter API
require 'redcarpet' # Formatting links

module Jekyll
  class TwitterFeed < Liquid::Tag
    def initialize(tag_name, text, tokens)
      super
      input = text.split(/, */ )
      @user = input[0]
      @count = input[1]
      if input[1] == nil
        @count = 3
      end
    end
    def render(context)
      # Initialize a redcarpet markdown renderer to autolink urls
      # Could use octokit instead to get GFM
      markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
                                                 :autolink => true, 
                                                 :space_after_headers => true)

      ## Attempt to load credentials externally here:
      require '~/.twitter_auth.rb'

      out = "<ul>"
      tweets = @client.user_timeline(@user)
      for i in 0 ... @count.to_i
      out = out + "<li>" + markdown.render(tweets[i].text) +
        " <a href=\"http://twitter.com/" + @user + "/statuses/" + 
        tweets[i].id.to_s + "\">"  + tweets[i].created_at.strftime("%I:%M %Y/%m/%d") + 
        "</a> " + "</li>"
      end
      out + "</ul>"
    end
  end
end
Liquid::Template.register_tag('twitter_feed', Jekyll::TwitterFeed)

If I replace the line

      require '~/.twitter_auth.rb'

where twitter_auth.rb contains something like:

require 'twitter'
@client = Twitter::Client.new(
:consumer_key => "CEoYXXXXXXXXXXX",
:consumer_secret => "apnHXXXXXXXXXXXXXXXXXXXXXXXX",
:oauth_token => "105XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
:oauth_token_secret => "BJ7AlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
)

If I place these contents directly into the script above, then my plugin script works just fine. But when I move them to an external file and try to read them in as shown, Jekyll fails to authenticate. The function seems to work just fine when I call it from irb, so I am not sure why it does not work during the Jekyll build.

Upvotes: 2

Views: 556

Answers (1)

matt
matt

Reputation: 79723

I think that you may be confused about how require works. When you call require, first Ruby checks if the file has already been required, if so it just returns directly. If it hasn’t then the contents of the file are run, but not in the same scope as the require statement. In other words using require isn’t the same as replacing the require statement with the contents of the file (which is how, for example, C’s #include works).

In your case, when you require your ~/.twitter_auth.rb file, the @client instance variable is being created, but as an instance variable of the top level main object, not as an instance variable of the TwitterFeed instance where require is being called form.

You could do something like assign the Twitter::Client object to a constant that you could then reference from the render method:

MyClient = Twitter::Client.new{...

and then

require '~/twitter_auth.rb'
@client = MyClient
...

I only suggest this as an explanation of what’s happening with require, it’s not really a good technique.

A better option, I think, would be to keep your credentials in a simple data format in your home directory, then read them form your script and create the Twitter client with them. In this case Yaml would probably do the job.

First replace your ~/twitter_auth.rb with a ~/twitter_auth.yaml that looks soemthing like:

:consumer_key: "CEoYXXXXXXXXXXX"
:consumer_secret: "apnHXXXXXXXXXXXXXXXXXXXXXXXX"
:oauth_token: "105XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
:oauth_token_secret: "BJ7AlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Then where you have requre "~/twitter_auth.rb" in your class, replace with this (you’ll also need require 'yaml' at the top of the file):

@client = Twitter::Client.new(YAML.load_file("~/twitter_auth.yaml"))

Upvotes: 2

Related Questions