Class: Jekyll::Tags::IncludeTag

Inherits:
Liquid::Tag
  • Object
show all
Defined in:
lib/liquid/tags/jekyll.rb

Constant Summary collapse

VALID_SYNTAX =

matches proper params format only 1(key-formatted str) = (2(double-quoted str) or 3(single-quoted str) 4(var-formatted str))

%r!
  ([\w-]+)\s*=\s*
  (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([a-z][\w'"\[\]\.-]*))
!x
VARIABLE_SYNTAX =

extracts filename as #variable and k/v pairs as #params

%r!
  (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
  (?<params>.*)
!mx
FULL_VALID_SYNTAX =
%r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
VALID_FILENAME_CHARS =
%r!^[\w/\.-]+$!
INVALID_SEQUENCES =
%r![./]{2,}!

Instance Method Summary collapse

Constructor Details

#initialize(tag_name, markup, tokens) ⇒ IncludeTag

Returns a new instance of IncludeTag.



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/liquid/tags/jekyll.rb', line 35

def initialize(tag_name, markup, tokens)
  super
  matched = markup.strip.match(VARIABLE_SYNTAX)
  if matched # include passes filename as variable
    @file = matched["variable"].strip # The file to include (as a var)
    @params = matched["params"].strip # The paired vars to load
  else # if the filename isn't a variable, just grab the first arg as filename and rest as params
    @file, @params = markup.strip.split(%r!\s+!, 2)
  end
  validate_params if @params
  @tag_name = tag_name
end

Instance Method Details

#load_cached_partial(path, context) ⇒ Object

# def add_include_to_dependency(site, path, context)

if context.registers[:page] && context.registers[:page].key?("path")
  site.regenerator.add_dependency(
    site.in_source_dir(context.registers[:page]["path"]),
    path
  )
end

end



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/liquid/tags/jekyll.rb', line 166

def load_cached_partial(path, context)
  context.registers[:cached_partials] ||= {}
  cached_partial = context.registers[:cached_partials]

  if cached_partial.key?(path)
    cached_partial[path]
  else
    unparsed_file = context.registers[:globals]
      .liquid_renderer
      .file(path)
    begin
      # Cache a version of the
      cached_partial[path] = unparsed_file.parse(read_file(path, context))
    rescue Liquid::Error => e
      e.template_name = path
      e.markup_context = "included " if e.markup_context.nil?
      raise e
    end
  end
end

#locate_include_file(context, file, safe) ⇒ Object

Traverse includes dirs, setting paths for includes

Raises:

  • (IOError)


120
121
122
123
124
125
126
127
# File 'lib/liquid/tags/jekyll.rb', line 120

def locate_include_file(context, file, safe)
  includes_dirs = ['_templates','_templates/liquid','_templates/liquid/ops','_templates/ops','theme/_includes','theme/_layouts']
  includes_dirs.each do |dir|
    path = File.join(dir.to_s, file.to_s)
    return path if File.exist?(path)
  end
  raise IOError, could_not_locate_message(file, includes_dirs, safe)
end

#outside_site_source?(path, dir, safe) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/liquid/tags/jekyll.rb', line 187

def outside_site_source?(path, dir, safe)
  safe && !realpath_prefixed_with?(path, dir)
end

#parse_params(context) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/liquid/tags/jekyll.rb', line 52

def parse_params(context)
  params = {}
  markup = @params
  while (match = VALID_SYNTAX.match(markup))
    # run until syntax no longer matches parameters
    markup = markup[match.end(0)..-1]
    # set val by which group matched in VALID_SYNTAX
    # either a quoted string (2,3) or a variable (4)
    value = if match[2]
              match[2].gsub(%r!\\"!, '"')
            elsif match[3]
              match[3].gsub(%r!\\'!, "'")
            elsif match[4] # val is resolved context var
              context[match[4]]
            end
    params[match[1]] = value # inserts param
  end
  params # returns hash for the include scope
end

#read_file(file, context) ⇒ Object

This method allows to modify the file content by inheriting from the class.



198
199
200
# File 'lib/liquid/tags/jekyll.rb', line 198

def read_file(file, context)
  File.read(file)
end

#realpath_prefixed_with?(path, dir) ⇒ Boolean

Returns:

  • (Boolean)


191
192
193
194
195
# File 'lib/liquid/tags/jekyll.rb', line 191

def realpath_prefixed_with?(path, dir)
  File.exist?(path) && File.realpath(path).start_with?(dir)
rescue StandardError
  false
end

#render(context) ⇒ Object

recall/render the included partial and place it in the parent doc



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/liquid/tags/jekyll.rb', line 130

def render(context)
  file = render_variable(context) || @file # use parsed variable filename unless passed explicit filename
  validate_file_name(file)
  path = locate_include_file(context, file, true) # ensure file exists in safe path
  return unless path
  # # ???????
  # add_include_to_dependency(site, path, context)
  #
  # Load the partial if it's identical to one we've already loaded ???
  partial = File.read(path) # reads the template file
  partial = Liquid::Template.parse(partial) # compiles template
  # setup and perform render
  context.stack do
    # create a hash object for any passed k/v pair args
    # by parsing passed parameters using the parent file's scope
    context["include"] = parse_params(context) if @params
    begin # render the include for output
      partial.render!(context)
    rescue Liquid::Error => e
      e.template_name = path
      e.markup_context = "included " if e.markup_context.nil?
      raise e
    end
  end
end

#render_variable(context) ⇒ Object

Express the filename from the variable Passes along the context in which it was called, from the parent file



109
110
111
# File 'lib/liquid/tags/jekyll.rb', line 109

def render_variable(context)
  Liquid::Template.parse(@file).render(context) if @file =~ VARIABLE_SYNTAX
end

#syntax_exampleObject



48
49
50
# File 'lib/liquid/tags/jekyll.rb', line 48

def syntax_example
  "{% #{@tag_name} file.ext param='value' param2='value' %}"
end

#tag_includes_dirs(context) ⇒ Object

Array of directories where includes are stored



114
115
116
117
# File 'lib/liquid/tags/jekyll.rb', line 114

def tag_includes_dirs(context)
  # context[:includes_dirs]
  ['_templates','_templates/liquid','_templates/liquid/ops','theme/_includes','_theme/layouts']
end

#validate_file_name(file) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/liquid/tags/jekyll.rb', line 72

def validate_file_name(file)
  if file =~ INVALID_SEQUENCES || file !~ VALID_FILENAME_CHARS
    raise ArgumentError, <<-MSG
Invalid syntax for include tag. File contains invalid characters or sequences:

  #{file}

Valid syntax:

  #{syntax_example}

MSG
  end
end

#validate_paramsObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/liquid/tags/jekyll.rb', line 87

def validate_params
  unless @params =~ FULL_VALID_SYNTAX
    raise ArgumentError, <<-MSG
Invalid syntax for include tag:

  #{@params}

Valid syntax:

  #{syntax_example}

MSG
  end
end