ngage 0.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.
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