geb 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +37 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/LICENSE +21 -0
  6. data/README.md +384 -0
  7. data/Rakefile +21 -0
  8. data/bin/geb +21 -0
  9. data/lib/geb/cli.rb +40 -0
  10. data/lib/geb/commands/build.rb +59 -0
  11. data/lib/geb/commands/init.rb +70 -0
  12. data/lib/geb/commands/release.rb +54 -0
  13. data/lib/geb/commands/remote.rb +48 -0
  14. data/lib/geb/commands/server.rb +77 -0
  15. data/lib/geb/commands/upload.rb +48 -0
  16. data/lib/geb/commands/version.rb +36 -0
  17. data/lib/geb/config.rb +103 -0
  18. data/lib/geb/defaults.rb +44 -0
  19. data/lib/geb/git.rb +112 -0
  20. data/lib/geb/page.rb +217 -0
  21. data/lib/geb/partial.rb +150 -0
  22. data/lib/geb/samples/basic/assets/css/site.css +7 -0
  23. data/lib/geb/samples/basic/assets/images/android-chrome-192x192.png +0 -0
  24. data/lib/geb/samples/basic/assets/images/android-chrome-512x512.png +0 -0
  25. data/lib/geb/samples/basic/assets/images/apple-touch-icon.png +0 -0
  26. data/lib/geb/samples/basic/assets/images/favicon-16x16.png +0 -0
  27. data/lib/geb/samples/basic/assets/images/favicon-32x32.png +0 -0
  28. data/lib/geb/samples/basic/assets/images/favicon.ico +0 -0
  29. data/lib/geb/samples/basic/assets/images/hero.png +0 -0
  30. data/lib/geb/samples/basic/assets/images/og-thumb.png +0 -0
  31. data/lib/geb/samples/basic/assets/images/twitter-thumb.png +0 -0
  32. data/lib/geb/samples/basic/assets/js/site.js +5 -0
  33. data/lib/geb/samples/basic/geb.config.yml +70 -0
  34. data/lib/geb/samples/basic/index.html +11 -0
  35. data/lib/geb/samples/basic/page.html +11 -0
  36. data/lib/geb/samples/basic/shared/partials/_analytics.html +9 -0
  37. data/lib/geb/samples/basic/shared/partials/_footer.html +3 -0
  38. data/lib/geb/samples/basic/shared/partials/_global_assets.html +19 -0
  39. data/lib/geb/samples/basic/shared/partials/_header.html +0 -0
  40. data/lib/geb/samples/basic/shared/partials/_meta_tags.html +34 -0
  41. data/lib/geb/samples/basic/shared/templates/_blog_post.html +0 -0
  42. data/lib/geb/samples/basic/shared/templates/_site.html +19 -0
  43. data/lib/geb/samples/basic/site.webmanifest +1 -0
  44. data/lib/geb/samples/bootstrap_jquery/assets/css/site.css +7 -0
  45. data/lib/geb/samples/bootstrap_jquery/assets/images/android-chrome-192x192.png +0 -0
  46. data/lib/geb/samples/bootstrap_jquery/assets/images/android-chrome-512x512.png +0 -0
  47. data/lib/geb/samples/bootstrap_jquery/assets/images/apple-touch-icon.png +0 -0
  48. data/lib/geb/samples/bootstrap_jquery/assets/images/favicon-16x16.png +0 -0
  49. data/lib/geb/samples/bootstrap_jquery/assets/images/favicon-32x32.png +0 -0
  50. data/lib/geb/samples/bootstrap_jquery/assets/images/favicon.ico +0 -0
  51. data/lib/geb/samples/bootstrap_jquery/assets/images/hero.png +0 -0
  52. data/lib/geb/samples/bootstrap_jquery/assets/images/og-thumb.png +0 -0
  53. data/lib/geb/samples/bootstrap_jquery/assets/images/twitter-thumb.png +0 -0
  54. data/lib/geb/samples/bootstrap_jquery/assets/js/site.js +5 -0
  55. data/lib/geb/samples/bootstrap_jquery/blog/blog_post_1.html +35 -0
  56. data/lib/geb/samples/bootstrap_jquery/blog/blog_post_2.html +35 -0
  57. data/lib/geb/samples/bootstrap_jquery/blog/blog_post_3.html +35 -0
  58. data/lib/geb/samples/bootstrap_jquery/blog/index.html +17 -0
  59. data/lib/geb/samples/bootstrap_jquery/geb.config.yml +69 -0
  60. data/lib/geb/samples/bootstrap_jquery/index.html +11 -0
  61. data/lib/geb/samples/bootstrap_jquery/page.html +11 -0
  62. data/lib/geb/samples/bootstrap_jquery/shared/partials/_analytics.html +9 -0
  63. data/lib/geb/samples/bootstrap_jquery/shared/partials/_footer.html +3 -0
  64. data/lib/geb/samples/bootstrap_jquery/shared/partials/_global_assets.html +19 -0
  65. data/lib/geb/samples/bootstrap_jquery/shared/partials/_header.html +0 -0
  66. data/lib/geb/samples/bootstrap_jquery/shared/partials/_meta_tags.html +34 -0
  67. data/lib/geb/samples/bootstrap_jquery/shared/templates/_blog_post.html +0 -0
  68. data/lib/geb/samples/bootstrap_jquery/shared/templates/_site.html +19 -0
  69. data/lib/geb/samples/bootstrap_jquery/site.webmanifest +1 -0
  70. data/lib/geb/samples/geb.config.yml +70 -0
  71. data/lib/geb/server.rb +138 -0
  72. data/lib/geb/site/build.rb +189 -0
  73. data/lib/geb/site/core.rb +229 -0
  74. data/lib/geb/site/release.rb +70 -0
  75. data/lib/geb/site/remote.rb +142 -0
  76. data/lib/geb/site/template.rb +208 -0
  77. data/lib/geb/site.rb +83 -0
  78. data/lib/geb/template.rb +166 -0
  79. data/lib/geb/utilities.rb +110 -0
  80. data/lib/geb.rb +36 -0
  81. data/lib/seth.rb +50 -0
  82. data/sig/geb.rbs +4 -0
  83. data/test/api tests/test_cli.rb +200 -0
  84. data/test/api tests/test_config.rb +330 -0
  85. data/test/api tests/test_defaults.rb +62 -0
  86. data/test/api tests/test_git.rb +105 -0
  87. data/test/api tests/test_page.rb +320 -0
  88. data/test/api tests/test_partial.rb +152 -0
  89. data/test/api tests/test_server.rb +416 -0
  90. data/test/api tests/test_site.rb +1315 -0
  91. data/test/api tests/test_template.rb +249 -0
  92. data/test/api tests/test_utilities.rb +162 -0
  93. data/test/command tests/test_geb_build.rb +199 -0
  94. data/test/command tests/test_geb_init.rb +312 -0
  95. data/test/command tests/test_geb_release.rb +166 -0
  96. data/test/command tests/test_geb_remote.rb +66 -0
  97. data/test/command tests/test_geb_server.rb +122 -0
  98. data/test/command tests/test_geb_upload.rb +96 -0
  99. data/test/command tests/test_geb_version.rb +58 -0
  100. data/test/support/geb_api_test.rb +37 -0
  101. data/test/support/geb_cli_test.rb +275 -0
  102. data/test/support/geb_minitest_ext.rb +35 -0
  103. data/test/support/geb_test_helpers.rb +84 -0
  104. data/test/support/geb_web_server_proxy.rb +128 -0
  105. data/test/test_helper.rb +61 -0
  106. metadata +301 -0
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # This module is responsible for handling site templates, including downloading
4
+ # and extracting templates from remote URLs.
5
+ #
6
+ # @title Geb - Site - Template Module
7
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
8
+ # @copyright 2024 Edin Mustajbegovic
9
+ # @license MIT
10
+ #
11
+ # @see https://github.com/mainfram-work/geb for more information
12
+
13
+ module Geb
14
+ class Site
15
+ module Template
16
+
17
+ class InvalidTemplateURL < Geb::Error
18
+ MESSAGE = "Invalid template URL specified. Ensure the template URL is properly accessible and packaged Gab site using geb release --with_template".freeze
19
+ def initialize(e = ""); super(e, MESSAGE); end
20
+ end # class InvalidTemplateURL < Geb::Error
21
+
22
+ class InvalidTemplateSpecification < Geb::Error
23
+ MESSAGE = "Template has no template paths defined in geb.config.yml".freeze
24
+ def initialize(e = ""); super(e, MESSAGE); end
25
+ end # class InvalidTemplateSpecification < Geb::Error
26
+
27
+ # copy the template from the specified path to the site path. It uses template_paths from the configuration
28
+ # file to find the template. If the template is not found, it raises an error.
29
+ # @raise UnvalidatedSiteAndTemplate if the site has not been validated
30
+ # @raise InvalidTemplateSpecification if the template path is not a directory or has no geb.config.yml file
31
+ # @raise InvalidTemplateSpecification if the resolved template paths are empty
32
+ # @raise InvalidTemplateSpecification if the site cannot be loaded from the template path
33
+ # @raise InvalidTemplateSpecification if the resolved template paths cannot be copied to the site path
34
+ def copy_template_from_path
35
+
36
+ # raise an error if the site has not been validated
37
+ raise UnvalidatedSiteAndTemplate.new unless @validated
38
+
39
+ # check if the template path is a directory
40
+ raise InvalidTemplateSpecification.new("Template path [#{@template_path}] is not a directory") unless File.directory?(@template_path)
41
+
42
+ # check if the template path has a geb.config.yml file
43
+ raise InvalidTemplateSpecification.new("Template path [#{@template_path}] has no geb.config.yml file") unless Geb::Config.site_directory_has_config?(@template_path)
44
+
45
+ # create a site for the template, load it and get the site configuration
46
+ Geb.log_start "Loading template site from path #{@template_path} ... "
47
+ template_site = Geb::Site.new
48
+ Geb.no_log { template_site.load(@template_path) } # suppress logging for loading the template site
49
+ Geb.log "done."
50
+
51
+ # resolve template paths to directories and files to be copied
52
+ Geb.log_start "Resolving directories and files from template site to copy ... "
53
+ resolved_template_paths = template_site.site_config.template_paths.flat_map do |template_file_path|
54
+ Dir.glob(File.join(template_site.site_path, template_file_path))
55
+ end
56
+ Geb.log "done. Found #{resolved_template_paths.count} directories and files."
57
+
58
+ # if the resolved template paths are empty, raise an error
59
+ raise InvalidTemplateSpecification.new("Failed to load site from [#{template_site.site_path}]") if resolved_template_paths.empty?
60
+
61
+ # copy the resolved template paths to the site path
62
+ Geb.copy_paths_to_directory(template_site.site_path, resolved_template_paths, @site_path)
63
+
64
+ end # def copy_template_from_path
65
+
66
+ # bundle the site as a template archive
67
+ # @raise SiteNotFoundError if the site is not loaded
68
+ # @raise InvalidTemplateSpecification if the template paths are not specified
69
+ # @raise InvalidTemplateSpecification if the resolved template paths are empty
70
+ # @raise InvalidTemplateSpecification if the template archive cannot be created
71
+ def bundle_template
72
+
73
+ # raise an error if the site is not loaded
74
+ raise Geb::Site::SiteNotFoundError.new("Site not loaded") unless @loaded
75
+
76
+ # resolve template paths to directories and files to be copied
77
+ Geb.log_start "Resolving directories and files to include in the template archive ... "
78
+ resolved_template_paths = @site_config.template_paths.flat_map do |template_file_path|
79
+ Dir.glob(File.join(@site_path, template_file_path))
80
+ end
81
+ Geb.log "done. Found #{resolved_template_paths.count} directories and files."
82
+
83
+ # if the resolved template paths are empty, raise an error
84
+ raise InvalidTemplateSpecification.new("Config template_paths not specified.") if resolved_template_paths.empty?
85
+
86
+ # create a temporary directory for the site template
87
+ tmp_archive_directory = Dir.mktmpdir
88
+
89
+ # copy the resolved paths to the temporary directory
90
+ Geb.log "Copying directories and files to the template archive directory #{tmp_archive_directory}"
91
+ Geb.copy_paths_to_directory(@site_path, resolved_template_paths, tmp_archive_directory)
92
+ Geb.log "Done copying directories and files to the template archive directory."
93
+
94
+ # create a template archive with files from the temporary directory into the release directory
95
+ output_archive_filename = File.join(@site_path, @site_config.output_dir, Geb::Defaults::RELEASE_OUTPUT_DIR, Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME)
96
+ Geb.log_start "Creating template archive in [#{output_archive_filename}] ... "
97
+ Open3.capture3("tar", "-czvf", output_archive_filename, "-C", tmp_archive_directory, ".")
98
+ Geb.log "done."
99
+
100
+ end # def bundle_template
101
+
102
+ # validate the template URL. It checks if the URL is accessible and is a tar.gz file.
103
+ # if the URL is not accessible, it tries to find the template by appending TEMPLATE_ARCHIVE_FILENAME
104
+ # this is to facilitate specifiying a top level URL. The method returns the URL if it is valid.
105
+ # @param template_url [String] the URL to the template
106
+ # @raise InvalidTemplateURL if the URL is not accessible or is not a tar.gz file
107
+ # @return [String] the validated template URL
108
+ def validate_template_url(template_url)
109
+
110
+ # get the HTTP response for the template URL
111
+ Geb.log_start "Validating template URL #{template_url} ... "
112
+ response = fetch_http_response(template_url)
113
+ Geb.log "done."
114
+
115
+ # check if the URL is accessible and is a tar.gz file, if not, try to find by appending TEMPLATE_ARCHIVE_FILENAME
116
+ unless response.is_a?(Net::HTTPSuccess) && Geb::Defaults::HTTP_TEMPLATE_CONTENT_TYPES.include?(response['Content-Type'])
117
+
118
+ # check if the URL already has the TEMPLATE_ARCHIVE_FILENAME appended, if not, append it and try again
119
+ unless template_url.end_with?(Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME)
120
+
121
+ # add TEMPLATE_ARCHIVE_FILENAME to the URL (handle trailing slashes)
122
+ template_url += '/' unless template_url.end_with?('/')
123
+ template_url += Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME
124
+
125
+ Geb.log ("Failed. Web server returned #{response.code}, trying to re-try with url #{template_url}") unless response.is_a?(Net::HTTPSuccess)
126
+ Geb.log ("Specified template is not a gzip archive, trying to re-try with url #{template_url}") unless Geb::Defaults::HTTP_TEMPLATE_CONTENT_TYPES.include?(response['Content-Type'])
127
+ Geb.log_start ("Trying to find geb template using URL #{template_url} ... ");
128
+
129
+ # get the HTTP response for the template URL (now modified to include the archive filename)
130
+ response = fetch_http_response(template_url)
131
+
132
+ end # unless
133
+
134
+ end # unless
135
+
136
+ # raise an error if the URL is not accessible and is not a tar.gz file
137
+ raise InvalidTemplateURL.new("Web server returned #{response.code}") unless response.is_a?(Net::HTTPSuccess)
138
+ raise InvalidTemplateURL.new("Specified template is not a gzip archive") unless Geb::Defaults::HTTP_TEMPLATE_CONTENT_TYPES.include?(response['Content-Type'])
139
+
140
+ Geb::log("Found a gzip archive at template url #{template_url}.");
141
+
142
+ return template_url
143
+
144
+ end # validate_template_url
145
+
146
+ # download the template into a temporary directory from the URl and extract it,
147
+ # return the path to the extracted template
148
+ # @param template_url [String] the URL to the template
149
+ # @raise InvalidTemplateURL if the template extraction fails
150
+ # @return [String] the path to the extracted template
151
+ def download_template_from_url(template_url)
152
+
153
+ # create a temporary directory
154
+ tmp_dir = Dir.mktmpdir
155
+
156
+ Geb.log_start "Downloading template from URL #{template_url} ... "
157
+
158
+ # download the template archive
159
+ File.open("#{tmp_dir}/#{Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME}", "wb") do |file|
160
+ file.write(Net::HTTP.get(URI.parse(template_url)))
161
+ end
162
+
163
+ # extract the template archive using Open3
164
+ _, stderr, status = Open3.capture3("tar", "-xzf", "#{tmp_dir}/#{Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME}", "-C", tmp_dir)
165
+
166
+ # raise an error if the template extraction failed or the geb config file is not present
167
+ raise InvalidTemplateURL.new("Failed to extract template archive: #{stderr}") unless status.success?
168
+ raise InvalidTemplateURL.new("Invalid template archive") unless File.exist?("#{tmp_dir}/geb.config.yml")
169
+
170
+ Geb.log "done. Extracted template to #{tmp_dir}."
171
+
172
+ # return the path to the extracted template
173
+ return tmp_dir
174
+
175
+ end # def ownload_template_from_url
176
+
177
+ # fetch the HTTP response for the URL
178
+ # @param url [String] the URL to fetch
179
+ # @raise InvalidTemplateURL if the URL is not accessible
180
+ # @return [Net::HTTPResponse] the HTTP response
181
+ def fetch_http_response(url)
182
+ return_response = nil
183
+ begin
184
+ return_response = Net::HTTP.get_response(URI.parse(url))
185
+ rescue StandardError => e
186
+ raise InvalidTemplateURL.new("HTTP error: #{e.message}")
187
+ end # begin
188
+ return return_response
189
+ end # def fetch_http_response
190
+
191
+ # check if the template directory exists
192
+ def template_directory_exists?(template_path)
193
+ File.directory?(template_path)
194
+ end # def template_directory_exists?
195
+
196
+ # check if the URL is a valid URL
197
+ def is_url?(url)
198
+ url =~ URI::DEFAULT_PARSER.make_regexp
199
+ end # def is_url?
200
+
201
+ # check if the template is a bundled template
202
+ def is_bundled_template?(template)
203
+ Geb::Defaults::AVAILABLE_TEMPLATES.include?(template)
204
+ end # def is_bundled_template?
205
+
206
+ end # module Template
207
+ end # class Site
208
+ end # module Geb
data/lib/geb/site.rb ADDED
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Represents a site object, handles creation, building and management of sites.
4
+ #
5
+ # @title Geb - Site
6
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
7
+ # @copyright 2024 Edin Mustajbegovic
8
+ # @license MIT
9
+ #
10
+ # @see https://github.com/mainfram-work/geb for more information
11
+
12
+ # include the required libraries
13
+ require 'uri'
14
+ require 'net/http'
15
+ require 'tmpdir'
16
+ require 'open3'
17
+ require 'shellwords'
18
+
19
+ # include site modules
20
+ require_relative 'site/core'
21
+ require_relative 'site/build'
22
+ require_relative 'site/release'
23
+ require_relative 'site/template'
24
+ require_relative 'site/remote'
25
+
26
+ module Geb
27
+ class Site
28
+
29
+ # include site sub-modules
30
+ include Geb::Site::Core # core site functionality, loading, validation and creation
31
+ include Geb::Site::Build # building the site, pages, page templates, partials and assets
32
+ include Geb::Site::Release # releasing the site, pages and assets
33
+ include Geb::Site::Template # mostly remote template handling
34
+ include Geb::Site::Remote # remote site functionality, ssh, scp, rsync, etc.
35
+
36
+ # @!attribute [r] site_path
37
+ # @return [String] the site path
38
+ attr_reader :site_path
39
+
40
+ # @!attribute [r] site_config
41
+ # @return [Geb::Config] the site configuration
42
+ attr_reader :site_config
43
+
44
+ # @!attribute [r] template_path
45
+ # @return [String] the path template the site is based on
46
+ attr_reader :template_path
47
+
48
+ # @!attribute [r] validated
49
+ # @return [Boolean] true if the site is validated
50
+ attr_reader :validated
51
+
52
+ # @!attribute [r] loaded
53
+ # @return [Boolean] true if the site is loaded
54
+ attr_reader :loaded
55
+
56
+ # @!attribute [r] pages
57
+ # @return [Hash] the site pages to process
58
+ attr_reader :pages
59
+
60
+ # site constructor
61
+ # initializes the site object and attributes
62
+ def initialize
63
+
64
+ @validated = false
65
+ @loaded = false
66
+ @site_path = nil
67
+ @template_path = nil
68
+ @pages = {}
69
+
70
+ end # def initialize
71
+
72
+ private
73
+
74
+ # get the site name. It either uses the configured name or the last folder in
75
+ # the site_path as site name.
76
+ # @return [String] the site name
77
+ def site_name
78
+ return @site_config.site_name
79
+ end # def site_name
80
+
81
+ end # class Site
82
+
83
+ end # module Geb
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Represents a page template. A page template is a page file that contains
4
+ # template definitions and insert sections. The class keeps a cache of
5
+ # already loaded page templates.
6
+ #
7
+ # @title Geb - Template
8
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
9
+ # @copyright 2024 Edin Mustajbegovic
10
+ # @license MIT
11
+ #
12
+ # @todo Make template, section and insert patterns configurable
13
+ #
14
+ # @see https://github.com/mainfram-work/geb for more information
15
+
16
+ require 'fileutils'
17
+
18
+ module Geb
19
+ class Template
20
+
21
+ TEMPLATE_PATTERN = /<% template: (.*?) %>/
22
+ SECTION_PATTERN = /<% start: (.*?) %>(.*?)<% end: \1 %>/m
23
+ INSERT_PATTERN = /<%= insert: (.*?) %>/
24
+
25
+ class TemplateFileNotFound < Geb::Error
26
+ MESSAGE = "Template file not found".freeze
27
+ def initialize(e = ""); super(e, MESSAGE); end
28
+ end # class TemplateFileNotFound < Geb::Error
29
+
30
+ class TemplateFileReadFailure < Geb::Error
31
+ MESSAGE = "Failed to read the template file.".freeze
32
+ def initialize(e = ""); super(e, MESSAGE); end
33
+ end # class TemplateFileReadFailure < Geb::Error
34
+
35
+ # define a class level cache for loaded template objects
36
+ @@loaded_templates = {}
37
+
38
+ # class method to expire the template cache
39
+ def self.expire_cache
40
+ @@loaded_templates = {}
41
+ end # def self.expire_cache
42
+
43
+ # class method to initialise a template if it is not already loaded, otherwise, return the cached template
44
+ # @param template_path [String] the path to the template file
45
+ # @return [Geb::Template] the template object
46
+ def self.load(template_path)
47
+
48
+ # initialise a return template object
49
+ return_template = nil
50
+
51
+ # check if the template is already loaded
52
+ if @@loaded_templates.key?(template_path)
53
+
54
+ # return the cached template
55
+ Geb.log " - using cached template: #{template_path}"
56
+ return_template = @@loaded_templates[template_path]
57
+
58
+ else
59
+
60
+ # create a new template object
61
+ return_template = Template.new(template_path)
62
+
63
+ # add the template to the cache
64
+ @@loaded_templates[template_path] = return_template
65
+
66
+ end # if else
67
+
68
+ # return the new template object
69
+ return return_template
70
+
71
+ end # def self.load
72
+
73
+ # extract the template path from the page content
74
+ # @param page_content [String] the page content
75
+ # @return [String] the template path, or nil if no template path is found
76
+ # @note the function looks for tags like this <% template: shared/templates/_site.html %> in the page content
77
+ def self.extract_template_path(page_content)
78
+
79
+ # match the template pattern and return the template path
80
+ match = page_content.match(TEMPLATE_PATTERN)
81
+
82
+ # return the template path or nil if no match
83
+ return match ? match[1].strip : nil
84
+
85
+ end # def self.extract_template_path
86
+
87
+ # extract the sections for the template from the page content
88
+ # @param page_content [String] the page content
89
+ # @return [Hash] the sections for the template, key is the section name, value is the section content
90
+ # @note the function looks for tags like this <% start: header %> ... <% end: header %> in the page content and returns whats in between
91
+ def self.extract_sections_for_template(page_content)
92
+
93
+ # initialise the sections for the template hash
94
+ sections_for_template = {}
95
+
96
+ # scan the page content for sections and add them to the hash
97
+ page_content.scan(SECTION_PATTERN).each do |section|
98
+ sections_for_template[section[0].strip] = section[1].strip
99
+ end # scan
100
+
101
+ # return the sections for the template
102
+ return sections_for_template
103
+
104
+ end # def self.extract_sections_for_template
105
+
106
+ # @!attribute [r] path
107
+ # @return [String] the path to the template file
108
+ attr_reader :path
109
+
110
+ # @!attribute [r] content
111
+ # @return [String] the content of the template file
112
+ attr_reader :content
113
+
114
+ # Template class constructor
115
+ # @param template_path [String] the path to the template file
116
+ # @return [Geb::Template] the template object
117
+ def initialize(template_path)
118
+
119
+ # set the specified template path and initialise the content
120
+ @path = template_path
121
+ @content = nil
122
+
123
+ # check if the template file exists
124
+ raise TemplateFileNotFound.new(template_path) unless template_file_exists?()
125
+
126
+ Geb.log " - loading template: #{@path}"
127
+
128
+ # read the template file, raise an error if the file could not be read
129
+ begin
130
+ @content = File.read(template_path)
131
+ rescue => e
132
+ raise TemplateFileReadFailure.new(e.message)
133
+ end # begin
134
+
135
+ end # def initialize
136
+
137
+ # parse the page content sections and replace the sections in the template with the page section content
138
+ # @param page_content_sections [Hash] the page content sections, key is the section name, value is the section content
139
+ # @return [String] the parsed template content
140
+ # @note the function looks for tags like this <%= insert: header %> in the template content
141
+ def parse(page_content_sections)
142
+
143
+ # create a duplicate of the template content
144
+ return_content = @content.dup
145
+
146
+ # step through the page content sections and replace the insert sections in the template with the page content
147
+ return_content.gsub!(INSERT_PATTERN) do |match|
148
+ section_name = match.match(INSERT_PATTERN)[1].strip
149
+ page_content_sections[section_name] || match
150
+ end # return_content.gsub!
151
+
152
+ # return the content
153
+ return return_content
154
+
155
+ end # def parse
156
+
157
+ private
158
+
159
+ # check if the template file exists
160
+ # @return [Boolean] true if the template file exists, otherwise false
161
+ def template_file_exists?
162
+ return File.exist?(@path)
163
+ end # def template_file_exists?
164
+
165
+ end # class Template
166
+ end # module Geb
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # A simple list of Geb utilities for the Geb gem
4
+ #
5
+ # @title Geb - Utilities
6
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
7
+ # @copyright 2024 Edin Mustajbegovic
8
+ # @license MIT
9
+ #
10
+ # @see https://github.com/mainfram-work/geb for more information
11
+
12
+ module Geb
13
+
14
+ # define the main error class
15
+ class Error < StandardError
16
+
17
+ # initialize the error class
18
+ # @param custom_error [String] the custom error message
19
+ # @param default_message [String] the default error message
20
+ # @return [Geb::Error] the error object
21
+ # @raise [Geb::Error] the error object
22
+ def initialize(custom_error = "", default_message = "")
23
+
24
+ # set the error message
25
+ message = custom_error.empty? ? default_message : "#{custom_error} #{default_message}"
26
+
27
+ # call the parent class constructor
28
+ super(message)
29
+
30
+ end # def initialize
31
+
32
+ end # class Error < StandardError
33
+
34
+ # initialise a flag for suppressing output
35
+ @@suppress_log_output = false
36
+
37
+ # log method for printing messages to the console
38
+ # @param message [String] the message to print
39
+ # @note this method will print a newline character at the end of the message
40
+ # @note this method will print the message to the console if the suppress log output flag is not set
41
+ def self.log(message)
42
+ puts message unless @@suppress_log_output
43
+ end # def self.log
44
+
45
+ # log method for printing messages to the console
46
+ # @param message [String] the message to print
47
+ # @note this method will not print a newline character at the end of the message
48
+ # @note this method will print the message to the console if the suppress log output flag is not set
49
+ def self.log_start(message)
50
+ print message unless @@suppress_log_output
51
+ end # def self.log
52
+
53
+ # method to suppress log output within a block
54
+ # @return [Object] the return value of the block
55
+ # @yield the block to suppress log output
56
+ def self.no_log
57
+
58
+ # store the original value of the suppress log output flag (just in case)
59
+ original_value = @@suppress_log_output
60
+
61
+ # set the suppress log output flag
62
+ @@suppress_log_output = true
63
+
64
+ # yield the block
65
+ yield
66
+
67
+ ensure
68
+
69
+ # reset the suppress log output flag
70
+ @@suppress_log_output = original_value
71
+
72
+ end # def self.no_log
73
+
74
+ # copy the specified file and directory paths to the destination directory
75
+ # @param source_path [String] the source directory
76
+ # @param paths [Array] the paths to copy (still full paths, but from source directory)
77
+ # @param destination_path [String] the destination directory
78
+ # @param quiet [Boolean] the flag to suppress output
79
+ # @raise [Geb::Error] if the any of the file operations fail
80
+ def self.copy_paths_to_directory(source_path, paths, destination_path, quiet = false)
81
+
82
+ # step through the resolved template paths and copy them to the site path, taking care of files vs directories
83
+ paths.each do |path|
84
+
85
+ # get the relative path of the resolved template path and build the destination path
86
+ relative_template_path = path.gsub(source_path, "")
87
+ destination_file_path = File.join(destination_path, relative_template_path)
88
+
89
+ # ensure the destination directory exists
90
+ FileUtils.mkdir_p(File.dirname(destination_path))
91
+
92
+ # copy the resolved template path to the destination path
93
+ if File.directory?(path)
94
+ Geb.log_start " - copying directory and all sub-directories from #{path} to #{destination_file_path} ... " unless quiet
95
+ FileUtils.cp_r(path, destination_file_path)
96
+ else
97
+ Geb.log_start " - copying file from #{path} to #{destination_file_path} ... " unless quiet
98
+ FileUtils.cp(path, destination_file_path)
99
+ end # if else
100
+ Geb.log "done." unless quiet
101
+
102
+ end # each
103
+
104
+ rescue Exception => e
105
+ raise Geb::Error.new("Failed to copy paths to directory: #{e.message}")
106
+
107
+ end # def self.copy_paths_to_directory
108
+
109
+
110
+ end # module Geb
data/lib/geb.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Main module definition for the Geb gem, it includes all the functionality
4
+ # and modules for the Geb gem.
5
+ #
6
+ # @title Geb
7
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
8
+ # @copyright 2024 Edin Mustajbegovic
9
+ # @license MIT
10
+ #
11
+ # @see https://github.com/mainfram-work/geb for more information
12
+
13
+ # Define the main module, version and main error class
14
+ module Geb
15
+
16
+ # define the version of the gem
17
+ VERSION = "0.1.11"
18
+
19
+ end # module Geb
20
+
21
+ # include external libraries
22
+ require "dry/cli"
23
+
24
+ # include geb libraries
25
+ require_relative "geb/defaults"
26
+ require_relative "geb/utilities"
27
+ require_relative "geb/config"
28
+ require_relative "geb/git"
29
+ require_relative "geb/site"
30
+ require_relative "geb/page"
31
+ require_relative "geb/template"
32
+ require_relative "geb/partial"
33
+ require_relative "geb/server"
34
+
35
+ # make sure geb/cli is loaded last
36
+ require_relative "geb/cli"
data/lib/seth.rb ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Module with some pretty crazy things.
4
+ # Seth is an egyption god of chaos and mischief. This code is a tribute to him.
5
+ # Some of the thigns here run at the very bassis of the Ruby language and change
6
+ # how standard things work. This is not for the faint of heart.
7
+ #
8
+ # @title Geb
9
+ # @author Edin Mustajbegovic <edin@actiontwelve.com>
10
+ # @copyright 2024 Edin Mustajbegovic
11
+ # @license MIT
12
+ #
13
+ # @see https://github.com/mainfram-work/geb for more information
14
+
15
+ module Seth
16
+
17
+ # Suppress warnings within the block being executed. This is useful for testing.
18
+ # @example
19
+ #
20
+ # suppress_warnings do
21
+ # # code that generates warnings
22
+ # end
23
+ # # no warnings will be printed
24
+ #
25
+ # @yield []
26
+ # @return [void]
27
+ def suppress_warnings
28
+
29
+ # save the original verbose setting
30
+ original_verbose = $VERBOSE
31
+
32
+ # suppress warnings
33
+ $VERBOSE = nil
34
+
35
+ # execute the block
36
+ yield
37
+
38
+ ensure
39
+
40
+ # restore the original verbose setting
41
+ $VERBOSE = original_verbose
42
+
43
+ end # def suppress_warnings
44
+
45
+ # can you imagine what this does? ;)
46
+ module_function :suppress_warnings
47
+
48
+ end # module Seth
49
+
50
+ puts "Seth has been loaded. Hang on to your hats!. Seth is here: #{__FILE__}"
data/sig/geb.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Geb
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end