ngage 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/exe/ngage +55 -0
  4. data/lib/ngage.rb +3 -0
  5. data/lib/ngage/jekyll.rb +204 -0
  6. data/lib/ngage/jekyll/cleaner.rb +111 -0
  7. data/lib/ngage/jekyll/collection.rb +235 -0
  8. data/lib/ngage/jekyll/command.rb +103 -0
  9. data/lib/ngage/jekyll/commands/build.rb +93 -0
  10. data/lib/ngage/jekyll/commands/clean.rb +45 -0
  11. data/lib/ngage/jekyll/commands/doctor.rb +173 -0
  12. data/lib/ngage/jekyll/commands/help.rb +34 -0
  13. data/lib/ngage/jekyll/commands/new.rb +157 -0
  14. data/lib/ngage/jekyll/commands/new_theme.rb +42 -0
  15. data/lib/ngage/jekyll/commands/serve.rb +354 -0
  16. data/lib/ngage/jekyll/commands/serve/live_reload_reactor.rb +122 -0
  17. data/lib/ngage/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
  18. data/lib/ngage/jekyll/commands/serve/servlet.rb +203 -0
  19. data/lib/ngage/jekyll/commands/serve/websockets.rb +81 -0
  20. data/lib/ngage/jekyll/configuration.rb +391 -0
  21. data/lib/ngage/jekyll/converter.rb +54 -0
  22. data/lib/ngage/jekyll/converters/identity.rb +41 -0
  23. data/lib/ngage/jekyll/converters/markdown.rb +116 -0
  24. data/lib/ngage/jekyll/converters/markdown/kramdown_parser.rb +122 -0
  25. data/lib/ngage/jekyll/converters/smartypants.rb +70 -0
  26. data/lib/ngage/jekyll/convertible.rb +253 -0
  27. data/lib/ngage/jekyll/deprecator.rb +50 -0
  28. data/lib/ngage/jekyll/document.rb +503 -0
  29. data/lib/ngage/jekyll/drops/collection_drop.rb +20 -0
  30. data/lib/ngage/jekyll/drops/document_drop.rb +69 -0
  31. data/lib/ngage/jekyll/drops/drop.rb +209 -0
  32. data/lib/ngage/jekyll/drops/excerpt_drop.rb +15 -0
  33. data/lib/ngage/jekyll/drops/jekyll_drop.rb +32 -0
  34. data/lib/ngage/jekyll/drops/site_drop.rb +56 -0
  35. data/lib/ngage/jekyll/drops/static_file_drop.rb +14 -0
  36. data/lib/ngage/jekyll/drops/unified_payload_drop.rb +26 -0
  37. data/lib/ngage/jekyll/drops/url_drop.rb +89 -0
  38. data/lib/ngage/jekyll/entry_filter.rb +127 -0
  39. data/lib/ngage/jekyll/errors.rb +20 -0
  40. data/lib/ngage/jekyll/excerpt.rb +180 -0
  41. data/lib/ngage/jekyll/external.rb +76 -0
  42. data/lib/ngage/jekyll/filters.rb +390 -0
  43. data/lib/ngage/jekyll/filters/date_filters.rb +110 -0
  44. data/lib/ngage/jekyll/filters/grouping_filters.rb +64 -0
  45. data/lib/ngage/jekyll/filters/url_filters.rb +68 -0
  46. data/lib/ngage/jekyll/frontmatter_defaults.rb +233 -0
  47. data/lib/ngage/jekyll/generator.rb +5 -0
  48. data/lib/ngage/jekyll/hooks.rb +106 -0
  49. data/lib/ngage/jekyll/layout.rb +62 -0
  50. data/lib/ngage/jekyll/liquid_extensions.rb +22 -0
  51. data/lib/ngage/jekyll/liquid_renderer.rb +63 -0
  52. data/lib/ngage/jekyll/liquid_renderer/file.rb +56 -0
  53. data/lib/ngage/jekyll/liquid_renderer/table.rb +98 -0
  54. data/lib/ngage/jekyll/log_adapter.rb +151 -0
  55. data/lib/ngage/jekyll/mime.types +825 -0
  56. data/lib/ngage/jekyll/page.rb +185 -0
  57. data/lib/ngage/jekyll/page_without_a_file.rb +14 -0
  58. data/lib/ngage/jekyll/plugin.rb +92 -0
  59. data/lib/ngage/jekyll/plugin_manager.rb +115 -0
  60. data/lib/ngage/jekyll/publisher.rb +23 -0
  61. data/lib/ngage/jekyll/reader.rb +154 -0
  62. data/lib/ngage/jekyll/readers/collection_reader.rb +22 -0
  63. data/lib/ngage/jekyll/readers/data_reader.rb +75 -0
  64. data/lib/ngage/jekyll/readers/layout_reader.rb +70 -0
  65. data/lib/ngage/jekyll/readers/page_reader.rb +25 -0
  66. data/lib/ngage/jekyll/readers/post_reader.rb +72 -0
  67. data/lib/ngage/jekyll/readers/static_file_reader.rb +25 -0
  68. data/lib/ngage/jekyll/readers/theme_assets_reader.rb +51 -0
  69. data/lib/ngage/jekyll/regenerator.rb +195 -0
  70. data/lib/ngage/jekyll/related_posts.rb +52 -0
  71. data/lib/ngage/jekyll/renderer.rb +266 -0
  72. data/lib/ngage/jekyll/site.rb +476 -0
  73. data/lib/ngage/jekyll/static_file.rb +169 -0
  74. data/lib/ngage/jekyll/stevenson.rb +60 -0
  75. data/lib/ngage/jekyll/tags/highlight.rb +108 -0
  76. data/lib/ngage/jekyll/tags/include.rb +226 -0
  77. data/lib/ngage/jekyll/tags/link.rb +40 -0
  78. data/lib/ngage/jekyll/tags/post_url.rb +104 -0
  79. data/lib/ngage/jekyll/theme.rb +73 -0
  80. data/lib/ngage/jekyll/theme_builder.rb +121 -0
  81. data/lib/ngage/jekyll/url.rb +160 -0
  82. data/lib/ngage/jekyll/utils.rb +370 -0
  83. data/lib/ngage/jekyll/utils/ansi.rb +57 -0
  84. data/lib/ngage/jekyll/utils/exec.rb +26 -0
  85. data/lib/ngage/jekyll/utils/internet.rb +37 -0
  86. data/lib/ngage/jekyll/utils/platforms.rb +82 -0
  87. data/lib/ngage/jekyll/utils/thread_event.rb +31 -0
  88. data/lib/ngage/jekyll/utils/win_tz.rb +75 -0
  89. data/lib/ngage/site_template/.gitignore +5 -0
  90. data/lib/ngage/site_template/404.html +25 -0
  91. data/lib/ngage/site_template/_config.yml +47 -0
  92. data/lib/ngage/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -0
  93. data/lib/ngage/site_template/about.markdown +18 -0
  94. data/lib/ngage/site_template/index.markdown +6 -0
  95. data/lib/ngage/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
  96. data/lib/ngage/theme_template/Gemfile +4 -0
  97. data/lib/ngage/theme_template/LICENSE.txt.erb +21 -0
  98. data/lib/ngage/theme_template/README.md.erb +52 -0
  99. data/lib/ngage/theme_template/_layouts/default.html +1 -0
  100. data/lib/ngage/theme_template/_layouts/page.html +5 -0
  101. data/lib/ngage/theme_template/_layouts/post.html +5 -0
  102. data/lib/ngage/theme_template/example/_config.yml.erb +1 -0
  103. data/lib/ngage/theme_template/example/_post.md +12 -0
  104. data/lib/ngage/theme_template/example/index.html +14 -0
  105. data/lib/ngage/theme_template/example/style.scss +7 -0
  106. data/lib/ngage/theme_template/gitignore.erb +6 -0
  107. data/lib/ngage/theme_template/theme.gemspec.erb +19 -0
  108. data/lib/ngage/version.rb +5 -0
  109. metadata +328 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d8c2ce22da23b23b6b841fe4d12997fe28d7508
4
+ data.tar.gz: 96bb7df108b7b4b8e1a29173199cbf59b9aed3b0
5
+ SHA512:
6
+ metadata.gz: f5020313d3402c110730edb0d27cf7297760f4a50038848b46429835ca83c83383f2acc57e3ef135b1e66899b9f17996a535b82519894ecc6fa4d0d41d221f2b
7
+ data.tar.gz: 5d1483a217e4343da15df13a60af2e8ebb7b2e002a451cc2d7866dc87edb039822f528f1307cb020c6a5812a173d8b03bd16d4e3a22783ee1f122259efb43823
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2008-2018 Tom Preston-Werner and Jekyll contributors
4
+ Copyright (c) 2018-present Ashwin Maroli
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ STDOUT.sync = true
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+
8
+ require "ngage"
9
+ require "mercenary"
10
+
11
+ Jekyll::PluginManager.require_from_bundler
12
+ Jekyll::Deprecator.process(ARGV)
13
+
14
+ Mercenary.program(:ngage) do |p|
15
+ p.version NGage::VERSION
16
+ p.description "An enhanced fork of Jekyll, NGage is yet another static site generator in Ruby"
17
+ p.syntax "ngage <subcommand> [options]"
18
+
19
+ p.option "source", "-s", "--source [DIR]", "Source directory (defaults to ./)"
20
+ p.option "destination", "-d", "--destination [DIR]",
21
+ "Destination directory (defaults to ./_site)"
22
+ p.option "safe", "--safe", "Safe mode (defaults to false)"
23
+ p.option "plugins_dir", "-p", "--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]", Array,
24
+ "Plugins directory (defaults to ./_plugins)"
25
+ p.option "layouts_dir", "--layouts DIR", String,
26
+ "Layouts directory (defaults to ./_layouts)"
27
+ p.option "profile", "--profile", "Generate a Liquid rendering profile"
28
+
29
+ Jekyll::External.require_if_present(Jekyll::External.blessed_gems) do |g, ver_constraint|
30
+ cmd = g.split("-").last
31
+ p.command(cmd.to_sym) do |c|
32
+ c.syntax cmd
33
+ c.action do
34
+ Jekyll.logger.abort_with "You must install the '#{g}' gem version #{ver_constraint} to use the 'ngage #{cmd}' command."
35
+ end
36
+ end
37
+ end
38
+
39
+ Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }
40
+
41
+ p.action do |args, _|
42
+ if args.empty?
43
+ Jekyll.logger.error "A subcommand is required."
44
+ puts p
45
+ abort
46
+ else
47
+ subcommand = args.first
48
+ unless p.has_command? subcommand
49
+ Jekyll.logger.abort_with "fatal: 'ngage #{args.first}' could not" \
50
+ " be found. You may need to install the jekyll-#{args.first} gem" \
51
+ " or a related gem to be able to use this subcommand."
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ngage/jekyll"
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift __dir__ # For use/testing when no gem is installed
4
+
5
+ # Require all of the Ruby files in the given directory.
6
+ #
7
+ # path - The String relative path from here to the directory.
8
+ #
9
+ # Returns nothing.
10
+ def require_all(path)
11
+ glob = File.join(__dir__, path, "*.rb")
12
+ Dir[glob].sort.each do |f|
13
+ require f
14
+ end
15
+ end
16
+
17
+ # rubygems
18
+ require "rubygems"
19
+
20
+ # stdlib
21
+ require "forwardable"
22
+ require "fileutils"
23
+ require "time"
24
+ require "English"
25
+ require "pathname"
26
+ require "logger"
27
+ require "set"
28
+ require "csv"
29
+ require "json"
30
+
31
+ # 3rd party
32
+ require "pathutil"
33
+ require "addressable/uri"
34
+ require "safe_yaml/load"
35
+ require "liquid"
36
+ require "kramdown"
37
+ require "colorator"
38
+ require "i18n"
39
+
40
+ SafeYAML::OPTIONS[:suppress_warnings] = true
41
+
42
+ module Jekyll
43
+ # internal requires
44
+ autoload :Cleaner, "jekyll/cleaner"
45
+ autoload :Collection, "jekyll/collection"
46
+ autoload :Configuration, "jekyll/configuration"
47
+ autoload :Convertible, "jekyll/convertible"
48
+ autoload :Deprecator, "jekyll/deprecator"
49
+ autoload :Document, "jekyll/document"
50
+ autoload :EntryFilter, "jekyll/entry_filter"
51
+ autoload :Errors, "jekyll/errors"
52
+ autoload :Excerpt, "jekyll/excerpt"
53
+ autoload :External, "jekyll/external"
54
+ autoload :FrontmatterDefaults, "jekyll/frontmatter_defaults"
55
+ autoload :Hooks, "jekyll/hooks"
56
+ autoload :Layout, "jekyll/layout"
57
+ autoload :CollectionReader, "jekyll/readers/collection_reader"
58
+ autoload :DataReader, "jekyll/readers/data_reader"
59
+ autoload :LayoutReader, "jekyll/readers/layout_reader"
60
+ autoload :PostReader, "jekyll/readers/post_reader"
61
+ autoload :PageReader, "jekyll/readers/page_reader"
62
+ autoload :StaticFileReader, "jekyll/readers/static_file_reader"
63
+ autoload :ThemeAssetsReader, "jekyll/readers/theme_assets_reader"
64
+ autoload :LogAdapter, "jekyll/log_adapter"
65
+ autoload :Page, "jekyll/page"
66
+ autoload :PageWithoutAFile, "jekyll/page_without_a_file"
67
+ autoload :PluginManager, "jekyll/plugin_manager"
68
+ autoload :Publisher, "jekyll/publisher"
69
+ autoload :Reader, "jekyll/reader"
70
+ autoload :Regenerator, "jekyll/regenerator"
71
+ autoload :RelatedPosts, "jekyll/related_posts"
72
+ autoload :Renderer, "jekyll/renderer"
73
+ autoload :LiquidRenderer, "jekyll/liquid_renderer"
74
+ autoload :Site, "jekyll/site"
75
+ autoload :StaticFile, "jekyll/static_file"
76
+ autoload :Stevenson, "jekyll/stevenson"
77
+ autoload :Theme, "jekyll/theme"
78
+ autoload :ThemeBuilder, "jekyll/theme_builder"
79
+ autoload :URL, "jekyll/url"
80
+ autoload :Utils, "jekyll/utils"
81
+ autoload :VERSION, "jekyll/version"
82
+
83
+ # extensions
84
+ require "jekyll/plugin"
85
+ require "jekyll/converter"
86
+ require "jekyll/generator"
87
+ require "jekyll/command"
88
+ require "jekyll/liquid_extensions"
89
+ require "jekyll/filters"
90
+
91
+ class << self
92
+ # Public: Tells you which Jekyll environment you are building in so you can skip tasks
93
+ # if you need to. This is useful when doing expensive compression tasks on css and
94
+ # images and allows you to skip that when working in development.
95
+
96
+ def env
97
+ ENV["JEKYLL_ENV"] || "development"
98
+ end
99
+
100
+ # Public: Generate a Jekyll configuration Hash by merging the default
101
+ # options with anything in _config.yml, and adding the given options on top.
102
+ #
103
+ # override - A Hash of config directives that override any options in both
104
+ # the defaults and the config file.
105
+ # See Jekyll::Configuration::DEFAULTS for a
106
+ # list of option names and their defaults.
107
+ #
108
+ # Returns the final configuration Hash.
109
+ def configuration(override = {})
110
+ config = Configuration.new
111
+ override = Configuration[override].stringify_keys
112
+ unless override.delete("skip_config_files")
113
+ config = config.read_config_files(config.config_files(override))
114
+ end
115
+
116
+ # Merge DEFAULTS < _config.yml < override
117
+ Configuration.from(Utils.deep_merge_hashes(config, override)).tap do |obj|
118
+ set_timezone(obj["timezone"]) if obj["timezone"]
119
+ end
120
+ end
121
+
122
+ # Public: Set the TZ environment variable to use the timezone specified
123
+ #
124
+ # timezone - the IANA Time Zone
125
+ #
126
+ # Returns nothing
127
+ # rubocop:disable Naming/AccessorMethodName
128
+ def set_timezone(timezone)
129
+ ENV["TZ"] = if Utils::Platforms.really_windows?
130
+ Utils::WinTZ.calculate(timezone)
131
+ else
132
+ timezone
133
+ end
134
+ end
135
+ # rubocop:enable Naming/AccessorMethodName
136
+
137
+ # Public: Fetch the logger instance for this Jekyll process.
138
+ #
139
+ # Returns the LogAdapter instance.
140
+ def logger
141
+ @logger ||= LogAdapter.new(Stevenson.new, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym)
142
+ end
143
+
144
+ # Public: Set the log writer.
145
+ # New log writer must respond to the same methods
146
+ # as Ruby's interal Logger.
147
+ #
148
+ # writer - the new Logger-compatible log transport
149
+ #
150
+ # Returns the new logger.
151
+ def logger=(writer)
152
+ @logger = LogAdapter.new(writer, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym)
153
+ end
154
+
155
+ # Public: An array of sites
156
+ #
157
+ # Returns the Jekyll sites created.
158
+ def sites
159
+ @sites ||= []
160
+ end
161
+
162
+ # Public: Ensures the questionable path is prefixed with the base directory
163
+ # and prepends the questionable path with the base directory if false.
164
+ #
165
+ # base_directory - the directory with which to prefix the questionable path
166
+ # questionable_path - the path we're unsure about, and want prefixed
167
+ #
168
+ # Returns the sanitized path.
169
+ def sanitized_path(base_directory, questionable_path)
170
+ return base_directory if base_directory.eql?(questionable_path)
171
+
172
+ clean_path = questionable_path.dup
173
+ clean_path.insert(0, "/") if clean_path.start_with?("~")
174
+ clean_path = File.expand_path(clean_path, "/")
175
+
176
+ return clean_path if clean_path.eql?(base_directory)
177
+
178
+ # remove any remaining extra leading slashes not stripped away by calling
179
+ # `File.expand_path` above.
180
+ clean_path.squeeze!("/")
181
+
182
+ if clean_path.start_with?(base_directory.sub(%r!\z!, "/"))
183
+ clean_path
184
+ else
185
+ clean_path.sub!(%r!\A\w:/!, "/")
186
+ File.join(base_directory, clean_path)
187
+ end
188
+ end
189
+
190
+ # Conditional optimizations
191
+ Jekyll::External.require_if_present("liquid-c")
192
+ end
193
+ end
194
+
195
+ require "jekyll/drops/drop"
196
+ require "jekyll/drops/document_drop"
197
+ require_all "jekyll/commands"
198
+ require_all "jekyll/converters"
199
+ require_all "jekyll/converters/markdown"
200
+ require_all "jekyll/drops"
201
+ require_all "jekyll/generators"
202
+ require_all "jekyll/tags"
203
+
204
+ require "jekyll-sass-converter"
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ # Handles the cleanup of a site's destination before it is built.
5
+ class Cleaner
6
+ HIDDEN_FILE_REGEX = %r!\/\.{1,2}$!
7
+ attr_reader :site
8
+
9
+ def initialize(site)
10
+ @site = site
11
+ end
12
+
13
+ # Cleans up the site's destination directory
14
+ def cleanup!
15
+ FileUtils.rm_rf(obsolete_files)
16
+ FileUtils.rm_rf(metadata_file) unless @site.incremental?
17
+ end
18
+
19
+ private
20
+
21
+ # Private: The list of files and directories to be deleted during cleanup process
22
+ #
23
+ # Returns an Array of the file and directory paths
24
+ def obsolete_files
25
+ out = (existing_files - new_files - new_dirs + replaced_files).to_a
26
+ Jekyll::Hooks.trigger :clean, :on_obsolete, out
27
+ out
28
+ end
29
+
30
+ # Private: The metadata file storing dependency tree and build history
31
+ #
32
+ # Returns an Array with the metdata file as the only item
33
+ def metadata_file
34
+ [site.regenerator.metadata_file]
35
+ end
36
+
37
+ # Private: The list of existing files, apart from those included in
38
+ # keep_files and hidden files.
39
+ #
40
+ # Returns a Set with the file paths
41
+ def existing_files
42
+ files = Set.new
43
+ regex = keep_file_regex
44
+ dirs = keep_dirs
45
+
46
+ Utils.safe_glob(site.in_dest_dir, ["**", "*"], File::FNM_DOTMATCH).each do |file|
47
+ next if file =~ HIDDEN_FILE_REGEX || file =~ regex || dirs.include?(file)
48
+
49
+ files << file
50
+ end
51
+
52
+ files
53
+ end
54
+
55
+ # Private: The list of files to be created when site is built.
56
+ #
57
+ # Returns a Set with the file paths
58
+ def new_files
59
+ @new_files ||= Set.new.tap do |files|
60
+ site.each_site_file { |item| files << item.destination(site.dest) }
61
+ end
62
+ end
63
+
64
+ # Private: The list of directories to be created when site is built.
65
+ # These are the parent directories of the files in #new_files.
66
+ #
67
+ # Returns a Set with the directory paths
68
+ def new_dirs
69
+ @new_dirs ||= new_files.map { |file| parent_dirs(file) }.flatten.to_set
70
+ end
71
+
72
+ # Private: The list of parent directories of a given file
73
+ #
74
+ # Returns an Array with the directory paths
75
+ def parent_dirs(file)
76
+ parent_dir = File.dirname(file)
77
+ if parent_dir == site.dest
78
+ []
79
+ else
80
+ [parent_dir] + parent_dirs(parent_dir)
81
+ end
82
+ end
83
+
84
+ # Private: The list of existing files that will be replaced by a directory
85
+ # during build
86
+ #
87
+ # Returns a Set with the file paths
88
+ def replaced_files
89
+ new_dirs.select { |dir| File.file?(dir) }.to_set
90
+ end
91
+
92
+ # Private: The list of directories that need to be kept because they are
93
+ # parent directories of files specified in keep_files
94
+ #
95
+ # Returns a Set with the directory paths
96
+ def keep_dirs
97
+ site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
98
+ end
99
+
100
+ # Private: Creates a regular expression from the config's keep_files array
101
+ #
102
+ # Examples
103
+ # ['.git','.svn'] with site.dest "/myblog/_site" creates
104
+ # the following regex: /\A\/myblog\/_site\/(\.git|\/.svn)/
105
+ #
106
+ # Returns the regular expression
107
+ def keep_file_regex
108
+ %r!\A#{Regexp.quote(site.dest)}\/(#{Regexp.union(site.keep_files).source})!
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class Collection
5
+ attr_reader :site, :label, :metadata
6
+ attr_writer :docs
7
+
8
+ # Create a new Collection.
9
+ #
10
+ # site - the site to which this collection belongs.
11
+ # label - the name of the collection
12
+ #
13
+ # Returns nothing.
14
+ def initialize(site, label)
15
+ @site = site
16
+ @label = sanitize_label(label)
17
+ @metadata = extract_metadata
18
+ end
19
+
20
+ # Fetch the Documents in this collection.
21
+ # Defaults to an empty array if no documents have been read in.
22
+ #
23
+ # Returns an array of Jekyll::Document objects.
24
+ def docs
25
+ @docs ||= []
26
+ end
27
+
28
+ # Override of normal respond_to? to match method_missing's logic for
29
+ # looking in @data.
30
+ def respond_to_missing?(method, include_private = false)
31
+ docs.respond_to?(method.to_sym, include_private) || super
32
+ end
33
+
34
+ # Override of method_missing to check in @data for the key.
35
+ def method_missing(method, *args, &blck)
36
+ if docs.respond_to?(method.to_sym)
37
+ Jekyll.logger.warn "Deprecation:",
38
+ "#{label}.#{method} should be changed to #{label}.docs.#{method}."
39
+ Jekyll.logger.warn "", "Called by #{caller(0..0)}."
40
+ docs.public_send(method.to_sym, *args, &blck)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ # Fetch the static files in this collection.
47
+ # Defaults to an empty array if no static files have been read in.
48
+ #
49
+ # Returns an array of Jekyll::StaticFile objects.
50
+ def files
51
+ @files ||= []
52
+ end
53
+
54
+ # Read the allowed documents into the collection's array of docs.
55
+ #
56
+ # Returns the sorted array of docs.
57
+ def read
58
+ filtered_entries.each do |file_path|
59
+ full_path = collection_dir(file_path)
60
+ next if File.directory?(full_path)
61
+
62
+ if Utils.has_yaml_header? full_path
63
+ read_document(full_path)
64
+ else
65
+ read_static_file(file_path, full_path)
66
+ end
67
+ end
68
+ docs.sort!
69
+ end
70
+
71
+ # All the entries in this collection.
72
+ #
73
+ # Returns an Array of file paths to the documents in this collection
74
+ # relative to the collection's directory
75
+ def entries
76
+ return [] unless exists?
77
+
78
+ @entries ||=
79
+ Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry|
80
+ entry["#{collection_dir}/"] = ""
81
+ entry
82
+ end
83
+ end
84
+
85
+ # Filtered version of the entries in this collection.
86
+ # See `Jekyll::EntryFilter#filter` for more information.
87
+ #
88
+ # Returns a list of filtered entry paths.
89
+ def filtered_entries
90
+ return [] unless exists?
91
+
92
+ @filtered_entries ||=
93
+ Dir.chdir(directory) do
94
+ entry_filter.filter(entries).reject do |f|
95
+ path = collection_dir(f)
96
+ File.directory?(path) || entry_filter.symlink?(f)
97
+ end
98
+ end
99
+ end
100
+
101
+ # The directory for this Collection, relative to the site source or the directory
102
+ # containing the collection.
103
+ #
104
+ # Returns a String containing the directory name where the collection
105
+ # is stored on the filesystem.
106
+ def relative_directory
107
+ @relative_directory ||= "_#{label}"
108
+ end
109
+
110
+ # The full path to the directory containing the collection.
111
+ #
112
+ # Returns a String containing th directory name where the collection
113
+ # is stored on the filesystem.
114
+ def directory
115
+ @directory ||= site.in_source_dir(
116
+ File.join(container, relative_directory)
117
+ )
118
+ end
119
+
120
+ # The full path to the directory containing the collection, with
121
+ # optional subpaths.
122
+ #
123
+ # *files - (optional) any other path pieces relative to the
124
+ # directory to append to the path
125
+ #
126
+ # Returns a String containing th directory name where the collection
127
+ # is stored on the filesystem.
128
+ def collection_dir(*files)
129
+ return directory if files.empty?
130
+
131
+ site.in_source_dir(container, relative_directory, *files)
132
+ end
133
+
134
+ # Checks whether the directory "exists" for this collection.
135
+ # The directory must exist on the filesystem and must not be a symlink
136
+ # if in safe mode.
137
+ #
138
+ # Returns false if the directory doesn't exist or if it's a symlink
139
+ # and we're in safe mode.
140
+ def exists?
141
+ File.directory?(directory) && !entry_filter.symlink?(directory)
142
+ end
143
+
144
+ # The entry filter for this collection.
145
+ # Creates an instance of Jekyll::EntryFilter.
146
+ #
147
+ # Returns the instance of Jekyll::EntryFilter for this collection.
148
+ def entry_filter
149
+ @entry_filter ||= Jekyll::EntryFilter.new(site, relative_directory)
150
+ end
151
+
152
+ # An inspect string.
153
+ #
154
+ # Returns the inspect string
155
+ def inspect
156
+ "#<Jekyll::Collection @label=#{label} docs=#{docs}>"
157
+ end
158
+
159
+ # Produce a sanitized label name
160
+ # Label names may not contain anything but alphanumeric characters,
161
+ # underscores, and hyphens.
162
+ #
163
+ # label - the possibly-unsafe label
164
+ #
165
+ # Returns a sanitized version of the label.
166
+ def sanitize_label(label)
167
+ label.gsub(%r![^a-z0-9_\-\.]!i, "")
168
+ end
169
+
170
+ # Produce a representation of this Collection for use in Liquid.
171
+ # Exposes two attributes:
172
+ # - label
173
+ # - docs
174
+ #
175
+ # Returns a representation of this collection for use in Liquid.
176
+ def to_liquid
177
+ Drops::CollectionDrop.new self
178
+ end
179
+
180
+ # Whether the collection's documents ought to be written as individual
181
+ # files in the output.
182
+ #
183
+ # Returns true if the 'write' metadata is true, false otherwise.
184
+ def write?
185
+ !!metadata.fetch("output", false)
186
+ end
187
+
188
+ # The URL template to render collection's documents at.
189
+ #
190
+ # Returns the URL template to render collection's documents at.
191
+ def url_template
192
+ @url_template ||= metadata.fetch("permalink") do
193
+ Utils.add_permalink_suffix("/:collection/:path", site.permalink_style)
194
+ end
195
+ end
196
+
197
+ # Extract options for this collection from the site configuration.
198
+ #
199
+ # Returns the metadata for this collection
200
+ def extract_metadata
201
+ if site.config["collections"].is_a?(Hash)
202
+ site.config["collections"][label] || {}
203
+ else
204
+ {}
205
+ end
206
+ end
207
+
208
+ private
209
+
210
+ def container
211
+ @container ||= site.config["collections_dir"]
212
+ end
213
+
214
+ def read_document(full_path)
215
+ doc = Document.new(full_path, :site => site, :collection => self)
216
+ doc.read
217
+ docs << doc if site.unpublished || doc.published?
218
+ end
219
+
220
+ def read_static_file(file_path, full_path)
221
+ relative_dir = Jekyll.sanitized_path(
222
+ relative_directory,
223
+ File.dirname(file_path)
224
+ ).chomp("/.")
225
+
226
+ files << StaticFile.new(
227
+ site,
228
+ site.source,
229
+ relative_dir,
230
+ File.basename(full_path),
231
+ self
232
+ )
233
+ end
234
+ end
235
+ end