site_maps 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +45 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +36 -0
  6. data/.tool-versions +1 -0
  7. data/CHANGELOG.md +8 -0
  8. data/Gemfile +18 -0
  9. data/Gemfile.lock +134 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +186 -0
  12. data/Rakefile +4 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +7 -0
  15. data/exec/site_maps +9 -0
  16. data/lib/site-maps.rb +3 -0
  17. data/lib/site_maps/adapters/adapter.rb +80 -0
  18. data/lib/site_maps/adapters/aws_sdk/config.rb +51 -0
  19. data/lib/site_maps/adapters/aws_sdk/location.rb +9 -0
  20. data/lib/site_maps/adapters/aws_sdk/storage.rb +52 -0
  21. data/lib/site_maps/adapters/aws_sdk.rb +31 -0
  22. data/lib/site_maps/adapters/file_system/config.rb +5 -0
  23. data/lib/site_maps/adapters/file_system/location.rb +35 -0
  24. data/lib/site_maps/adapters/file_system/storage.rb +61 -0
  25. data/lib/site_maps/adapters/file_system.rb +26 -0
  26. data/lib/site_maps/adapters/noop.rb +18 -0
  27. data/lib/site_maps/atomic_repository.rb +24 -0
  28. data/lib/site_maps/builder/link.rb +27 -0
  29. data/lib/site_maps/builder/normalizer.rb +48 -0
  30. data/lib/site_maps/builder/sitemap_index/item.rb +35 -0
  31. data/lib/site_maps/builder/sitemap_index.rb +40 -0
  32. data/lib/site_maps/builder/url.rb +152 -0
  33. data/lib/site_maps/builder/url_set.rb +92 -0
  34. data/lib/site_maps/cli.rb +68 -0
  35. data/lib/site_maps/configuration.rb +119 -0
  36. data/lib/site_maps/incremental_location.rb +62 -0
  37. data/lib/site_maps/notification/bus.rb +90 -0
  38. data/lib/site_maps/notification/event.rb +50 -0
  39. data/lib/site_maps/notification/publisher.rb +78 -0
  40. data/lib/site_maps/notification.rb +36 -0
  41. data/lib/site_maps/primitives/array.rb +15 -0
  42. data/lib/site_maps/primitives/output.rb +66 -0
  43. data/lib/site_maps/primitives/string.rb +43 -0
  44. data/lib/site_maps/process.rb +29 -0
  45. data/lib/site_maps/railtie.rb +18 -0
  46. data/lib/site_maps/runner/event_listener.rb +78 -0
  47. data/lib/site_maps/runner.rb +136 -0
  48. data/lib/site_maps/sitemap_builder.rb +75 -0
  49. data/lib/site_maps/sitemap_reader.rb +56 -0
  50. data/lib/site_maps/version.rb +5 -0
  51. data/lib/site_maps.rb +112 -0
  52. data/site_maps.gemspec +44 -0
  53. metadata +172 -0
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SiteMaps
4
+ module Runner::EventListener
5
+ extend Primitives::Output
6
+
7
+ module_function
8
+
9
+ def [](event_name)
10
+ method_name = Primitives::String.new(event_name).underscore.to_sym
11
+ return unless respond_to?(:"on_#{method_name}")
12
+
13
+ method(:"on_#{method_name}")
14
+ end
15
+
16
+ def on_sitemaps_runner_enqueue_process(event)
17
+ process = event[:process]
18
+ kwargs = event[:kwargs]
19
+ location = process.location(**kwargs)
20
+ print_message(
21
+ "Enqueue process %<name>s#{" at %<location>s" if location}",
22
+ name: colorize(process.name, :bold),
23
+ location: colorize(location, :lightgray)
24
+ )
25
+ if kwargs.any?
26
+ print_message("└── Keyword Arguments: {%<kwargs>s}", kwargs: kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", "))
27
+ end
28
+ end
29
+
30
+ def on_sitemaps_runner_before_process_execution(event)
31
+ process = event[:process]
32
+ kwargs = event[:kwargs]
33
+ location = process.location(**kwargs)
34
+ print_message(
35
+ "Executing process %<name>s#{" at %<location>s" if location}",
36
+ name: colorize(process.name, :bold),
37
+ location: colorize(location, :lightgray)
38
+ )
39
+ if kwargs.any?
40
+ print_message("└── Keyword Arguments: {%<kwargs>s}", kwargs: kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", "))
41
+ end
42
+ end
43
+
44
+ def on_sitemaps_runner_process_execution(event)
45
+ process = event[:process]
46
+ kwargs = event[:kwargs]
47
+ location = process.location(**kwargs)
48
+ print_message(
49
+ "[%<runtime>s] Executed process %<name>s#{" at %<location>s" if location}",
50
+ name: colorize(process.name, :bold),
51
+ location: colorize(location, :lightgray),
52
+ runtime: formatted_runtime(event[:runtime])
53
+ )
54
+ if kwargs.any?
55
+ print_message("└── Keyword Arguments: {%<kwargs>s}", kwargs: kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", "))
56
+ end
57
+ end
58
+
59
+ def on_sitemaps_builder_finalize_urlset(event)
60
+ links_count = event[:links_count]
61
+ news_count = event[:news_count]
62
+ url = event[:url]
63
+ text = +"[%<runtime>s] Finalize URLSet with "
64
+ text << "%<links>d links" if links_count > 0
65
+ text << " and " if links_count > 0 && news_count > 0
66
+ text << "%<news>d news" if news_count > 0
67
+ text << " URLs at %<url>s"
68
+
69
+ print_message(
70
+ text,
71
+ links: links_count,
72
+ news: news_count,
73
+ url: colorize(url, :lightgray),
74
+ runtime: formatted_runtime(event[:runtime])
75
+ )
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SiteMaps
4
+ class Runner
5
+ attr_reader :adapter
6
+
7
+ def initialize(adapter = SiteMaps.current_adapter, max_threads: 4)
8
+ @adapter = adapter.tap(&:reset!)
9
+ @pool = Concurrent::FixedThreadPool.new(max_threads)
10
+ @execution = Concurrent::Hash.new
11
+ @failed = Concurrent::AtomicBoolean.new(false)
12
+ @errors = Concurrent::Array.new
13
+ end
14
+
15
+ def enqueue(process_name = :default, **kwargs)
16
+ process_name = process_name.to_sym
17
+ process = @adapter.processes.fetch(process_name) do
18
+ raise ArgumentError, "Process :#{process_name} not found"
19
+ end
20
+ kwargs = process.keyword_arguments(kwargs)
21
+ SiteMaps::Notification.instrument("sitemaps.runner.enqueue_process") do |payload|
22
+ payload[:process] = process
23
+ payload[:kwargs] = kwargs
24
+ if process.dynamic?
25
+ @execution[process_name] ||= Concurrent::Array.new
26
+ if @execution[process_name].any? { |_, k| k == kwargs }
27
+ raise ArgumentError, "Process :#{process_name} already enqueued with kwargs #{kwargs}"
28
+ end
29
+ @execution[process_name] << [process, kwargs]
30
+ else
31
+ if @execution.key?(process_name)
32
+ raise ArgumentError, "Process :#{process_name} already defined"
33
+ end
34
+ @execution[process_name] = Concurrent::Array.new([[process, kwargs]])
35
+ end
36
+ end
37
+ self
38
+ end
39
+
40
+ def enqueue_remaining
41
+ @adapter.processes.each_key do |process_name|
42
+ next if @execution.key?(process_name)
43
+
44
+ enqueue(process_name)
45
+ end
46
+ self
47
+ end
48
+ alias_method :enqueue_all, :enqueue_remaining
49
+
50
+ def run
51
+ may_preload_sitemap_index
52
+
53
+ futures = []
54
+ @execution.each do |_process_name, items|
55
+ items.each do |process, kwargs|
56
+ SiteMaps::Notification.publish("sitemaps.runner.before_process_execution", process: process, kwargs: kwargs)
57
+ futures << Concurrent::Future.execute(executor: pool) do
58
+ wrap_process_execution(process) do
59
+ SiteMaps::Notification.instrument("sitemaps.runner.process_execution") do |payload|
60
+ payload[:process] = process
61
+ payload[:kwargs] = kwargs
62
+ builder = SiteMaps::SitemapBuilder.new(
63
+ adapter: adapter,
64
+ location: process.location(**kwargs)
65
+ )
66
+ process.call(builder, **kwargs)
67
+ builder.finalize!
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ futures.each(&:wait)
75
+ failed.false? ? finalize! : fail_with_errors!
76
+ ensure
77
+ pool.shutdown
78
+ pool.wait_for_termination
79
+ @execution.clear
80
+ end
81
+
82
+ private
83
+
84
+ attr_reader :pool, :failed, :errors
85
+
86
+ def finalize!
87
+ adapter.repo.remaining_index_links.each do |item|
88
+ adapter.sitemap_index.add(item)
89
+ end
90
+ unless adapter.sitemap_index.empty?
91
+ raw_data = adapter.sitemap_index.to_xml
92
+ adapter.write(adapter.config.url, raw_data, last_modified: Time.now)
93
+ end
94
+ end
95
+
96
+ def fail_with_errors!
97
+ return if errors.empty?
98
+
99
+ raise errors.first.last
100
+ end
101
+
102
+ def handle_process_error(process, error)
103
+ errors << [process, error]
104
+ end
105
+
106
+ def may_preload_sitemap_index
107
+ return unless preload_sitemap_index_links?
108
+
109
+ adapter.fetch_sitemap_index_links.each do |item|
110
+ adapter.repo.preloaded_index_links.push(item)
111
+ end
112
+ end
113
+
114
+ def wrap_process_execution(process)
115
+ return if failed.true?
116
+
117
+ yield
118
+ rescue => e
119
+ handle_process_error(process, e)
120
+ failed.make_true
121
+ end
122
+
123
+ def preload_sitemap_index_links?
124
+ return false if @execution.empty?
125
+
126
+ (@adapter.processes.keys - @execution.keys).any? || # There are processes that have not been enqueued
127
+ @adapter.processes.any? { |_, process| process.dynamic? } # There are dynamic processes
128
+ end
129
+
130
+ # def single_thread?
131
+ # @pool.max_length == 1 || (
132
+ # @execution.size == 1 && @execution.first.last.size == 1
133
+ # )
134
+ # end
135
+ end
136
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SiteMaps
4
+ class SitemapBuilder
5
+ extend Forwardable
6
+
7
+ def initialize(adapter:, location: nil)
8
+ @adapter = adapter
9
+ @url_set = SiteMaps::Builder::URLSet.new
10
+ @location = location
11
+ @mutex = Mutex.new
12
+ end
13
+
14
+ def add(path, params: nil, **options)
15
+ @mutex.synchronize do
16
+ link = build_link(path, params)
17
+ url_set.add(link, **options)
18
+ rescue SiteMaps::FullSitemapError
19
+ finalize_and_start_next_urlset!
20
+ url_set.add(link, **options)
21
+ end
22
+ end
23
+
24
+ def add_sitemap_index(url, lastmod: Time.now)
25
+ sitemap_index.add(url, lastmod: lastmod)
26
+ end
27
+
28
+ def finalize!
29
+ return if url_set.finalized?
30
+ return if url_set.empty?
31
+
32
+ raw_data = url_set.finalize!
33
+
34
+ SiteMaps::Notification.instrument("sitemaps.builder.finalize_urlset") do |payload|
35
+ payload[:links_count] = url_set.links_count
36
+ payload[:news_count] = url_set.news_count
37
+ payload[:last_modified] = url_set.last_modified
38
+
39
+ if adapter.maybe_inline_urlset? && sitemap_index.empty?
40
+ payload[:url] = repo.main_url
41
+ adapter.write(repo.main_url, raw_data, last_modified: url_set.last_modified)
42
+ else
43
+ sitemap_url = repo.generate_url(location)
44
+ payload[:url] = sitemap_url
45
+ adapter.write(sitemap_url, raw_data, last_modified: url_set.last_modified)
46
+ add_sitemap_index(sitemap_url, lastmod: url_set.last_modified)
47
+ end
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ attr_reader :url_set, :adapter, :location
54
+
55
+ def_delegators :adapter, :sitemap_index, :config, :repo
56
+
57
+ def finalize_and_start_next_urlset!
58
+ raw_data = url_set.finalize!
59
+ SiteMaps::Notification.instrument("sitemaps.builder.finalize_urlset") do |payload|
60
+ sitemap_url = repo.generate_url(location)
61
+ payload[:url] = sitemap_url
62
+ payload[:links_count] = url_set.links_count
63
+ payload[:news_count] = url_set.news_count
64
+ payload[:last_modified] = url_set.last_modified
65
+ adapter.write(sitemap_url, raw_data, last_modified: url_set.last_modified)
66
+ add_sitemap_index(sitemap_url, lastmod: url_set.last_modified)
67
+ end
68
+ @url_set = SiteMaps::Builder::URLSet.new
69
+ end
70
+
71
+ def build_link(path, params)
72
+ SiteMaps::Builder::Link.new(config.base_uri, path, params)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open-uri"
4
+
5
+ module SiteMaps
6
+ class SitemapReader
7
+ Error = Class.new(SiteMaps::Error)
8
+ FileNotFound = Class.new(Error)
9
+ MalformedFile = Class.new(Error)
10
+
11
+ def initialize(location)
12
+ @location = Pathname.new(location)
13
+ end
14
+
15
+ def read
16
+ if compressed?
17
+ Zlib::GzipReader.new(read_file).read
18
+ else
19
+ read_file.read
20
+ end
21
+ rescue Zlib::GzipFile::Error => _e
22
+ raise MalformedFile.new("The file #{@location} is not a valid Gzip file")
23
+ end
24
+
25
+ def to_doc
26
+ @doc ||= begin
27
+ require "nokogiri"
28
+ Nokogiri::XML(read)
29
+ end
30
+ rescue LoadError
31
+ raise SiteMaps::Error, "Nokogiri is required to parse the XML file"
32
+ end
33
+
34
+ protected
35
+
36
+ def read_file
37
+ if remote?
38
+ ::URI.parse(@location.to_s).open
39
+ else
40
+ ::File.open(@location, "r")
41
+ end
42
+ rescue Errno::ENOENT
43
+ raise FileNotFound.new("The file #{@location} does not exist")
44
+ rescue OpenURI::HTTPError
45
+ raise FileNotFound.new("The file #{@location} could not be opened")
46
+ end
47
+
48
+ def compressed?
49
+ @location.extname == ".gz"
50
+ end
51
+
52
+ def remote?
53
+ %r{^https?://}.match?(@location.to_s)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SiteMaps
4
+ VERSION = "0.0.1.beta1"
5
+ end
data/lib/site_maps.rb ADDED
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "site_maps/version"
4
+
5
+ require "builder"
6
+ require "concurrent-ruby"
7
+ require "date"
8
+ require "fileutils"
9
+ require "forwardable"
10
+ require "rack/utils"
11
+ require "stringio"
12
+ require "time"
13
+ require "uri"
14
+ require "zeitwerk"
15
+ require "zlib"
16
+
17
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
18
+ loader.ignore("#{__dir__}/site-maps.rb")
19
+ loader.ignore("#{__dir__}/site_maps/tasks.rb")
20
+ loader.ignore("#{__dir__}/site_maps/cli.rb")
21
+ loader.inflector.inflect "cli" => "CLI"
22
+ loader.inflector.inflect "dsl" => "DSL"
23
+ loader.inflector.inflect "url_set" => "URLSet"
24
+ loader.inflector.inflect "url" => "URL"
25
+ loader.inflector.inflect "xml" => "XML"
26
+ loader.log! if ENV["DEBUG_ZEITWERK"]
27
+ loader.setup
28
+
29
+ module SiteMaps
30
+ MAX_LENGTH = {
31
+ links: 50_000,
32
+ images: 1_000,
33
+ news: 1_000
34
+ }
35
+ MAX_FILESIZE = 50_000_000 # bytes
36
+ DEFAULT_LOGGER = Logger.new($stdout)
37
+
38
+ Error = Class.new(StandardError)
39
+ AdapterNotFound = Class.new(Error)
40
+ AdapterNotSetError = Class.new(Error)
41
+ FileNotFoundError = Class.new(Error)
42
+ FullSitemapError = Class.new(Error)
43
+ ConfigurationError = Class.new(Error)
44
+
45
+ class << self
46
+ attr_reader :current_adapter
47
+ attr_writer :logger
48
+
49
+ # @param adapter [Class, String, Symbol] The name of the adapter to use
50
+ # @param options [Hash] Options to pass to the adapter. Note that these are adapter-specific
51
+ # @param block [Proc] A block to pass to the adapter
52
+ # @return [Object] An instance of the adapter
53
+ def use(adapter, **options, &block)
54
+ adapter_class = if adapter.is_a?(Class) # && adapter < Adapters::Adapter
55
+ adapter
56
+ else
57
+ const_name = Primitives::String.new(adapter.to_s).classify
58
+ begin
59
+ Adapters.const_get(const_name)
60
+ rescue NameError
61
+ raise AdapterNotFound, "Adapter #{adapter.inspect} not found"
62
+ end
63
+ end
64
+ @current_adapter = adapter_class.new(**options, &block)
65
+ end
66
+
67
+ def config
68
+ @config ||= Configuration.new
69
+ yield(@config) if block_given?
70
+ @config
71
+ end
72
+ alias_method :configure, :config
73
+
74
+ # Load and prepare a runner with the current adapter
75
+ # Note that it won't start running until you call `#run` on the runner
76
+ #
77
+ # Example:
78
+ # SiteMaps.generate(config_file: "config/site_maps.rb", max_threads: 10)
79
+ # .enqueue_all
80
+ # .run
81
+ #
82
+ # You may also enqueue processes manually, specially those that are dynamic
83
+ #
84
+ # Example:
85
+ # SiteMaps.generate(config_file: "config/site_maps.rb", max_threads: 10)
86
+ # .enqueue(:monthly, year: 2020, month: 1)
87
+ # .enqueue(:monthly, year: 2020, month: 2)
88
+ # .enqueue_remaining # Enqueue all other non-enqueued processes
89
+ # .run
90
+ #
91
+ # @param config_file [String] The path to a configuration file
92
+ # @param options [Hash] Options to pass to the runner
93
+ # @return [Runner] An instance of the runner
94
+ def generate(config_file: nil, **options)
95
+ if config_file
96
+ @current_adapter = nil
97
+ load(config_file)
98
+ end
99
+ raise AdapterNotSetError, "No adapter set. Use SiteMaps.use to set an adapter" unless current_adapter
100
+
101
+ Runner.new(current_adapter, **options)
102
+ end
103
+
104
+ def logger
105
+ @logger ||= DEFAULT_LOGGER
106
+ end
107
+ end
108
+ end
109
+
110
+ if defined?(::Rails)
111
+ require_relative "site_maps/railtie"
112
+ end
data/site_maps.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/site_maps/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "site_maps"
7
+ spec.version = SiteMaps::VERSION
8
+ spec.authors = ["Marcos G. Zimmermann"]
9
+ spec.email = ["mgzmaster@gmail.com"]
10
+
11
+ spec.summary = <<~SUMMARY
12
+ SiteMaps simplifies the generation of sitemaps for ruby or rails applications.
13
+ SUMMARY
14
+ spec.description = <<~DESCRIPTION
15
+ SiteMaps is gem that provides a simple way to generate static sitemaps for your ruby or rails applications.
16
+ DESCRIPTION
17
+
18
+ spec.homepage = "https://github.com/marcosgz/site_maps"
19
+ spec.license = "MIT"
20
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
21
+
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes." unless spec.respond_to?(:metadata)
23
+
24
+ spec.metadata["homepage_uri"] = spec.homepage
25
+ spec.metadata["bug_tracker_uri"] = "https://github.com/marcosgz/site_maps/issues"
26
+ spec.metadata["documentation_uri"] = "https://github.com/marcosgz/site_maps"
27
+ spec.metadata["source_code_uri"] = "https://github.com/marcosgz/site_maps"
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+
35
+ spec.bindir = "exec"
36
+ spec.executables = spec.files.grep(%r{^exec/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_dependency "rack", ">= 2.0"
40
+ spec.add_dependency "zeitwerk", ">= 0"
41
+ spec.add_dependency "builder", "~> 3.0"
42
+ spec.add_dependency "concurrent-ruby", ">= 1.1"
43
+ spec.add_dependency "thor", ">= 0.0.0"
44
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: site_maps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Marcos G. Zimmermann
8
+ autorequire:
9
+ bindir: exec
10
+ cert_chain: []
11
+ date: 2024-11-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: builder
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: concurrent-ruby
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.0
83
+ description: 'SiteMaps is gem that provides a simple way to generate static sitemaps
84
+ for your ruby or rails applications.
85
+
86
+ '
87
+ email:
88
+ - mgzmaster@gmail.com
89
+ executables:
90
+ - site_maps
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".github/workflows/main.yml"
95
+ - ".gitignore"
96
+ - ".rspec"
97
+ - ".rubocop.yml"
98
+ - ".tool-versions"
99
+ - CHANGELOG.md
100
+ - Gemfile
101
+ - Gemfile.lock
102
+ - LICENSE.txt
103
+ - README.md
104
+ - Rakefile
105
+ - bin/console
106
+ - bin/setup
107
+ - exec/site_maps
108
+ - lib/site-maps.rb
109
+ - lib/site_maps.rb
110
+ - lib/site_maps/adapters/adapter.rb
111
+ - lib/site_maps/adapters/aws_sdk.rb
112
+ - lib/site_maps/adapters/aws_sdk/config.rb
113
+ - lib/site_maps/adapters/aws_sdk/location.rb
114
+ - lib/site_maps/adapters/aws_sdk/storage.rb
115
+ - lib/site_maps/adapters/file_system.rb
116
+ - lib/site_maps/adapters/file_system/config.rb
117
+ - lib/site_maps/adapters/file_system/location.rb
118
+ - lib/site_maps/adapters/file_system/storage.rb
119
+ - lib/site_maps/adapters/noop.rb
120
+ - lib/site_maps/atomic_repository.rb
121
+ - lib/site_maps/builder/link.rb
122
+ - lib/site_maps/builder/normalizer.rb
123
+ - lib/site_maps/builder/sitemap_index.rb
124
+ - lib/site_maps/builder/sitemap_index/item.rb
125
+ - lib/site_maps/builder/url.rb
126
+ - lib/site_maps/builder/url_set.rb
127
+ - lib/site_maps/cli.rb
128
+ - lib/site_maps/configuration.rb
129
+ - lib/site_maps/incremental_location.rb
130
+ - lib/site_maps/notification.rb
131
+ - lib/site_maps/notification/bus.rb
132
+ - lib/site_maps/notification/event.rb
133
+ - lib/site_maps/notification/publisher.rb
134
+ - lib/site_maps/primitives/array.rb
135
+ - lib/site_maps/primitives/output.rb
136
+ - lib/site_maps/primitives/string.rb
137
+ - lib/site_maps/process.rb
138
+ - lib/site_maps/railtie.rb
139
+ - lib/site_maps/runner.rb
140
+ - lib/site_maps/runner/event_listener.rb
141
+ - lib/site_maps/sitemap_builder.rb
142
+ - lib/site_maps/sitemap_reader.rb
143
+ - lib/site_maps/version.rb
144
+ - site_maps.gemspec
145
+ homepage: https://github.com/marcosgz/site_maps
146
+ licenses:
147
+ - MIT
148
+ metadata:
149
+ homepage_uri: https://github.com/marcosgz/site_maps
150
+ bug_tracker_uri: https://github.com/marcosgz/site_maps/issues
151
+ documentation_uri: https://github.com/marcosgz/site_maps
152
+ source_code_uri: https://github.com/marcosgz/site_maps
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 2.7.0
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubygems_version: 3.5.21
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: SiteMaps simplifies the generation of sitemaps for ruby or rails applications.
172
+ test_files: []