ember-seo 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,7 @@
1
+ module Constraint
2
+ class Static
3
+ def matches?(request)
4
+ request.url.include?("_escaped_fragment_")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class StaticController < ApplicationController
2
+
3
+ def show
4
+ file_name = request.params["_escaped_fragment_"]
5
+ file_name = "index" unless file_name.present?
6
+ render file: "public/static/#{file_name}.html", layout: false
7
+ end
8
+
9
+ end
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'site_crawler'
2
+
3
+ namespace :ember_seo do
4
+
5
+ desc "Take a static snapshot of your site"
6
+ task :scrape do
7
+ SiteCrawler.new(Rails.application.config.ember_seo_starting_paths).start_crawl
8
+ end
9
+
10
+ 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: []