sitemap 0.1b

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Daniel Mircea, The Geeks
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ = Sitemap
2
+
3
+ A simple ruby on rails sitemap generator.
4
+
5
+ == Usage
6
+
7
+ Create a sitemap.rb file in your config directory. Paths can be indexed as follows:
8
+
9
+ Sitemap.instance.render :host => "mywebsite.com" do
10
+ path :root, :priority => 1
11
+ path :faq, :priority => 0.5, :change_frequency => "weekly"
12
+ resources :activities, :format => "html"
13
+ resources :articles, :objects => proc { Article.published }
14
+ end
15
+
16
+ Please read the docs for a more comprehensive list of options.
17
+
18
+ == License
19
+
20
+ This package is licensed under the MIT license and/or the Creative
21
+ Commons Attribution-ShareAlike.
@@ -0,0 +1,37 @@
1
+ require "rake/rdoctask"
2
+ require "rake/testtask"
3
+
4
+ spec = Gem::Specification.load(File.expand_path("sitemap.gemspec", File.dirname(__FILE__)))
5
+
6
+ desc "Default: run sitemap unit tests."
7
+ task :default => :test
8
+
9
+ desc "Test the sitemap plugin."
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << "lib"
12
+ t.pattern = "test/**/*_test.rb"
13
+ t.verbose = true
14
+ end
15
+
16
+ # Create the documentation.
17
+ Rake::RDocTask.new do |rdoc|
18
+ rdoc.rdoc_files.include "README.rdoc", "lib/**/*.rb"
19
+ rdoc.options = spec.rdoc_options
20
+ end
21
+
22
+ desc "Push new release to rubyforge and git tag"
23
+ task :push do
24
+ sh "git push"
25
+ puts "Tagging version #{spec.version} .."
26
+ sh "git tag v#{spec.version}"
27
+ sh "git push --tag"
28
+ puts "Building and pushing gem .."
29
+ sh "gem build #{spec.name}.gemspec"
30
+ sh "gem push #{spec.name}-#{spec.version}.gem"
31
+ end
32
+
33
+ desc "Install #{spec.name} locally"
34
+ task :install do
35
+ sh "gem build #{spec.name}.gemspec"
36
+ sh "gem install #{spec.name}-#{spec.version}.gem"
37
+ end
@@ -0,0 +1,168 @@
1
+ #
2
+ # = sitemap.rb - Sitemap
3
+ #
4
+ # Author:: Daniel Mircea daniel@viseztrance.com
5
+ # Copyright:: 2011 (c) Daniel Mircea, {The Geeks}[http://thegeeks.ro]
6
+ # License:: MIT and/or Creative Commons Attribution-ShareAlike
7
+
8
+ require "singleton"
9
+ require "builder"
10
+ require "sitemap/railtie"
11
+ require "sitemap/ping"
12
+
13
+ module Sitemap
14
+
15
+ VERSION = Gem::Specification.load(File.expand_path("../sitemap.gemspec", File.dirname(__FILE__))).version.to_s
16
+
17
+ mattr_accessor :defaults
18
+
19
+ self.defaults = {
20
+ :params => {},
21
+ :search => {
22
+ :updated_at => proc { |obj| obj.updated_at.strftime("%Y-%m-%d") if obj.respond_to?(:updated_at) }
23
+ }
24
+ }
25
+
26
+ class Generator
27
+
28
+ include Singleton
29
+
30
+ SEARCH_ATTRIBUTES = {
31
+ :updated_at => "lastmod",
32
+ :change_frequency => "changefreq",
33
+ :priority => "priority"
34
+ }
35
+
36
+ attr_accessor :entries, :host, :routes
37
+
38
+ # Instantiates a new object.
39
+ # Should never be called directly.
40
+ def initialize
41
+ self.class.send(:include, Rails.application.routes.url_helpers)
42
+ self.entries = []
43
+ end
44
+
45
+ # Sets the urls to be indexed.
46
+ #
47
+ # The +host+, or any other global option can be set here:
48
+ #
49
+ # Sitemap.instance.render :host => "mywebsite.com" do
50
+ # ...
51
+ # end
52
+ #
53
+ # Simple paths can be added as follows:
54
+ #
55
+ # Sitemap.instance.render :host => "mywebsite.com" do
56
+ # path :faq
57
+ # end
58
+ #
59
+ # Object collections are supported too:
60
+ #
61
+ # Sitemap.instance.render :host => "mywebsite.com" do
62
+ # resources :activities
63
+ # end
64
+ #
65
+ # Search options such as frequency and priority can be declared as an options hash:
66
+ #
67
+ # Sitemap.instance.render :host => "mywebsite.com" do
68
+ # path :root, :priority => 1
69
+ # path :faq, :priority => 0.8, :change_frequency => "daily"
70
+ # resources :activities, :change_frequency => "weekly"
71
+ # end
72
+ #
73
+ def render(options = {}, &block)
74
+ options.each do |k, v|
75
+ self.send("#{k}=", v)
76
+ end
77
+ self.routes = block
78
+ end
79
+
80
+ # Adds the specified url or object (such as an ActiveRecord model instance).
81
+ # In either case the data is being looked up in the current application routes.
82
+ #
83
+ # Params can be specified as follows:
84
+ #
85
+ # # config/routes.rb
86
+ # match "/frequent-questions" => "static#faq", :as => "faq"
87
+ #
88
+ # # config/sitemap.rb
89
+ # path :faq, :params => { :filter => "recent" }
90
+ #
91
+ # The resolved url would be <tt>http://mywebsite.com/frequent-questions?filter=recent</tt>.
92
+ #
93
+ def path(object, options = {})
94
+ params = Sitemap.defaults[:params].clone.merge!(options[:params] || {})
95
+ params[:host] ||= host # Use global host if none was specified.
96
+ params.merge!(params) { |type, value| get_data(object, value) }
97
+
98
+ search = Sitemap.defaults[:search].clone.merge!(options.select { |k, v| SEARCH_ATTRIBUTES.keys.include?(k) })
99
+ search.merge!(search) { |type, value| get_data(object, value) }
100
+
101
+ self.entries << {
102
+ :object => object,
103
+ :search => search,
104
+ :params => params
105
+ }
106
+ end
107
+
108
+ # Adds the associated object types.
109
+ #
110
+ # The following will map all Activity entries, as well as the index (<tt>/activities</tt>) page:
111
+ #
112
+ # resources :activities
113
+ #
114
+ # You can also specify which entries are being mapped:
115
+ #
116
+ # resources :articles, :objects => proc { Article.published }
117
+ #
118
+ # To skip the index action and map only the records:
119
+ #
120
+ # resources :articles, :skip_index => true
121
+ #
122
+ # As with the path, you can specify params through the +params+ options hash.
123
+ # The params can also be build conditionally by using a +proc+:
124
+ #
125
+ # resources :activities, :params => { :host => proc { |activity| [activity.location, host].join(".") } }, :skip_index => true
126
+ #
127
+ # In this case the host will change based the each of the objects associated +location+ attribute.
128
+ # Because the index page doesn't have this attribute it's best to skip it.
129
+ #
130
+ def resources(type, options = {})
131
+ path(type) unless options[:skip_index]
132
+ objects = options[:objects] ? options[:objects].call : type.to_s.classify.constantize.all
133
+ options.reject! { |k, v| k == :objects }
134
+
135
+ objects.each do |object|
136
+ path(object, options)
137
+ end
138
+ end
139
+
140
+ # Parses the loaded data and returns the xml entries.
141
+ def build
142
+ instance_exec(self, &routes)
143
+ xml = Builder::XmlMarkup.new(:indent => 2)
144
+ file = File.read(File.expand_path("../views/index.xml.builder", __FILE__))
145
+ instance_eval file
146
+ end
147
+
148
+ # Builds xml entries and saves the data to the specified location.
149
+ def save(location)
150
+ file = File.new(location, "w")
151
+ file.write(build)
152
+ file.close
153
+ end
154
+
155
+ # URL to the <tt>sitemap.xml</tt> file.
156
+ def file_url
157
+ URI::HTTP.build(:host => host, :path => "/sitemap.xml").to_s
158
+ end
159
+
160
+ private
161
+
162
+ def get_data(object, data)
163
+ data.respond_to?(:call) ? data.call(object) : data
164
+ end
165
+
166
+ end
167
+
168
+ end
@@ -0,0 +1,24 @@
1
+ require "net/http"
2
+ require "cgi"
3
+
4
+ module Sitemap
5
+
6
+ module Ping
7
+
8
+ SEARCH_ENGINES = {
9
+ "Google" => "http://www.google.com/webmasters/tools/ping?sitemap=%s",
10
+ "Yahoo!" => "http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=SitemapWriter&url=%s",
11
+ "Ask.com" => "http://submissions.ask.com/ping?sitemap=%s",
12
+ "Bing" => "http://www.bing.com/webmaster/ping.aspx?siteMap=%s"
13
+ }
14
+
15
+ def self.send_request(file_path = false)
16
+ SEARCH_ENGINES.each do |name, url|
17
+ request = url % CGI.escape(file_path || Sitemap::Generator.instance.file_url)
18
+ Net::HTTP.get(URI.parse(request))
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,13 @@
1
+ require "rails"
2
+
3
+ module Sitemap
4
+
5
+ class Railtie < Rails::Railtie
6
+
7
+ rake_tasks do
8
+ load "tasks/sitemap.rake"
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,19 @@
1
+ namespace :sitemap do
2
+
3
+ def setup
4
+ require File.join(Rails.root, "config", "sitemap")
5
+ end
6
+
7
+ desc "Generates a new sitemap."
8
+ task :generate => :environment do
9
+ setup
10
+ path = File.join(Rails.public_path, "sitemap.xml")
11
+ Sitemap::Generator.instance.save path
12
+ end
13
+
14
+ desc "Ping engines."
15
+ task :ping => :environment do
16
+ Sitemap::Ping.send_request ENV["LOCATION"]
17
+ end
18
+
19
+ end
@@ -0,0 +1,15 @@
1
+ xml.instruct!
2
+ xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
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
11
+ end
12
+ end
13
+
14
+ end
15
+
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
2
+
3
+ spec = Gem::Specification.new do |spec|
4
+ spec.name = "sitemap"
5
+ spec.version = "0.1b"
6
+ spec.summary = "Sitemap"
7
+ spec.description = "A simple ruby on rails sitemap generator"
8
+
9
+ spec.authors << "Daniel Mircea"
10
+ spec.email = "daniel@viseztrance.com"
11
+ spec.homepage = "http://github.com/viseztrance/rails-sitemap"
12
+
13
+ spec.add_development_dependency "sqlite3"
14
+ spec.add_development_dependency "nokogiri"
15
+
16
+ spec.files = Dir["{lib,docs}/**/*"] + ["README.rdoc", "LICENSE", "Rakefile", "sitemap.gemspec"]
17
+ spec.test_files = Dir["test/**/*"]
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.has_rdoc = true
21
+ spec.rdoc_options << "--main" << "README.rdoc" << "--title" << "Sitemap" << "--line-numbers"
22
+ "--webcvs" << "http://github.com/viseztrance/rails-sitemap"
23
+ spec.extra_rdoc_files = ["README.rdoc", "LICENSE"]
24
+ end
@@ -0,0 +1,49 @@
1
+ module TestApp
2
+
3
+ class Application < Rails::Application
4
+ config.active_support.deprecation = :log
5
+ end
6
+
7
+ end
8
+
9
+ TestApp::Application.initialize!
10
+
11
+ TestApp::Application.routes.draw do
12
+
13
+ root :to => "main#index"
14
+
15
+ match "/questions" => "static#faq", :as => "faq"
16
+
17
+ resources :activities
18
+
19
+ end
20
+
21
+ module SitemapTestSetup
22
+
23
+ def create_db
24
+ # Database
25
+ ActiveRecord::Schema.define(:version => 1) do
26
+ create_table :activities do |t|
27
+ t.string :name
28
+ t.text :contents
29
+ t.string :location
30
+ t.timestamps
31
+ end
32
+ end
33
+ 1.upto(8) do |i|
34
+ options = {
35
+ :name => "Coding #{i}",
36
+ :contents => "Lorem ipsum dolor sit",
37
+ :location => "someplace-#{i}"
38
+ }
39
+ Activity.create!(options)
40
+ end
41
+ end
42
+
43
+ def drop_db
44
+ ActiveRecord::Base.connection.tables.each do |table|
45
+ ActiveRecord::Base.connection.drop_table(table)
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,24 @@
1
+ # Reset singleton
2
+ # http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby
3
+ class << Singleton
4
+
5
+ def included_with_reset(klass)
6
+
7
+ included_without_reset(klass)
8
+
9
+ class << klass
10
+
11
+ def reset_instance
12
+ Singleton.send :__init__, self
13
+ self
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ alias_method :included_without_reset, :included
21
+
22
+ alias_method :included, :included_with_reset
23
+
24
+ end
@@ -0,0 +1,139 @@
1
+ require "test/unit"
2
+ require "rubygems"
3
+ require "rails"
4
+ require "action_controller/railtie" # Rails 3.1
5
+ require "active_record"
6
+ require "nokogiri"
7
+
8
+ require File.expand_path("singleton", File.dirname(__FILE__))
9
+ require File.expand_path("setup", File.dirname(__FILE__))
10
+ require File.expand_path("../lib/sitemap", File.dirname(__FILE__))
11
+
12
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
13
+
14
+ class Activity < ActiveRecord::Base; end
15
+
16
+ class SitemapTest < Test::Unit::TestCase
17
+
18
+ include SitemapTestSetup
19
+
20
+ def setup
21
+ create_db
22
+ Sitemap::Generator.reset_instance
23
+ end
24
+
25
+ def teardown
26
+ drop_db
27
+ end
28
+
29
+ def test_xml_response
30
+ Sitemap::Generator.instance.render(:host => "someplace.com") {}
31
+ doc = Nokogiri::XML(Sitemap::Generator.instance.build)
32
+ assert doc.errors.empty?
33
+ assert_equal doc.root.name, "urlset"
34
+ end
35
+
36
+ def test_path_route
37
+ urls = ["http://someplace.com/", "http://someplace.com/questions"]
38
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
39
+ path :root
40
+ path :faq
41
+ end
42
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
43
+ elements = doc.xpath "//url/loc"
44
+ assert_equal elements.length, urls.length
45
+ elements.each_with_index do |element, i|
46
+ assert_equal element.text, urls[i]
47
+ end
48
+ end
49
+
50
+ def test_resources_route
51
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
52
+ resources :activities
53
+ end
54
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
55
+ elements = doc.xpath "//url/loc"
56
+ assert_equal elements.length, Activity.count + 1
57
+ assert_equal elements.first.text, "http://someplace.com/activities"
58
+ elements[1..-1].each_with_index do |element, i|
59
+ assert_equal element.text, "http://someplace.com/activities/#{i + 1}"
60
+ end
61
+ end
62
+
63
+ def test_custom_resource_objects
64
+ activities = [Activity.first, Activity.last]
65
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
66
+ resources :activities, :objects => proc { activities }, :skip_index => true
67
+ end
68
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
69
+ elements = doc.xpath "//url/loc"
70
+ assert_equal elements.length, activities.length
71
+ activities.each_with_index do |activity, i|
72
+ assert_equal elements[i].text, "http://someplace.com/activities/%d" % activity.id
73
+ end
74
+ end
75
+
76
+ def test_params_options
77
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
78
+ path :faq, :params => { :host => "anotherplace.com", :format => "html", :filter => "recent" }
79
+ end
80
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
81
+ elements = doc.xpath "//url/loc"
82
+ assert_equal elements.first.text, "http://anotherplace.com/questions.html?filter=recent"
83
+ end
84
+
85
+ def test_params_blocks
86
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
87
+ resources :activities, :skip_index => true, :params => { :host => proc { |obj| [obj.location, host].join(".") } }
88
+ end
89
+ activities = Activity.all
90
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
91
+ elements = doc.xpath "//url/loc"
92
+ elements.each_with_index do |element, i|
93
+ assert_equal element.text, "http://%s.someplace.com/activities/%d" % [activities[i].location, activities[i].id]
94
+ end
95
+ end
96
+
97
+ def test_search_attribute_options
98
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
99
+ path :faq, :priority => 1, :change_frequency => "always"
100
+ resources :activities, :change_frequency => "weekly"
101
+ end
102
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
103
+ assert_equal doc.xpath("//url/priority").first.text, "1"
104
+ elements = doc.xpath "//url/changefreq"
105
+ assert_equal elements[0].text, "always"
106
+ elements[1..-1].each do |element|
107
+ assert_equal element.text, "weekly"
108
+ end
109
+ end
110
+
111
+ def test_search_attribute_blocks
112
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
113
+ resources :activities, :priority => proc { |obj| obj.id <= 2 ? 1 : 0.5 }, :skip_index => true
114
+ end
115
+ activities = Activity.all
116
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
117
+ elements = doc.xpath "//url/priority"
118
+ elements.each_with_index do |element, i|
119
+ value = activities[i].id <= 2 ? "1" : "0.5"
120
+ assert_equal element.text, value
121
+ end
122
+ end
123
+
124
+ def test_discards_empty_search_attributes # Empty or false (boolean).
125
+ Sitemap::Generator.instance.render(:host => "someplace.com") do
126
+ path :faq, :priority => "", :change_frequency => lambda { |e| return false}, :updated_at => Date.today
127
+ end
128
+ doc = Nokogiri::HTML(Sitemap::Generator.instance.build)
129
+ assert_equal doc.xpath("//url/priority").count, 0
130
+ assert_equal doc.xpath("//url/changefreq").count, 0
131
+ assert_equal doc.xpath("//url/lastmod").text, Date.today.to_s
132
+ end
133
+
134
+ def test_file_url
135
+ Sitemap::Generator.instance.render(:host => "someplace.com") {}
136
+ assert_equal Sitemap::Generator.instance.file_url, "http://someplace.com/sitemap.xml"
137
+ end
138
+
139
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sitemap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1b
5
+ prerelease: 3
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Mircea
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sqlite3
16
+ requirement: &24552460 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *24552460
25
+ - !ruby/object:Gem::Dependency
26
+ name: nokogiri
27
+ requirement: &24552040 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *24552040
36
+ description: A simple ruby on rails sitemap generator
37
+ email: daniel@viseztrance.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ - LICENSE
43
+ files:
44
+ - lib/views/index.xml.builder
45
+ - lib/sitemap.rb
46
+ - lib/sitemap/railtie.rb
47
+ - lib/sitemap/ping.rb
48
+ - lib/tasks/sitemap.rake
49
+ - README.rdoc
50
+ - LICENSE
51
+ - Rakefile
52
+ - sitemap.gemspec
53
+ - test/singleton.rb
54
+ - test/setup.rb
55
+ - test/sitemap_test.rb
56
+ homepage: http://github.com/viseztrance/rails-sitemap
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --main
61
+ - README.rdoc
62
+ - --title
63
+ - Sitemap
64
+ - --line-numbers
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>'
77
+ - !ruby/object:Gem::Version
78
+ version: 1.3.1
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 1.8.8
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Sitemap
85
+ test_files:
86
+ - test/singleton.rb
87
+ - test/setup.rb
88
+ - test/sitemap_test.rb