sitemap 0.1 → 0.2b

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.
@@ -43,10 +43,6 @@ You may change the defaults for either <tt>params</tt> or <tt>search</tt> option
43
43
 
44
44
  Sitemap.defaults[:params] = { :format => "html" }
45
45
 
46
- == Limitations
47
-
48
- Sitemaps can only have up to 50000 urls and an uncompressed size of 10MB. This issue will be resolved in a future release.
49
-
50
46
  == License
51
47
 
52
48
  This package is licensed under the MIT license and/or the Creative
@@ -9,6 +9,7 @@ require "singleton"
9
9
  require "builder"
10
10
  require "sitemap/railtie"
11
11
  require "sitemap/ping"
12
+ require "sitemap/store"
12
13
  require "sitemap/generator"
13
14
 
14
15
  module Sitemap
@@ -20,8 +21,12 @@ module Sitemap
20
21
  self.defaults = {
21
22
  :params => {},
22
23
  :search => {
23
- :updated_at => proc { |obj| obj.updated_at.strftime("%Y-%m-%d") if obj.respond_to?(:updated_at) }
24
- }
24
+ :updated_at => proc { |obj|
25
+ obj.updated_at.strftime("%Y-%m-%d") if obj.respond_to?(:updated_at)
26
+ }
27
+ },
28
+ :query_batch_size => 500,
29
+ :max_urls => 10000
25
30
  }
26
31
 
27
32
  end
@@ -10,13 +10,17 @@ module Sitemap
10
10
  :priority => "priority"
11
11
  }
12
12
 
13
- attr_accessor :entries, :host, :routes
13
+ attr_accessor :store, :host, :routes, :fragments
14
14
 
15
15
  # Instantiates a new object.
16
16
  # Should never be called directly.
17
17
  def initialize
18
18
  self.class.send(:include, Rails.application.routes.url_helpers)
19
- self.entries = []
19
+ self.fragments = []
20
+ self.store = Store.new(:max_entries => Sitemap.defaults[:max_urls])
21
+ self.store.before_reset do |entries|
22
+ self.process_fragment!
23
+ end
20
24
  end
21
25
 
22
26
  # Sets the urls to be indexed.
@@ -75,7 +79,7 @@ module Sitemap
75
79
  search = Sitemap.defaults[:search].clone.merge!(options.select { |k, v| SEARCH_ATTRIBUTES.keys.include?(k) })
76
80
  search.merge!(search) { |type, value| get_data(object, value) }
77
81
 
78
- self.entries << {
82
+ self.store << {
79
83
  :object => object,
80
84
  :search => search,
81
85
  :params => params
@@ -106,32 +110,65 @@ module Sitemap
106
110
  #
107
111
  def resources(type, options = {})
108
112
  path(type) unless options[:skip_index]
109
- objects = options[:objects] ? options[:objects].call : type.to_s.classify.constantize.all
110
- options.reject! { |k, v| k == :objects }
111
-
112
- objects.each do |object|
113
- path(object, options)
113
+ link_params = options.reject { |k, v| k == :objects }
114
+ get_objects = lambda {
115
+ options[:objects] ? options[:objects].call : type.to_s.classify.constantize
116
+ }
117
+ get_objects.call.find_each(:batch_size => Sitemap.defaults[:query_batch_size]) do |object|
118
+ path(object, link_params)
114
119
  end
115
120
  end
116
121
 
117
122
  # Parses the loaded data and returns the xml entries.
118
- def build
119
- instance_exec(self, &routes)
123
+ def render(object = "fragment")
120
124
  xml = Builder::XmlMarkup.new(:indent => 2)
121
- file = File.read(File.expand_path("../../views/index.xml.builder", __FILE__))
125
+ file = File.read(File.expand_path("../../views/#{object}.xml.builder", __FILE__))
122
126
  instance_eval file
123
127
  end
124
128
 
125
- # Builds xml entries and saves the data to the specified location.
126
- def save(location)
127
- file = File.new(location, "w")
128
- file.write(build)
129
+ # Creates a temporary file from the existing entries.
130
+ def process_fragment!
131
+ file = Tempfile.new("sitemap.xml")
132
+ file.write(render)
129
133
  file.close
134
+ self.fragments << file
135
+ end
136
+
137
+ # Generates fragments.
138
+ def build!
139
+ instance_exec(self, &routes)
140
+ process_fragment! unless store.entries.empty?
141
+ end
142
+
143
+ # Creates the sitemap index file and saves any existing fragments.
144
+ def save(location)
145
+ if fragments.length == 1
146
+ FileUtils.mv(fragments.first.path, location)
147
+ else
148
+ remove_saved_files(location)
149
+ root = File.join(Pathname.new(location).dirname, "sitemap")
150
+ Dir.mkdir(root) unless File.directory?(root)
151
+ fragments.each_with_index do |fragment, i|
152
+ file_pattern = File.join(root, "sitemap-fragment-#{i + 1}.xml")
153
+ FileUtils.mv(fragment.path, file_pattern)
154
+ end
155
+ file = File.new(location, "w")
156
+ file.write(render "index")
157
+ file.close
158
+ end
159
+ end
160
+
161
+ # URL to the sitemap file.
162
+ #
163
+ # Defaults to <tt>sitemap.xml</tt>.
164
+ def file_url(path = "sitemap.xml")
165
+ URI::HTTP.build(:host => host, :path => File.join("/", path)).to_s
130
166
  end
131
167
 
132
- # URL to the <tt>sitemap.xml</tt> file.
133
- def file_url
134
- URI::HTTP.build(:host => host, :path => "/sitemap.xml").to_s
168
+ def remove_saved_files(location)
169
+ root = File.join(Pathname.new(location).dirname, "sitemap")
170
+ Dir[File.join(root, "sitemap-fragment-*.xml")].each { |file| File.unlink(file) }
171
+ File.unlink(location) if File.exist?(location)
135
172
  end
136
173
 
137
174
  private
@@ -0,0 +1,30 @@
1
+ module Sitemap
2
+
3
+ class Store
4
+
5
+ attr_accessor :entries, :max_entries, :reset_count, :before_reset_callback
6
+
7
+ def initialize(options = {})
8
+ self.entries = []
9
+ self.reset_count = 0
10
+ self.max_entries = options[:max_entries]
11
+ end
12
+
13
+ def << entry
14
+ reset! if entries.length >= max_entries
15
+ self.entries << entry
16
+ end
17
+
18
+ def reset!
19
+ before_reset_callback.call(entries) if before_reset_callback
20
+ self.entries = []
21
+ self.reset_count += 1
22
+ end
23
+
24
+ def before_reset(&block)
25
+ self.before_reset_callback = block
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -8,6 +8,7 @@ namespace :sitemap do
8
8
  task :generate => :environment do
9
9
  setup
10
10
  path = File.join(Rails.public_path, "sitemap.xml")
11
+ Sitemap::Generator.instance.build!
11
12
  Sitemap::Generator.instance.save path
12
13
  end
13
14
 
@@ -0,0 +1,15 @@
1
+ xml.instruct!
2
+ xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
3
+
4
+ store.entries.each do |entry|
5
+ xml.url do
6
+ xml.loc polymorphic_url(entry[:object], entry[:params])
7
+ entry[:search].each do |type, value|
8
+ next if !value || value.blank?
9
+ xml.tag! SEARCH_ATTRIBUTES[type], value.to_s
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+
@@ -1,15 +1,11 @@
1
1
  xml.instruct!
2
- xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
2
+ xml.sitemapindex :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
3
3
 
4
- entries.each do |entry|
5
- xml.url do
6
- xml.loc polymorphic_url(entry[:object], entry[:params])
7
- entry[:search].each do |type, value|
8
- next if !value || value.blank?
9
- xml.tag! SEARCH_ATTRIBUTES[type], value.to_s
10
- end
4
+ fragments.each_with_index do |fragment, i|
5
+ xml.sitemap do
6
+ xml.loc file_url("sitemaps/sitemap-fragment-#{i + 1}.xml")
7
+ xml.lastmod Time.now.strftime("%Y-%m-%dT%H:%M:%S+00:00")
11
8
  end
12
9
  end
13
10
 
14
11
  end
15
-
@@ -2,7 +2,7 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
2
2
 
3
3
  spec = Gem::Specification.new do |spec|
4
4
  spec.name = "sitemap"
5
- spec.version = "0.1"
5
+ spec.version = "0.2b"
6
6
  spec.summary = "Sitemap"
7
7
  spec.description = "A simple ruby on rails sitemap generator"
8
8
 
@@ -27,6 +27,7 @@ module SitemapTestSetup
27
27
  t.string :name
28
28
  t.text :contents
29
29
  t.string :location
30
+ t.boolean :published, :default => true
30
31
  t.timestamps
31
32
  end
32
33
  end
@@ -34,7 +35,8 @@ module SitemapTestSetup
34
35
  options = {
35
36
  :name => "Coding #{i}",
36
37
  :contents => "Lorem ipsum dolor sit",
37
- :location => "someplace-#{i}"
38
+ :location => "someplace-#{i}",
39
+ :published => (i < 6)
38
40
  }
39
41
  Activity.create!(options)
40
42
  end
@@ -19,6 +19,7 @@ class SitemapTest < Test::Unit::TestCase
19
19
 
20
20
  def setup
21
21
  create_db
22
+ Sitemap.defaults[:max_urls] = 10000
22
23
  Sitemap::Generator.reset_instance
23
24
  end
24
25
 
@@ -28,7 +29,7 @@ class SitemapTest < Test::Unit::TestCase
28
29
 
29
30
  def test_xml_response
30
31
  Sitemap::Generator.instance.load(:host => "someplace.com") {}
31
- doc = Nokogiri::XML(Sitemap::Generator.instance.build)
32
+ doc = Nokogiri::XML(Sitemap::Generator.instance.render)
32
33
  assert doc.errors.empty?
33
34
  assert_equal doc.root.name, "urlset"
34
35
  end
@@ -39,7 +40,8 @@ class SitemapTest < Test::Unit::TestCase
39
40
  path :root
40
41
  path :faq
41
42
  end
42
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
43
+ Sitemap::Generator.instance.build!
44
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
43
45
  elements = doc.xpath "//url/loc"
44
46
  assert_equal elements.length, urls.length
45
47
  elements.each_with_index do |element, i|
@@ -51,7 +53,8 @@ class SitemapTest < Test::Unit::TestCase
51
53
  Sitemap::Generator.instance.load(:host => "someplace.com") do
52
54
  resources :activities
53
55
  end
54
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
56
+ Sitemap::Generator.instance.build!
57
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
55
58
  elements = doc.xpath "//url/loc"
56
59
  assert_equal elements.length, Activity.count + 1
57
60
  assert_equal elements.first.text, "http://someplace.com/activities"
@@ -61,14 +64,15 @@ class SitemapTest < Test::Unit::TestCase
61
64
  end
62
65
 
63
66
  def test_custom_resource_objects
64
- activities = [Activity.first, Activity.last]
67
+ activities = proc { Activity.where(:published => true) }
65
68
  Sitemap::Generator.instance.load(:host => "someplace.com") do
66
- resources :activities, :objects => proc { activities }, :skip_index => true
69
+ resources :activities, :objects => activities, :skip_index => true
67
70
  end
68
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
71
+ Sitemap::Generator.instance.build!
72
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
69
73
  elements = doc.xpath "//url/loc"
70
- assert_equal elements.length, activities.length
71
- activities.each_with_index do |activity, i|
74
+ assert_equal elements.length, activities.call.length
75
+ activities.call.each_with_index do |activity, i|
72
76
  assert_equal elements[i].text, "http://someplace.com/activities/%d" % activity.id
73
77
  end
74
78
  end
@@ -77,7 +81,8 @@ class SitemapTest < Test::Unit::TestCase
77
81
  Sitemap::Generator.instance.load(:host => "someplace.com") do
78
82
  path :faq, :params => { :host => "anotherplace.com", :format => "html", :filter => "recent" }
79
83
  end
80
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
84
+ Sitemap::Generator.instance.build!
85
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
81
86
  elements = doc.xpath "//url/loc"
82
87
  assert_equal elements.first.text, "http://anotherplace.com/questions.html?filter=recent"
83
88
  end
@@ -87,7 +92,8 @@ class SitemapTest < Test::Unit::TestCase
87
92
  resources :activities, :skip_index => true, :params => { :host => proc { |obj| [obj.location, host].join(".") } }
88
93
  end
89
94
  activities = Activity.all
90
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
95
+ Sitemap::Generator.instance.build!
96
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
91
97
  elements = doc.xpath "//url/loc"
92
98
  elements.each_with_index do |element, i|
93
99
  assert_equal element.text, "http://%s.someplace.com/activities/%d" % [activities[i].location, activities[i].id]
@@ -99,7 +105,8 @@ class SitemapTest < Test::Unit::TestCase
99
105
  path :faq, :priority => 1, :change_frequency => "always"
100
106
  resources :activities, :change_frequency => "weekly"
101
107
  end
102
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
108
+ Sitemap::Generator.instance.build!
109
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
103
110
  assert_equal doc.xpath("//url/priority").first.text, "1"
104
111
  elements = doc.xpath "//url/changefreq"
105
112
  assert_equal elements[0].text, "always"
@@ -113,7 +120,7 @@ class SitemapTest < Test::Unit::TestCase
113
120
  resources :activities, :priority => proc { |obj| obj.id <= 2 ? 1 : 0.5 }, :skip_index => true
114
121
  end
115
122
  activities = Activity.all
116
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
123
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
117
124
  elements = doc.xpath "//url/priority"
118
125
  elements.each_with_index do |element, i|
119
126
  value = activities[i].id <= 2 ? "1" : "0.5"
@@ -125,7 +132,8 @@ class SitemapTest < Test::Unit::TestCase
125
132
  Sitemap::Generator.instance.load(:host => "someplace.com") do
126
133
  path :faq, :priority => "", :change_frequency => lambda { |e| return false}, :updated_at => Date.today
127
134
  end
128
- doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
135
+ Sitemap::Generator.instance.build!
136
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render)
129
137
  assert_equal doc.xpath("//url/priority").count, 0
130
138
  assert_equal doc.xpath("//url/changefreq").count, 0
131
139
  assert_equal doc.xpath("//url/lastmod").text, Date.today.to_s
@@ -136,4 +144,50 @@ class SitemapTest < Test::Unit::TestCase
136
144
  assert_equal Sitemap::Generator.instance.file_url, "http://someplace.com/sitemap.xml"
137
145
  end
138
146
 
147
+ def test_save_creates_file
148
+ path = File.join(Dir.tmpdir, "sitemap.xml")
149
+ File.unlink(path) if File.exist?(path)
150
+ Sitemap::Generator.instance.load(:host => "someplace.com") do
151
+ resources :activities
152
+ end
153
+ Sitemap::Generator.instance.build!
154
+ Sitemap::Generator.instance.save(path)
155
+ assert File.exist?(path)
156
+ File.unlink(path)
157
+ end
158
+
159
+ def test_saves_fragments
160
+ Sitemap.defaults[:max_urls] = 2
161
+ Sitemap::Generator.instance.load(:host => "someplace.com") do
162
+ path :root
163
+ path :root
164
+ path :root
165
+ path :root
166
+ end
167
+ path = File.join(Dir.tmpdir, "sitemap.xml")
168
+ root = File.join(Dir.tmpdir, "sitemap") # Directory is being removed at the end of the test.
169
+ assert !File.directory?(root)
170
+ Sitemap::Generator.instance.build!
171
+ Sitemap::Generator.instance.save(path)
172
+ 1.upto(2) { |i|
173
+ assert File.exists?(File.join(root, "sitemap-fragment-#{i}.xml"))
174
+ }
175
+ FileUtils.rm_rf(root)
176
+ end
177
+
178
+ def test_fragments_index
179
+ Sitemap.defaults[:max_urls] = 2
180
+ Sitemap::Generator.instance.load(:host => "someplace.com") do
181
+ path :root
182
+ path :root
183
+ path :root
184
+ path :root
185
+ path :root
186
+ end
187
+ Sitemap::Generator.instance.build!
188
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.render("index"))
189
+ elements = doc.xpath "//sitemap"
190
+ assert_equal Sitemap::Generator.instance.fragments.length, 3
191
+ end
192
+
139
193
  end
@@ -0,0 +1,35 @@
1
+ require "test/unit"
2
+ require "rubygems"
3
+
4
+ class StoreTest < Test::Unit::TestCase
5
+
6
+ def test_append_entries
7
+ store = Sitemap::Store.new(:max_entries => 1000)
8
+ 3.times { store << "contents" }
9
+ assert_equal store.entries.length, 3
10
+ end
11
+
12
+ def test_reset_entries_limit
13
+ store = Sitemap::Store.new(:max_entries => 2)
14
+ 2.times { store << "contents" }
15
+ assert_equal store.entries.length, 2
16
+ store << "contents"
17
+ assert_equal store.entries.length, 1
18
+ end
19
+
20
+ def test_reset_callback
21
+ store = Sitemap::Store.new(:max_entries => 2)
22
+ store.before_reset do |entries|
23
+ store.instance_variable_set("@callback_data", entries.join(", "))
24
+ end
25
+ 3.times { |i| store << "item #{i + 1}" }
26
+ assert_equal store.instance_variable_get("@callback_data"), "item 1, item 2"
27
+ end
28
+
29
+ def test_increments_reset_count
30
+ store = Sitemap::Store.new(:max_entries => 2)
31
+ 5.times { store << "contents" }
32
+ assert_equal store.reset_count, 2
33
+ end
34
+
35
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sitemap
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
5
- prerelease:
4
+ version: 0.2b
5
+ prerelease: 3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Daniel Mircea
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-10 00:00:00.000000000Z
12
+ date: 2012-03-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sqlite3
16
- requirement: &17823580 !ruby/object:Gem::Requirement
16
+ requirement: &13114780 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *17823580
24
+ version_requirements: *13114780
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: nokogiri
27
- requirement: &17823140 !ruby/object:Gem::Requirement
27
+ requirement: &13114300 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *17823140
35
+ version_requirements: *13114300
36
36
  description: A simple ruby on rails sitemap generator
37
37
  email: daniel@viseztrance.com
38
38
  executables: []
@@ -41,21 +41,24 @@ extra_rdoc_files:
41
41
  - README.rdoc
42
42
  - LICENSE
43
43
  files:
44
- - lib/generators/sitemap/USAGE
45
- - lib/generators/sitemap/templates/sitemap.rb
46
- - lib/generators/sitemap/install_generator.rb
47
- - lib/views/index.xml.builder
48
- - lib/sitemap.rb
44
+ - lib/tasks/sitemap.rake
45
+ - lib/sitemap/ping.rb
49
46
  - lib/sitemap/railtie.rb
47
+ - lib/sitemap/store.rb
50
48
  - lib/sitemap/generator.rb
51
- - lib/sitemap/ping.rb
52
- - lib/tasks/sitemap.rake
49
+ - lib/views/index.xml.builder
50
+ - lib/views/fragment.xml.builder
51
+ - lib/sitemap.rb
52
+ - lib/generators/sitemap/USAGE
53
+ - lib/generators/sitemap/install_generator.rb
54
+ - lib/generators/sitemap/templates/sitemap.rb
53
55
  - README.rdoc
54
56
  - LICENSE
55
57
  - Rakefile
56
58
  - sitemap.gemspec
57
59
  - test/singleton.rb
58
60
  - test/setup.rb
61
+ - test/store_test.rb
59
62
  - test/sitemap_test.rb
60
63
  homepage: http://github.com/viseztrance/rails-sitemap
61
64
  licenses: []
@@ -77,16 +80,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
80
  required_rubygems_version: !ruby/object:Gem::Requirement
78
81
  none: false
79
82
  requirements:
80
- - - ! '>='
83
+ - - ! '>'
81
84
  - !ruby/object:Gem::Version
82
- version: '0'
85
+ version: 1.3.1
83
86
  requirements: []
84
87
  rubyforge_project:
85
- rubygems_version: 1.8.8
88
+ rubygems_version: 1.8.10
86
89
  signing_key:
87
90
  specification_version: 3
88
91
  summary: Sitemap
89
92
  test_files:
90
93
  - test/singleton.rb
91
94
  - test/setup.rb
95
+ - test/store_test.rb
92
96
  - test/sitemap_test.rb
97
+ has_rdoc: true