ember-seo 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +64 -0
- data/lib/ember-seo.rb +1 -0
- data/lib/generators/ember_seo/install_generator.rb +24 -0
- data/lib/generators/ember_seo/templates/hashbang.js +28 -0
- data/lib/generators/ember_seo/templates/location.js.coffee +20 -0
- data/lib/generators/ember_seo/templates/static_constraint.rb +7 -0
- data/lib/generators/ember_seo/templates/static_controller.rb +9 -0
- data/lib/site_crawler.rb +72 -0
- data/lib/static_file_saver.rb +32 -0
- data/lib/tasks/ember-seo.rake +10 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c9f2e60d41909d4f87c46a90319513956fbcd9f4
|
4
|
+
data.tar.gz: 7f245a81ae5ce9620faf217598b9e53082c2e5c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 950cb5691db08403e12eef0d1d391ac662ef573551f5d106c6e8218053d592c20fd00c111e2225ccdc300f4a3008d736196d4b31bf696e8115d4148a8d884946
|
7
|
+
data.tar.gz: 5b310d03146c42d2463cfb81b36bd260447b67456bcf6521f62734bef1a5e75fe23af82f3fc786d124e22bc0f3b6f49e3a536a2efc585d43f4bed920478bcd54
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Vic Ramon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
## Ember SEO
|
2
|
+
|
3
|
+
**Status**
|
4
|
+
|
5
|
+
This gem successfully creates a snapshot of your site on your local server and properly serves it up with escaped fragement. However, I am unsure if the escaped fragment pattern is exactly correct and if this will actually work with Google.
|
6
|
+
|
7
|
+
Additionally, hashbang urls are really ugly, so I'd like for the router to determine if the request is from a Google bot and only then use hashbangs.
|
8
|
+
|
9
|
+
|
10
|
+
**Introduction**
|
11
|
+
|
12
|
+
This gem helps make Ember sites SEO friendly by:
|
13
|
+
|
14
|
+
* implementing hashbang urls in your Ember app
|
15
|
+
* providing a rake task that crawls your site and takes a static snapshot
|
16
|
+
* serving the correct static files to Google by properly handling requests with `_escaped_fragment_`
|
17
|
+
|
18
|
+
### Installation
|
19
|
+
|
20
|
+
#### Bundle & Install
|
21
|
+
|
22
|
+
Add it to your Gemfile:
|
23
|
+
|
24
|
+
`gem 'ember-seo'`
|
25
|
+
|
26
|
+
Run the generator:
|
27
|
+
|
28
|
+
`rails g ember_seo:install`
|
29
|
+
|
30
|
+
This creates Rails routes, a Rails controller to display your static site, and app/models/hashbang.js which has a hashbang location helper. If you're not using Ember Appkit Rails then you should move hashbang.js to app/assets/javascripts/models.
|
31
|
+
|
32
|
+
|
33
|
+
#### Setup
|
34
|
+
|
35
|
+
Modify your Ember router to use hashbang:
|
36
|
+
|
37
|
+
```
|
38
|
+
var Router = Ember.Router.extend({
|
39
|
+
location: 'hashbang'
|
40
|
+
});
|
41
|
+
```
|
42
|
+
|
43
|
+
Set the starting paths for the crawler in your environment file:
|
44
|
+
|
45
|
+
```
|
46
|
+
config.ember_seo_starting_paths = ['/', '/posts', '/etc']
|
47
|
+
```
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
Make sure your server is running on localhost:3000 then run the crawler:
|
52
|
+
|
53
|
+
```
|
54
|
+
rake ember_seo:scrape
|
55
|
+
```
|
56
|
+
|
57
|
+
This takes a snapshot of your site and saves it to public/static.
|
58
|
+
|
59
|
+
Now visit a url in your app, for example:
|
60
|
+
|
61
|
+
`http://localhost:3000/#!/posts`, and replace `#!` with `?_escaped_fragment=` like so:
|
62
|
+
`http://localhost:3000/?_escaped_fragment_=posts` and you should see the correct static file. View
|
63
|
+
the source of the page to ensure that you are viewing the static file and not your Ember
|
64
|
+
application.
|
data/lib/ember-seo.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f } if defined?(Rake)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module EmberSeo
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
desc "Sets up app for Ember Seo gem"
|
9
|
+
|
10
|
+
def generate
|
11
|
+
copy_file "../templates/static_controller.rb", "app/controllers/static_controller.rb"
|
12
|
+
copy_file "../templates/hashbang.js", "app/models/hashbang.js"
|
13
|
+
copy_file "../templates/static_constraint.rb", "lib/constraints/static_constraint.rb"
|
14
|
+
|
15
|
+
inject_into_file 'config/routes.rb', after: "routes.draw do\n" do
|
16
|
+
"\n get '/', to: 'static#show', constraints: Constraint::Static.new" +
|
17
|
+
"\n get '*path', to: 'static#show', constraints: Constraint::Static.new\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Ember.Location.registerImplementation("hashbang", Ember.HashLocation.extend({
|
2
|
+
getURL: function() {
|
3
|
+
return Ember.get(this, "location").hash.substr(2);
|
4
|
+
},
|
5
|
+
setURL: function(path) {
|
6
|
+
Ember.get(this, "location").hash = "!" + path;
|
7
|
+
return Ember.set(this, "lastSetURL", path);
|
8
|
+
},
|
9
|
+
onUpdateURL: function(callback) {
|
10
|
+
var guid, self;
|
11
|
+
self = this;
|
12
|
+
guid = Ember.guidFor(this);
|
13
|
+
return Ember.$(window).bind("hashchange.ember-location-" + guid, function() {
|
14
|
+
return Ember.run(function() {
|
15
|
+
var path;
|
16
|
+
path = location.hash.substr(2);
|
17
|
+
if (Ember.get(self, "lastSetURL") === path) {
|
18
|
+
return;
|
19
|
+
}
|
20
|
+
Ember.set(self, "lastSetURL", null);
|
21
|
+
return callback(path);
|
22
|
+
});
|
23
|
+
});
|
24
|
+
},
|
25
|
+
formatURL: function(url) {
|
26
|
+
return "#!" + url;
|
27
|
+
}
|
28
|
+
}));
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Ember.Location.registerImplementation "hashbang", Ember.HashLocation.extend
|
2
|
+
getURL: ->
|
3
|
+
Ember.get(this, "location").hash.substr 2
|
4
|
+
|
5
|
+
setURL: (path) ->
|
6
|
+
Ember.get(this, "location").hash = "!" + path
|
7
|
+
Ember.set(this, "lastSetURL", path)
|
8
|
+
|
9
|
+
onUpdateURL: (callback) ->
|
10
|
+
self = this
|
11
|
+
guid = Ember.guidFor(this)
|
12
|
+
Ember.$(window).bind "hashchange.ember-location-" + guid, ->
|
13
|
+
Ember.run ->
|
14
|
+
path = location.hash.substr(2)
|
15
|
+
return if Ember.get(self, "lastSetURL") is path
|
16
|
+
Ember.set(self, "lastSetURL", null)
|
17
|
+
callback path
|
18
|
+
|
19
|
+
formatURL: (url) ->
|
20
|
+
"#!" + url
|
data/lib/site_crawler.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'static_file_saver'
|
2
|
+
require 'capybara/dsl'
|
3
|
+
require 'capybara/poltergeist'
|
4
|
+
|
5
|
+
class SiteCrawler
|
6
|
+
include ::Capybara::DSL
|
7
|
+
|
8
|
+
attr_accessor :start_paths, :saved_paths, :queued_paths, :current_path, :current_path_bang
|
9
|
+
|
10
|
+
def initialize(start_paths=['/'])
|
11
|
+
self.start_paths = start_paths
|
12
|
+
self.saved_paths = []
|
13
|
+
self.queued_paths= []
|
14
|
+
capybara_setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def start_crawl
|
18
|
+
start_message
|
19
|
+
crawl(@start_paths)
|
20
|
+
end
|
21
|
+
|
22
|
+
def crawl(paths)
|
23
|
+
paths.each do |path|
|
24
|
+
@current_path = path
|
25
|
+
@current_path_bang = "#!#{@current_path}"
|
26
|
+
if !saved_paths.include? @current_path
|
27
|
+
visit_page
|
28
|
+
save_page
|
29
|
+
end
|
30
|
+
end
|
31
|
+
queued_paths.present? ? crawl(queued_paths) : done_message
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def visit_page
|
37
|
+
puts "visiting #{current_path}..."
|
38
|
+
visit(@current_path_bang)
|
39
|
+
@document = Nokogiri::HTML.parse(source)
|
40
|
+
queue_links
|
41
|
+
end
|
42
|
+
|
43
|
+
def save_page
|
44
|
+
@document.css('script').remove
|
45
|
+
StaticFileSaver.new(@current_path_bang, @document).save
|
46
|
+
saved_paths << current_path
|
47
|
+
queued_paths.delete(current_path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def capybara_setup
|
51
|
+
Capybara.current_driver = :poltergeist
|
52
|
+
Capybara.app_host = 'http://localhost:3000/'
|
53
|
+
end
|
54
|
+
|
55
|
+
def queue_links
|
56
|
+
@document.css('a').each do |link|
|
57
|
+
path = link['href'].gsub("#!",'')
|
58
|
+
if !queued_paths.include? path and !path.include?('mailto:')
|
59
|
+
queued_paths << path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_message
|
65
|
+
puts "Ember SEO Crawling...\n\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def done_message
|
69
|
+
puts "\nDone crawling."
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class StaticFileSaver
|
2
|
+
attr_accessor :path, :document
|
3
|
+
|
4
|
+
def initialize(ember_path, document)
|
5
|
+
self.path = static_file_path_for(ember_path)
|
6
|
+
self.document = document
|
7
|
+
end
|
8
|
+
|
9
|
+
def save
|
10
|
+
create_directory
|
11
|
+
create_file
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def create_file
|
17
|
+
File.open(@path + ".html", "w+") do |f|
|
18
|
+
f.write(@document.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_directory
|
23
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
24
|
+
end
|
25
|
+
|
26
|
+
def static_file_path_for(path)
|
27
|
+
file_name = path.gsub("#!",'')
|
28
|
+
file_name << 'index' if path[path.length-1] == '/'
|
29
|
+
"public/static" + file_name
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ember-seo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vic Ramon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
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: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: poltergeist
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: capybara
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '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'
|
83
|
+
description: Ember Seo helps you make you Ember app crawlable by Google.
|
84
|
+
email: vic@vicramon.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- LICENSE
|
90
|
+
- README.md
|
91
|
+
- lib/ember-seo.rb
|
92
|
+
- lib/generators/ember_seo/install_generator.rb
|
93
|
+
- lib/generators/ember_seo/templates/hashbang.js
|
94
|
+
- lib/generators/ember_seo/templates/location.js.coffee
|
95
|
+
- lib/generators/ember_seo/templates/static_constraint.rb
|
96
|
+
- lib/generators/ember_seo/templates/static_controller.rb
|
97
|
+
- lib/site_crawler.rb
|
98
|
+
- lib/static_file_saver.rb
|
99
|
+
- lib/tasks/ember-seo.rake
|
100
|
+
homepage: ''
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 1.9.3
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.2.1
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: Ember Seo helps you make you Ember app crawlable by Google.
|
124
|
+
test_files: []
|