blue_velvet 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +79 -0
- data/Rakefile +42 -0
- data/app/assets/javascripts/blue_velvet/application.js +15 -0
- data/app/assets/stylesheets/blue_velvet/application.css +13 -0
- data/app/assets/stylesheets/facebook/attribute.css.scss +2 -0
- data/app/controllers/facebook/page_controller.rb +54 -0
- data/app/models/facebook/attribute.rb +8 -0
- data/app/models/facebook/client.rb +45 -0
- data/app/views/facebook/page/_attribute_not_found.html.erb +8 -0
- data/app/views/facebook/page/_page_attribute.html.erb +7 -0
- data/config/facebook.yml +18 -0
- data/config/initializers/koala.rb +29 -0
- data/config/routes.rb +2 -0
- data/lib/blue_velvet/engine.rb +6 -0
- data/lib/blue_velvet/version.rb +3 -0
- data/lib/blue_velvet.rb +4 -0
- data/lib/generators/facebook/config_generator.rb +15 -0
- data/lib/generators/facebook/views_generator.rb +14 -0
- data/test/blue_velvet_test.rb +7 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +9 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/facebook.yml +18 -0
- data/test/dummy/config/facebook.yml.example +18 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +16 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +71 -0
- data/test/dummy/log/test.log +935 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/fixtures/cassettes/facebook.yml +95 -0
- data/test/fixtures/cassettes/facebook_authentication.yml +95 -0
- data/test/functional/facebook/page_controller_test.rb +25 -0
- data/test/integration/facebook/configuration_test.rb +21 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +34 -0
- data/test/unit/facebook/attribute_test.rb +8 -0
- data/test/unit/facebook/client_test.rb +38 -0
- metadata +202 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Tom Scott
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# BlueVelvet
|
2
|
+
|
3
|
+
BlueVelvet is a content management system that uses the Facebook
|
4
|
+
platform as the data store. It allows you to map route actions to
|
5
|
+
Facebook API calls and display the results with partials in your other
|
6
|
+
pages. You can even subclass the controller and create your own actions,
|
7
|
+
using one or more of your Facebook page's plethora of properties.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Add the gem to your Gemfile
|
12
|
+
|
13
|
+
gem 'blue_velvet'
|
14
|
+
|
15
|
+
Then rebundle
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Generate the Facebook configuration YAML file and edit it with your
|
20
|
+
Facebook App details. Obtain your Facebook page ID from the URL of your
|
21
|
+
Facebook page, it's the number at the end, after the hyphenated name and
|
22
|
+
a "/"..
|
23
|
+
|
24
|
+
$ rails generate facebook:config
|
25
|
+
|
26
|
+
And finally, add the query to your `config/routes.rb`:
|
27
|
+
|
28
|
+
get "/about" => "facebook/page#description"
|
29
|
+
|
30
|
+
If you start up your Rails server, you can now browse to
|
31
|
+
`http://localhost:3000/about` and expect to see the content in your
|
32
|
+
Facebook page's description!
|
33
|
+
|
34
|
+
For more information on the many properties available to you, check out
|
35
|
+
the API documentation.
|
36
|
+
|
37
|
+
## Background
|
38
|
+
|
39
|
+
This project is the result of the work I've done on my band's website,
|
40
|
+
[TheWonderBars.com](http://thewonderbars.com). I didn't want to have to
|
41
|
+
maintain two versions of press data, and since we had an active Facebook
|
42
|
+
page that was chock full of content well before the site was designed and
|
43
|
+
deployed, I considered using Facebook as our main "database", rather
|
44
|
+
than a locally-maintained SQL database of some kind. At this point in
|
45
|
+
time, TheWonderBars.com does not require a database in order to
|
46
|
+
function.
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
All contributions will be examined and considered. Anyone can feel free
|
51
|
+
to make their voice known by contributing to the project. Click the
|
52
|
+
"Fork" button on the top of the screen (register a GitHub account if you
|
53
|
+
don't already have one), create a feature branch, and submit the changes
|
54
|
+
back to me in the form of a pull request.
|
55
|
+
|
56
|
+
You must write tests for your feature or bug fix and they must pass.
|
57
|
+
|
58
|
+
## License
|
59
|
+
|
60
|
+
Copyright 2012 Tom Scott
|
61
|
+
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
63
|
+
a copy of this software and associated documentation files (the
|
64
|
+
"Software"), to deal in the Software without restriction, including
|
65
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
66
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
67
|
+
permit persons to whom the Software is furnished to do so, subject to
|
68
|
+
the following conditions:
|
69
|
+
|
70
|
+
The above copyright notice and this permission notice shall be
|
71
|
+
included in all copies or substantial portions of the Software.
|
72
|
+
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
74
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
75
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
76
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
77
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
78
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
79
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'BlueVelvet'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Configure the dummy app with a dummy Facebook page."
|
38
|
+
task :configuration do
|
39
|
+
cp "config/facebook.yml", "test/dummy/config/facebook.yml"
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree ../facebook
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree ../facebook
|
13
|
+
*/
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# The interim between the Facebook page data and the end user, this defines the internal API
|
2
|
+
# used by the Rails app to call Facebook attributes as if they were controller actions.
|
3
|
+
class Facebook::PageController < ApplicationController
|
4
|
+
# Much like method_missing, this method is called when ApplicationController can't find
|
5
|
+
# the hard-coded action. For this controller specifically, it will be called on all routing
|
6
|
+
# requests to +facebook/page+.
|
7
|
+
#
|
8
|
+
# This method retrieves any attribute from the Facebook page and displays it with a consistent
|
9
|
+
# partial template. The template used is page_attribute, which creates a +<div class="facebook_attribute"
|
10
|
+
# id="{whatever-facebook-attribute-you-used}>+ and renders it with the contents of the Facebook
|
11
|
+
# attribute. Because this method makes use of a partial, it can be called in place of any other
|
12
|
+
# +render+ or +redirect_to+ call, from any part of the application like so:
|
13
|
+
#
|
14
|
+
# class InfoController < ApplicationController
|
15
|
+
# def about
|
16
|
+
# render 'facebook/page#description'
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Or, you may route to the action:
|
21
|
+
#
|
22
|
+
# get "/about" => "facebook/page#description"
|
23
|
+
#
|
24
|
+
# Note that using routes.rb is easier, but binds you to using CSS as the main method of
|
25
|
+
# customization. You may override our styles if you simply create styles for
|
26
|
+
# +.facebook_attribute+, and for individual pages the name of the attribute as an ID.
|
27
|
+
def action_missing attribute_name
|
28
|
+
if facebook.has_attribute? attribute_name
|
29
|
+
attribute_content = facebook.send(:"#{attribute_name}").gsub(/\n/, "<br>")
|
30
|
+
attribute = Facebook::Attribute.new \
|
31
|
+
name: attribute_name.parameterize,
|
32
|
+
title: attribute_name.titleize,
|
33
|
+
body: attribute_content
|
34
|
+
render partial: 'page_attribute', locals: { attribute: attribute }
|
35
|
+
else
|
36
|
+
render partial: 'attribute_not_found', locals: { attribute_name: "#{attribute_name}" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
# Shorthand accessor for the omnipresent Facebook client
|
42
|
+
def facebook
|
43
|
+
@facebook_client ||= Facebook::Client.new
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def use_layout
|
48
|
+
if request.xhr?
|
49
|
+
false
|
50
|
+
else
|
51
|
+
'application'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# A client for the Facebook Graph API which looks for data on a specific Facebook page or profile. It
|
2
|
+
# reads from the YAML configuration stored in `config/facebook.yml` for the current Rails environment.
|
3
|
+
class Facebook::Client
|
4
|
+
attr_reader :graph, :oauth
|
5
|
+
attr_accessor :page
|
6
|
+
|
7
|
+
# Connects to the open graph.
|
8
|
+
def initialize options={}
|
9
|
+
@oauth = Koala::Facebook::OAuth.new \
|
10
|
+
Facebook.config[:app_id], Facebook.config[:secret_key]
|
11
|
+
@graph = Koala::Facebook::API.new access_token
|
12
|
+
@page = @graph.get_object(Facebook.config[:page_id])
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_attribute? attribute
|
16
|
+
@page["#{attribute}"].present?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets any attribute from Facebook, as long as it's within the filter, and returns it.
|
20
|
+
def method_missing attribute
|
21
|
+
if @page.present?
|
22
|
+
if self.has_attribute? attribute
|
23
|
+
@page["#{attribute}"]
|
24
|
+
else
|
25
|
+
raise Facebook::AttributeNotFound
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise Facebook::PageNotFound
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def access_token
|
35
|
+
@oauth.get_app_access_token
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Facebook
|
40
|
+
# Thrown when the Facebook page doesn't have the attribute requested.
|
41
|
+
class AttributeNotFound < StandardError; end
|
42
|
+
|
43
|
+
# Thrown when the Facebook page can't be found
|
44
|
+
class PageNotFound < StandardError; end
|
45
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<div id="error">
|
2
|
+
<h2>Attribute Not Found</h2>
|
3
|
+
<p>
|
4
|
+
We're sorry, but the requested attribute <strong><%= attribute_name %></strong>
|
5
|
+
could not be found on this Facebook page. Either it has not been filled in yet,
|
6
|
+
or the page author has opted not to publicize its contents.
|
7
|
+
</p>
|
8
|
+
</div>
|
data/config/facebook.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# This is your Facebook configuration file. Put your Facebook app info in this file
|
2
|
+
# and it will be read by the Rails application. and BlueVelvet.
|
3
|
+
|
4
|
+
development:
|
5
|
+
app_id: 149744351829141
|
6
|
+
secret_key: 0a8de678e00605bda2c583ea187d197e
|
7
|
+
page_id: 137797553025194
|
8
|
+
|
9
|
+
test:
|
10
|
+
app_id: 149744351829141
|
11
|
+
secret_key: 0a8de678e00605bda2c583ea187d197e
|
12
|
+
page_id: 137797553025194
|
13
|
+
|
14
|
+
production:
|
15
|
+
app_id: 149744351829141
|
16
|
+
secret_key: 0a8de678e00605bda2c583ea187d197e
|
17
|
+
page_id: 137797553025194
|
18
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'koala'
|
2
|
+
|
3
|
+
module Facebook
|
4
|
+
def self.config
|
5
|
+
if Rails.env.staging?
|
6
|
+
ActiveSupport::HashWithIndifferentAccess.new \
|
7
|
+
app_id: ENV['FB_APP_ID'],
|
8
|
+
secret_key: ENV['FB_SECRET_KEY'],
|
9
|
+
page_id: ENV['FB_PAGE_ID']
|
10
|
+
else
|
11
|
+
ActiveSupport::HashWithIndifferentAccess.new \
|
12
|
+
YAML.load_file(Rails.root.join("config/facebook.yml"))[Rails.env]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Koala::Facebook::OAuth.class_eval do
|
18
|
+
def initialize_with_default_settings(*args)
|
19
|
+
case args.size
|
20
|
+
when 0, 1
|
21
|
+
raise "application id and/or secret are not specified in the config" unless Facebook.config[:app_id].present? && Facebook.config[:secret_key].present?
|
22
|
+
initialize_without_default_settings(Facebook.config[:app_id], Facebook.config[:secret], args.first)
|
23
|
+
when 2, 3
|
24
|
+
initialize_without_default_settings(*args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method_chain :initialize, :default_settings
|
29
|
+
end
|
data/config/routes.rb
ADDED
data/lib/blue_velvet.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Creates YAML configuration in +config/facebook.yml+ which helps you
|
2
|
+
# connect to the Facebook platform. Required inherently by Koala, which
|
3
|
+
# forms the backend of this gem.
|
4
|
+
class Facebook::ConfigGenerator < Rails::Generators::Base
|
5
|
+
# Edit this file with your Facebook app details
|
6
|
+
def create_yaml_file
|
7
|
+
copy_file "config/facebook.yml", "#{Rails.root}/config/facebook/yml"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Don't touch this file, all it does is read from YAML and instantiate
|
11
|
+
# Koala.
|
12
|
+
def create_initializer
|
13
|
+
copy_file "config/initializers/koala.rb", "#{Rails.root}/config/initializers/koala.rb"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Copies internal gem views for use in the main app. Any view files
|
2
|
+
# encountered in the app in the same location as those in the engine
|
3
|
+
# will override the default views, so this generator allows you to
|
4
|
+
# customize the display of your Facebook content.
|
5
|
+
class Facebook::ViewsGenerator < Rails::Generators::Base
|
6
|
+
def copy_page_views
|
7
|
+
views_dir = File.join(__FILE__, 'app', 'views', 'facebook')
|
8
|
+
Dir[views_dir].each do |view_file_path|
|
9
|
+
view_file = File.basename view_file_path
|
10
|
+
app_path = "#{Rails.root}/app/views/facebook/#{view_file}"
|
11
|
+
copy_file view_file_path, app_path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|