geb 0.1.11

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 (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