bunto 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.markdown +59 -0
- data/bin/bunto +51 -0
- data/lib/bunto.rb +179 -0
- data/lib/bunto/cleaner.rb +105 -0
- data/lib/bunto/collection.rb +205 -0
- data/lib/bunto/command.rb +65 -0
- data/lib/bunto/commands/build.rb +77 -0
- data/lib/bunto/commands/clean.rb +42 -0
- data/lib/bunto/commands/doctor.rb +114 -0
- data/lib/bunto/commands/help.rb +31 -0
- data/lib/bunto/commands/new.rb +82 -0
- data/lib/bunto/commands/serve.rb +204 -0
- data/lib/bunto/commands/serve/servlet.rb +61 -0
- data/lib/bunto/configuration.rb +323 -0
- data/lib/bunto/converter.rb +48 -0
- data/lib/bunto/converters/identity.rb +21 -0
- data/lib/bunto/converters/markdown.rb +92 -0
- data/lib/bunto/converters/markdown/kramdown_parser.rb +117 -0
- data/lib/bunto/converters/markdown/rdiscount_parser.rb +33 -0
- data/lib/bunto/converters/markdown/redcarpet_parser.rb +102 -0
- data/lib/bunto/converters/smartypants.rb +34 -0
- data/lib/bunto/convertible.rb +297 -0
- data/lib/bunto/deprecator.rb +46 -0
- data/lib/bunto/document.rb +444 -0
- data/lib/bunto/drops/bunto_drop.rb +21 -0
- data/lib/bunto/drops/collection_drop.rb +22 -0
- data/lib/bunto/drops/document_drop.rb +27 -0
- data/lib/bunto/drops/drop.rb +176 -0
- data/lib/bunto/drops/site_drop.rb +38 -0
- data/lib/bunto/drops/unified_payload_drop.rb +25 -0
- data/lib/bunto/drops/url_drop.rb +83 -0
- data/lib/bunto/entry_filter.rb +72 -0
- data/lib/bunto/errors.rb +10 -0
- data/lib/bunto/excerpt.rb +127 -0
- data/lib/bunto/external.rb +59 -0
- data/lib/bunto/filters.rb +367 -0
- data/lib/bunto/frontmatter_defaults.rb +188 -0
- data/lib/bunto/generator.rb +3 -0
- data/lib/bunto/hooks.rb +101 -0
- data/lib/bunto/layout.rb +49 -0
- data/lib/bunto/liquid_extensions.rb +22 -0
- data/lib/bunto/liquid_renderer.rb +39 -0
- data/lib/bunto/liquid_renderer/file.rb +50 -0
- data/lib/bunto/liquid_renderer/table.rb +94 -0
- data/lib/bunto/log_adapter.rb +115 -0
- data/lib/bunto/mime.types +800 -0
- data/lib/bunto/page.rb +180 -0
- data/lib/bunto/plugin.rb +96 -0
- data/lib/bunto/plugin_manager.rb +95 -0
- data/lib/bunto/post.rb +329 -0
- data/lib/bunto/publisher.rb +21 -0
- data/lib/bunto/reader.rb +126 -0
- data/lib/bunto/readers/collection_reader.rb +20 -0
- data/lib/bunto/readers/data_reader.rb +69 -0
- data/lib/bunto/readers/layout_reader.rb +53 -0
- data/lib/bunto/readers/page_reader.rb +21 -0
- data/lib/bunto/readers/post_reader.rb +62 -0
- data/lib/bunto/readers/static_file_reader.rb +21 -0
- data/lib/bunto/regenerator.rb +175 -0
- data/lib/bunto/related_posts.rb +56 -0
- data/lib/bunto/renderer.rb +191 -0
- data/lib/bunto/site.rb +391 -0
- data/lib/bunto/static_file.rb +141 -0
- data/lib/bunto/stevenson.rb +58 -0
- data/lib/bunto/tags/highlight.rb +122 -0
- data/lib/bunto/tags/include.rb +190 -0
- data/lib/bunto/tags/post_url.rb +88 -0
- data/lib/bunto/url.rb +136 -0
- data/lib/bunto/utils.rb +287 -0
- data/lib/bunto/utils/ansi.rb +59 -0
- data/lib/bunto/utils/platforms.rb +30 -0
- data/lib/bunto/version.rb +3 -0
- data/lib/site_template/.gitignore +3 -0
- data/lib/site_template/_config.yml +21 -0
- data/lib/site_template/_includes/footer.html +38 -0
- data/lib/site_template/_includes/head.html +12 -0
- data/lib/site_template/_includes/header.html +27 -0
- data/lib/site_template/_includes/icon-github.html +1 -0
- data/lib/site_template/_includes/icon-github.svg +1 -0
- data/lib/site_template/_includes/icon-twitter.html +1 -0
- data/lib/site_template/_includes/icon-twitter.svg +1 -0
- data/lib/site_template/_layouts/default.html +20 -0
- data/lib/site_template/_layouts/page.html +14 -0
- data/lib/site_template/_layouts/post.html +15 -0
- data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +25 -0
- data/lib/site_template/_sass/_base.scss +206 -0
- data/lib/site_template/_sass/_layout.scss +242 -0
- data/lib/site_template/_sass/_syntax-highlighting.scss +71 -0
- data/lib/site_template/about.md +15 -0
- data/lib/site_template/css/main.scss +53 -0
- data/lib/site_template/feed.xml +30 -0
- data/lib/site_template/index.html +23 -0
- metadata +252 -0
data/lib/bunto/errors.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Bunto
|
2
|
+
module Errors
|
3
|
+
FatalException = Class.new(::RuntimeError)
|
4
|
+
|
5
|
+
DropMutationException = Class.new(FatalException)
|
6
|
+
InvalidPermalinkError = Class.new(FatalException)
|
7
|
+
InvalidYAMLFrontMatterError = Class.new(FatalException)
|
8
|
+
MissingDependencyException = Class.new(FatalException)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Bunto
|
2
|
+
class Excerpt
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_accessor :doc
|
6
|
+
attr_accessor :content, :ext
|
7
|
+
attr_writer :output
|
8
|
+
|
9
|
+
def_delegators :@doc, :site, :name, :ext, :relative_path, :extname,
|
10
|
+
:render_with_liquid?, :collection, :related_posts
|
11
|
+
|
12
|
+
# Initialize this Excerpt instance.
|
13
|
+
#
|
14
|
+
# doc - The Document.
|
15
|
+
#
|
16
|
+
# Returns the new Excerpt.
|
17
|
+
def initialize(doc)
|
18
|
+
self.doc = doc
|
19
|
+
self.content = extract_excerpt(doc.content)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Fetch YAML front-matter data from related doc, without layout key
|
23
|
+
#
|
24
|
+
# Returns Hash of doc data
|
25
|
+
def data
|
26
|
+
@data ||= doc.data.dup
|
27
|
+
@data.delete("layout")
|
28
|
+
@data.delete("excerpt")
|
29
|
+
@data
|
30
|
+
end
|
31
|
+
|
32
|
+
def trigger_hooks(*)
|
33
|
+
end
|
34
|
+
|
35
|
+
# 'Path' of the excerpt.
|
36
|
+
#
|
37
|
+
# Returns the path for the doc this excerpt belongs to with #excerpt appended
|
38
|
+
def path
|
39
|
+
File.join(doc.path, "#excerpt")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check if excerpt includes a string
|
43
|
+
#
|
44
|
+
# Returns true if the string passed in
|
45
|
+
def include?(something)
|
46
|
+
(output && output.include?(something)) || content.include?(something)
|
47
|
+
end
|
48
|
+
|
49
|
+
# The UID for this doc (useful in feeds).
|
50
|
+
# e.g. /2008/11/05/my-awesome-doc
|
51
|
+
#
|
52
|
+
# Returns the String UID.
|
53
|
+
def id
|
54
|
+
"#{doc.id}#excerpt"
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
output || content
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_liquid
|
62
|
+
doc.data['excerpt'] = nil
|
63
|
+
@to_liquid ||= doc.to_liquid
|
64
|
+
doc.data['excerpt'] = self
|
65
|
+
@to_liquid
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the shorthand String identifier of this doc.
|
69
|
+
def inspect
|
70
|
+
"<Excerpt: #{self.id}>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def output
|
74
|
+
@output ||= Renderer.new(doc.site, self, site.site_payload).run
|
75
|
+
end
|
76
|
+
|
77
|
+
def place_in_layout?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# Internal: Extract excerpt from the content
|
84
|
+
#
|
85
|
+
# By default excerpt is your first paragraph of a doc: everything before
|
86
|
+
# the first two new lines:
|
87
|
+
#
|
88
|
+
# ---
|
89
|
+
# title: Example
|
90
|
+
# ---
|
91
|
+
#
|
92
|
+
# First paragraph with [link][1].
|
93
|
+
#
|
94
|
+
# Second paragraph.
|
95
|
+
#
|
96
|
+
# [1]: http://example.com/
|
97
|
+
#
|
98
|
+
# This is fairly good option for Markdown and Textile files. But might cause
|
99
|
+
# problems for HTML docs (which is quite unusual for Bunto). If default
|
100
|
+
# excerpt delimiter is not good for you, you might want to set your own via
|
101
|
+
# configuration option `excerpt_separator`. For example, following is a good
|
102
|
+
# alternative for HTML docs:
|
103
|
+
#
|
104
|
+
# # file: _config.yml
|
105
|
+
# excerpt_separator: "<!-- more -->"
|
106
|
+
#
|
107
|
+
# Notice that all markdown-style link references will be appended to the
|
108
|
+
# excerpt. So the example doc above will have this excerpt source:
|
109
|
+
#
|
110
|
+
# First paragraph with [link][1].
|
111
|
+
#
|
112
|
+
# [1]: http://example.com/
|
113
|
+
#
|
114
|
+
# Excerpts are rendered same time as content is rendered.
|
115
|
+
#
|
116
|
+
# Returns excerpt String
|
117
|
+
def extract_excerpt(doc_content)
|
118
|
+
head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
|
119
|
+
|
120
|
+
if tail.empty?
|
121
|
+
head
|
122
|
+
else
|
123
|
+
"" << head << "\n\n" << tail.scan(/^\[[^\]]+\]:.+$/).join("\n")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Bunto
|
2
|
+
module External
|
3
|
+
class << self
|
4
|
+
#
|
5
|
+
# Gems that, if installed, should be loaded.
|
6
|
+
# Usually contain subcommands.
|
7
|
+
#
|
8
|
+
def blessed_gems
|
9
|
+
%w(
|
10
|
+
bunto-docs
|
11
|
+
bunto-import
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Require a gem or file if it's present, otherwise silently fail.
|
17
|
+
#
|
18
|
+
# names - a string gem name or array of gem names
|
19
|
+
#
|
20
|
+
def require_if_present(names, &block)
|
21
|
+
Array(names).each do |name|
|
22
|
+
begin
|
23
|
+
require name
|
24
|
+
rescue LoadError
|
25
|
+
Bunto.logger.debug "Couldn't load #{name}. Skipping."
|
26
|
+
block.call(name) if block
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Require a gem or gems. If it's not present, show a very nice error
|
34
|
+
# message that explains everything and is much more helpful than the
|
35
|
+
# normal LoadError.
|
36
|
+
#
|
37
|
+
# names - a string gem name or array of gem names
|
38
|
+
#
|
39
|
+
def require_with_graceful_fail(names)
|
40
|
+
Array(names).each do |name|
|
41
|
+
begin
|
42
|
+
Bunto.logger.debug "Requiring:", "#{name}"
|
43
|
+
require name
|
44
|
+
rescue LoadError => e
|
45
|
+
Bunto.logger.error "Dependency Error:", <<-MSG
|
46
|
+
Yikes! It looks like you don't have #{name} or one of its dependencies installed.
|
47
|
+
In order to use Bunto as currently configured, you'll need to install this gem.
|
48
|
+
|
49
|
+
The full error message from Ruby is: '#{e.message}'
|
50
|
+
|
51
|
+
If you run into trouble, you can find helpful resources at http://buntorb.com/help/!
|
52
|
+
MSG
|
53
|
+
raise Bunto::Errors::MissingDependencyException.new(name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Bunto
|
6
|
+
module Filters
|
7
|
+
# Convert a Markdown string into HTML output.
|
8
|
+
#
|
9
|
+
# input - The Markdown String to convert.
|
10
|
+
#
|
11
|
+
# Returns the HTML formatted String.
|
12
|
+
def markdownify(input)
|
13
|
+
site = @context.registers[:site]
|
14
|
+
converter = site.find_converter_instance(Bunto::Converters::Markdown)
|
15
|
+
converter.convert(input)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Convert a Markdown string into HTML output.
|
19
|
+
#
|
20
|
+
# input - The Markdown String to convert.
|
21
|
+
#
|
22
|
+
# Returns the HTML formatted String.
|
23
|
+
def smartify(input)
|
24
|
+
site = @context.registers[:site]
|
25
|
+
converter = site.find_converter_instance(Bunto::Converters::SmartyPants)
|
26
|
+
converter.convert(input)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convert a Sass string into CSS output.
|
30
|
+
#
|
31
|
+
# input - The Sass String to convert.
|
32
|
+
#
|
33
|
+
# Returns the CSS formatted String.
|
34
|
+
def sassify(input)
|
35
|
+
site = @context.registers[:site]
|
36
|
+
converter = site.find_converter_instance(Bunto::Converters::Sass)
|
37
|
+
converter.convert(input)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert a Scss string into CSS output.
|
41
|
+
#
|
42
|
+
# input - The Scss String to convert.
|
43
|
+
#
|
44
|
+
# Returns the CSS formatted String.
|
45
|
+
def scssify(input)
|
46
|
+
site = @context.registers[:site]
|
47
|
+
converter = site.find_converter_instance(Bunto::Converters::Scss)
|
48
|
+
converter.convert(input)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Slugify a filename or title.
|
52
|
+
#
|
53
|
+
# input - The filename or title to slugify.
|
54
|
+
# mode - how string is slugified
|
55
|
+
#
|
56
|
+
# Returns the given filename or title as a lowercase URL String.
|
57
|
+
# See Utils.slugify for more detail.
|
58
|
+
def slugify(input, mode=nil)
|
59
|
+
Utils.slugify(input, :mode => mode)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Format a date in short format e.g. "27 Jan 2011".
|
63
|
+
#
|
64
|
+
# date - the Time to format.
|
65
|
+
#
|
66
|
+
# Returns the formatting String.
|
67
|
+
def date_to_string(date)
|
68
|
+
time(date).strftime("%d %b %Y")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Format a date in long format e.g. "27 January 2011".
|
72
|
+
#
|
73
|
+
# date - The Time to format.
|
74
|
+
#
|
75
|
+
# Returns the formatted String.
|
76
|
+
def date_to_long_string(date)
|
77
|
+
time(date).strftime("%d %B %Y")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Format a date for use in XML.
|
81
|
+
#
|
82
|
+
# date - The Time to format.
|
83
|
+
#
|
84
|
+
# Examples
|
85
|
+
#
|
86
|
+
# date_to_xmlschema(Time.now)
|
87
|
+
# # => "2011-04-24T20:34:46+08:00"
|
88
|
+
#
|
89
|
+
# Returns the formatted String.
|
90
|
+
def date_to_xmlschema(date)
|
91
|
+
time(date).xmlschema
|
92
|
+
end
|
93
|
+
|
94
|
+
# Format a date according to RFC-822
|
95
|
+
#
|
96
|
+
# date - The Time to format.
|
97
|
+
#
|
98
|
+
# Examples
|
99
|
+
#
|
100
|
+
# date_to_rfc822(Time.now)
|
101
|
+
# # => "Sun, 24 Apr 2011 12:34:46 +0000"
|
102
|
+
#
|
103
|
+
# Returns the formatted String.
|
104
|
+
def date_to_rfc822(date)
|
105
|
+
time(date).rfc822
|
106
|
+
end
|
107
|
+
|
108
|
+
# XML escape a string for use. Replaces any special characters with
|
109
|
+
# appropriate HTML entity replacements.
|
110
|
+
#
|
111
|
+
# input - The String to escape.
|
112
|
+
#
|
113
|
+
# Examples
|
114
|
+
#
|
115
|
+
# xml_escape('foo "bar" <baz>')
|
116
|
+
# # => "foo "bar" <baz>"
|
117
|
+
#
|
118
|
+
# Returns the escaped String.
|
119
|
+
def xml_escape(input)
|
120
|
+
CGI.escapeHTML(input.to_s)
|
121
|
+
end
|
122
|
+
|
123
|
+
# CGI escape a string for use in a URL. Replaces any special characters
|
124
|
+
# with appropriate %XX replacements.
|
125
|
+
#
|
126
|
+
# input - The String to escape.
|
127
|
+
#
|
128
|
+
# Examples
|
129
|
+
#
|
130
|
+
# cgi_escape('foo,bar;baz?')
|
131
|
+
# # => "foo%2Cbar%3Bbaz%3F"
|
132
|
+
#
|
133
|
+
# Returns the escaped String.
|
134
|
+
def cgi_escape(input)
|
135
|
+
CGI::escape(input)
|
136
|
+
end
|
137
|
+
|
138
|
+
# URI escape a string.
|
139
|
+
#
|
140
|
+
# input - The String to escape.
|
141
|
+
#
|
142
|
+
# Examples
|
143
|
+
#
|
144
|
+
# uri_escape('foo, bar \\baz?')
|
145
|
+
# # => "foo,%20bar%20%5Cbaz?"
|
146
|
+
#
|
147
|
+
# Returns the escaped String.
|
148
|
+
def uri_escape(input)
|
149
|
+
URI.escape(input)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Count the number of words in the input string.
|
153
|
+
#
|
154
|
+
# input - The String on which to operate.
|
155
|
+
#
|
156
|
+
# Returns the Integer word count.
|
157
|
+
def number_of_words(input)
|
158
|
+
input.split.length
|
159
|
+
end
|
160
|
+
|
161
|
+
# Join an array of things into a string by separating with commas and the
|
162
|
+
# word "and" for the last one.
|
163
|
+
#
|
164
|
+
# array - The Array of Strings to join.
|
165
|
+
#
|
166
|
+
# Examples
|
167
|
+
#
|
168
|
+
# array_to_sentence_string(["apples", "oranges", "grapes"])
|
169
|
+
# # => "apples, oranges, and grapes"
|
170
|
+
#
|
171
|
+
# Returns the formatted String.
|
172
|
+
def array_to_sentence_string(array)
|
173
|
+
connector = "and"
|
174
|
+
case array.length
|
175
|
+
when 0
|
176
|
+
""
|
177
|
+
when 1
|
178
|
+
array[0].to_s
|
179
|
+
when 2
|
180
|
+
"#{array[0]} #{connector} #{array[1]}"
|
181
|
+
else
|
182
|
+
"#{array[0...-1].join(', ')}, #{connector} #{array[-1]}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Convert the input into json string
|
187
|
+
#
|
188
|
+
# input - The Array or Hash to be converted
|
189
|
+
#
|
190
|
+
# Returns the converted json string
|
191
|
+
def jsonify(input)
|
192
|
+
as_liquid(input).to_json
|
193
|
+
end
|
194
|
+
|
195
|
+
# Group an array of items by a property
|
196
|
+
#
|
197
|
+
# input - the inputted Enumerable
|
198
|
+
# property - the property
|
199
|
+
#
|
200
|
+
# Returns an array of Hashes, each looking something like this:
|
201
|
+
# {"name" => "larry"
|
202
|
+
# "items" => [...] } # all the items where `property` == "larry"
|
203
|
+
def group_by(input, property)
|
204
|
+
if groupable?(input)
|
205
|
+
input.group_by do |item|
|
206
|
+
item_property(item, property).to_s
|
207
|
+
end.inject([]) do |memo, i|
|
208
|
+
memo << { "name" => i.first, "items" => i.last }
|
209
|
+
end
|
210
|
+
else
|
211
|
+
input
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Filter an array of objects
|
216
|
+
#
|
217
|
+
# input - the object array
|
218
|
+
# property - property within each object to filter by
|
219
|
+
# value - desired value
|
220
|
+
#
|
221
|
+
# Returns the filtered array of objects
|
222
|
+
def where(input, property, value)
|
223
|
+
return input unless input.is_a?(Enumerable)
|
224
|
+
input = input.values if input.is_a?(Hash)
|
225
|
+
input.select { |object| item_property(object, property).to_s == value.to_s }
|
226
|
+
end
|
227
|
+
|
228
|
+
# Sort an array of objects
|
229
|
+
#
|
230
|
+
# input - the object array
|
231
|
+
# property - property within each object to filter by
|
232
|
+
# nils ('first' | 'last') - nils appear before or after non-nil values
|
233
|
+
#
|
234
|
+
# Returns the filtered array of objects
|
235
|
+
def sort(input, property = nil, nils = "first")
|
236
|
+
if input.nil?
|
237
|
+
raise ArgumentError.new("Cannot sort a null object.")
|
238
|
+
end
|
239
|
+
if property.nil?
|
240
|
+
input.sort
|
241
|
+
else
|
242
|
+
case
|
243
|
+
when nils == "first"
|
244
|
+
order = - 1
|
245
|
+
when nils == "last"
|
246
|
+
order = + 1
|
247
|
+
else
|
248
|
+
raise ArgumentError.new("Invalid nils order: " \
|
249
|
+
"'#{nils}' is not a valid nils order. It must be 'first' or 'last'.")
|
250
|
+
end
|
251
|
+
|
252
|
+
input.sort do |apple, orange|
|
253
|
+
apple_property = item_property(apple, property)
|
254
|
+
orange_property = item_property(orange, property)
|
255
|
+
|
256
|
+
if !apple_property.nil? && orange_property.nil?
|
257
|
+
- order
|
258
|
+
elsif apple_property.nil? && !orange_property.nil?
|
259
|
+
+ order
|
260
|
+
else
|
261
|
+
apple_property <=> orange_property
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def pop(array, input = 1)
|
268
|
+
return array unless array.is_a?(Array)
|
269
|
+
new_ary = array.dup
|
270
|
+
new_ary.pop(input.to_i || 1)
|
271
|
+
new_ary
|
272
|
+
end
|
273
|
+
|
274
|
+
def push(array, input)
|
275
|
+
return array unless array.is_a?(Array)
|
276
|
+
new_ary = array.dup
|
277
|
+
new_ary.push(input)
|
278
|
+
new_ary
|
279
|
+
end
|
280
|
+
|
281
|
+
def shift(array, input = 1)
|
282
|
+
return array unless array.is_a?(Array)
|
283
|
+
new_ary = array.dup
|
284
|
+
new_ary.shift(input.to_i || 1)
|
285
|
+
new_ary
|
286
|
+
end
|
287
|
+
|
288
|
+
def unshift(array, input)
|
289
|
+
return array unless array.is_a?(Array)
|
290
|
+
new_ary = array.dup
|
291
|
+
new_ary.unshift(input)
|
292
|
+
new_ary
|
293
|
+
end
|
294
|
+
|
295
|
+
def sample(input, num = 1)
|
296
|
+
return input unless input.respond_to?(:sample)
|
297
|
+
n = num.to_i rescue 1
|
298
|
+
if n == 1
|
299
|
+
input.sample
|
300
|
+
else
|
301
|
+
input.sample(n)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Convert an object into its String representation for debugging
|
306
|
+
#
|
307
|
+
# input - The Object to be converted
|
308
|
+
#
|
309
|
+
# Returns a String representation of the object.
|
310
|
+
def inspect(input)
|
311
|
+
CGI.escapeHTML(input.inspect)
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
def time(input)
|
316
|
+
case input
|
317
|
+
when Time
|
318
|
+
input
|
319
|
+
when Date
|
320
|
+
input.to_time
|
321
|
+
when String
|
322
|
+
Time.parse(input) rescue Time.at(input.to_i)
|
323
|
+
when Numeric
|
324
|
+
Time.at(input)
|
325
|
+
else
|
326
|
+
Bunto.logger.error "Invalid Date:", "'#{input}' is not a valid datetime."
|
327
|
+
exit(1)
|
328
|
+
end.localtime
|
329
|
+
end
|
330
|
+
|
331
|
+
def groupable?(element)
|
332
|
+
element.respond_to?(:group_by)
|
333
|
+
end
|
334
|
+
|
335
|
+
def item_property(item, property)
|
336
|
+
if item.respond_to?(:to_liquid)
|
337
|
+
item.to_liquid[property.to_s]
|
338
|
+
elsif item.respond_to?(:data)
|
339
|
+
item.data[property.to_s]
|
340
|
+
else
|
341
|
+
item[property.to_s]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def as_liquid(item)
|
346
|
+
case item
|
347
|
+
when Hash
|
348
|
+
pairs = item.map { |k, v| as_liquid([k, v]) }
|
349
|
+
Hash[pairs]
|
350
|
+
when Array
|
351
|
+
item.map { |i| as_liquid(i) }
|
352
|
+
else
|
353
|
+
if item.respond_to?(:to_liquid)
|
354
|
+
liquidated = item.to_liquid
|
355
|
+
# prevent infinite recursion for simple types (which return `self`)
|
356
|
+
if liquidated == item
|
357
|
+
item
|
358
|
+
else
|
359
|
+
as_liquid(liquidated)
|
360
|
+
end
|
361
|
+
else
|
362
|
+
item
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|