且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Jekyll 扩展调用外部脚本

更新时间:2023-12-05 19:43:28

我认为您可能对 require 的工作方式感到困惑.当您调用 require 时,首先 Ruby 会检查该文件是否已被需要,如果是则直接返回.如果没有,则运行文件的内容,但与 require 语句不在同一范围内.换句话说,使用 require 与将 require 语句替换为文件的内容(em> 例如,C 的 #include 是如何工作的).

在您的情况下,当您需要 ~/.twitter_auth.rb 文件时,正在创建 @client 实例变量,但作为顶部的实例变量级别 main 对象,不是作为 TwitterFeed 实例的实例变量,其中 require 被称为表单.>

可以做一些事情,例如将 Twitter::Client 对象分配给一个常量,然后您可以从 render 方法中引用该常量:

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

然后

需要'~/twitter_auth.rb'@client = 我的客户...

我建议这只是为了解释 require 发生的事情,这不是一个很好的技术.

我认为更好的选择是将您的凭据以简单的数据格式保存在您的主目录中,然后从您的脚本中读取它们并使用它们创建 Twitter 客户端.在这种情况下,Yaml 可能会完成这项工作.

首先将您的 ~/twitter_auth.rb 替换为看起来像这样的 ~/twitter_auth.yaml:

:consumer_key: "CEoYXXXXXXXXXXXX":consumer_secret: "apnHXXXXXXXXXXXXXXXXXXXXXXXX":oauth_token: "105XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX":oauth_token_secret: "BJ7AlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

然后在你的类中有 requre "~/twitter_auth.rb" 的地方,用这个替换(你还需要 require 'yaml' 在顶部文件):

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

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.

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"))