virgo-gadgeteer 0.2.2

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.
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Copyright (c) 2009 Virgo Systems Kft., released under the MIT license
data/README.rdoc ADDED
@@ -0,0 +1,49 @@
1
+ Gadgeteer simplifies OpenSocial Gadget development by giving you helpers you can use in your Rails application to verify Signed Requests and access OpenSocial data.
2
+
3
+ = Requirements
4
+
5
+ Gadgeteer requires the {oauth gem}[http://github.com/pelle/oauth/tree/master] (0.2.7+).
6
+
7
+ *Note*: the current version of the oauth gem doesn't comply completely with the OAuth standard, and also doesn't work with Rails 2.3</tt>. You can use {lackac's fork}[http://github.com/lackac/oauth/tree/master], until the fixes are merged in.
8
+
9
+ = Usage
10
+
11
+ You can configure the secrets and public keys used by your application two ways.
12
+
13
+ For consumer secrets you can put your consumer key/secret pairs into <tt>config/oauth_secrets.yml</tt>:
14
+
15
+ key: secret
16
+
17
+ or you could setup those in your ApplicationController:
18
+
19
+ class ApplicationController < ActionController::Base
20
+
21
+ oauth_secrets['key'] = 'secret'
22
+
23
+ end
24
+
25
+ For public keys you can put the certificates into <tt>config/certs</tt> with <tt>.cert</tt> extension, or you could setup the public keys in your ApplicationController by creating a <tt>OpenSSL::PKey::RSA</tt> object and adding it to the <tt>public_keys</tt> hash:
26
+
27
+ class ApplicationController < ActionController::Base
28
+
29
+ public_keys['example.com'] = OpenSSL::PKey::RSA.new(OpenSSL::X509::Certificate.new(CERT).public_key)
30
+
31
+ end
32
+
33
+ You can use the <tt>verify_signature</tt> method as a before_filter in your controllers to make sure the signed requests are correct:
34
+
35
+ class SecretNotesController < ActionController::Base
36
+
37
+ before_filter :verify_signature
38
+
39
+ end
40
+
41
+ The correct secret or public key will be used for verification based on the current request. If the <tt>xoauth_signature_publickey</tt> parameter is set, the corresponding public key will be used. Otherwise the consumer secret connected to the key found in the <tt>oauth_consumer_key</tt> parameter will be used. The singature will be verified based on this key/secret pair and the singature method set in the parameters.
42
+
43
+ If there are OpenSocial related request parameters, you can access them with the <tt>open_social</tt> method:
44
+
45
+ def index
46
+ @secret_notes = SecretNote.find_by_profile_id(open_social[:viewer_id])
47
+ end
48
+
49
+ Copyright (c) 2009 Virgo Systems Kft., released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rcov/rcovtask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "gadgeteer"
10
+ s.summary = %Q{Making it easy to develop OpenSocial gadgets with Rails or Sinatra.}
11
+ s.email = "bacsi.laszlo@virgo.hu"
12
+ s.homepage = "http://github.com/virgo/gadgeteer"
13
+ s.description = "Making it easy to develop OpenSocial gadgets with Rails or Sinatra."
14
+ s.authors = ["Laszlo Bacsi"]
15
+ s.executables = ["gadgeteer"]
16
+ s.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*', 'templates/*', 'javascripts/*.js', 'rails/*'].to_a
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
21
+
22
+ Rake::TestTask.new do |t|
23
+ t.libs << 'lib'
24
+ t.pattern = 'test/**/*_test.rb'
25
+ t.verbose = false
26
+ end
27
+
28
+ Rake::RDocTask.new do |rdoc|
29
+ rdoc.rdoc_dir = 'rdoc'
30
+ rdoc.title = 'gadgeteer'
31
+ rdoc.options << '--line-numbers' << '--inline-source'
32
+ rdoc.rdoc_files.include('README*')
33
+ rdoc.rdoc_files.include('lib/**/*.rb')
34
+ end
35
+
36
+ Rcov::RcovTask.new do |t|
37
+ t.libs << 'test'
38
+ t.test_files = FileList['test/**/*_test.rb']
39
+ t.verbose = true
40
+ end
41
+
42
+ task :default => :rcov
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 2
3
+ :major: 0
4
+ :minor: 2
data/bin/gadgeteer ADDED
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $usage = <<USAGE
4
+ == Synopsis
5
+ This script generates some files for you to start coding your gadget with.
6
+
7
+ == Examples
8
+ For a Rails project:
9
+ gadgeteer --rails Gadget
10
+
11
+ For a Sinatra project:
12
+ gadgeteer --sinatra Gadget
13
+
14
+ == Usage
15
+ gadgeteer <--rails|--sinatra> [options] ModelName
16
+
17
+ For help use: gadgeteer -h
18
+
19
+ == Options
20
+ -h, --help Displays help message
21
+ -V, --version Display the version, then exit
22
+ -q, --quiet Output as little as possible, overrides verbose
23
+ -v, --verbose Verbose output
24
+ -r, --rails Generates files for a Rails app
25
+ -s, --sinatra Generates files for a Sinatra app
26
+ -f, --force Overwrite existing files
27
+ -a, --author me Name of the author (for gadget.xml)
28
+ -e, --email i@me.us Email address of the author (for gadget.xml)
29
+
30
+ == Author
31
+ László Bácsi
32
+
33
+ == Copyright
34
+ Copyright (c) 2009 Virgo Systems Kft. Licensed under the MIT License:
35
+ http://www.opensource.org/licenses/mit-license.php
36
+ USAGE
37
+
38
+ require 'optparse'
39
+ require 'rdoc/usage'
40
+ require 'ostruct'
41
+ require 'date'
42
+
43
+ require 'erb'
44
+ require 'yaml'
45
+ require 'activesupport'
46
+
47
+ module Gadgeteer
48
+ class App
49
+ VERSION_HASH = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'VERSION.yml'))
50
+ VERSION = "#{VERSION_HASH[:major]}.#{VERSION_HASH[:minor]}.#{VERSION_HASH[:patch]}"
51
+
52
+ MAPPING = {
53
+ :rails => {
54
+ :files => {
55
+ "gadget.yml" => "config/:singular.yml",
56
+ "gadget.rb" => "app/models/:singular.rb",
57
+ "gadget.haml" => "app/views/:plural/show.xml.haml",
58
+ "canvas.haml" => "app/views/:plural/_canvas.html.haml",
59
+ "gadget.js" => "public/javascripts/:singular.js",
60
+ "gadgets_controller.rb" => "app/controllers/:plural_controller.rb"
61
+ },
62
+ :help => <<-HELP
63
+ Add this to your Rails application routes:
64
+
65
+ map.resource ::singular
66
+ HELP
67
+ },
68
+ :sinatra => {
69
+ :files => {
70
+ "gadget.yml" => "config/:singular.yml",
71
+ "gadget.rb" => ":singular.rb",
72
+ "gadget.haml" => "views/:singular.haml",
73
+ "canvas.haml" => "views/canvas.haml",
74
+ "gadget.js" => "public/javascripts/:singular.js"
75
+ },
76
+ :help => <<-HELP
77
+ Add this to your Sinatra application:
78
+
79
+ require 'sinatra/gadgeteer'
80
+ require ':singular'
81
+
82
+ get '/:singular.xml' do
83
+ haml ::singular
84
+ end
85
+ HELP
86
+ }
87
+ }
88
+
89
+ attr_reader :options
90
+
91
+ def initialize(arguments, stdin)
92
+ @arguments = arguments
93
+ @stdin = stdin
94
+
95
+ # Set defaults
96
+ @options = OpenStruct.new
97
+ @options.verbose = true
98
+ @options.quiet = false
99
+ @options.author = "Calvin"
100
+ @options.email = "calvin@example.com"
101
+ end
102
+
103
+ # Parse options, check arguments, then process the command
104
+ def run
105
+ if parsed_options? && arguments_valid?
106
+ process_arguments
107
+ process_command
108
+ else
109
+ output_usage
110
+ end
111
+ end
112
+
113
+ protected
114
+
115
+ def parsed_options?
116
+ # Specify options
117
+ opts = OptionParser.new
118
+ opts.on('-V', '--version') { output_version ; exit 0 }
119
+ opts.on('-h', '--help') { output_help }
120
+ opts.on('-v', '--verbose') { @options.verbose = true }
121
+ opts.on('-q', '--quiet') { @options.quiet = true }
122
+ opts.on('-r', '--rails') { @options.rails = true }
123
+ opts.on('-s', '--sinatra') { @options.sinatra = true }
124
+ opts.on('-f', '--force') { @options.force = true }
125
+ opts.on('-a', '--author AUTHOR') { |x| @options.author = x }
126
+ opts.on('-e', '--email EMAIL') { |x| @options.email = x }
127
+
128
+ opts.parse!(@arguments) rescue return false
129
+
130
+ process_options
131
+ end
132
+
133
+ # Performs post-parse processing on options
134
+ def process_options
135
+ @options.verbose = false if @options.quiet
136
+ @options.rails ^ @options.sinatra
137
+ end
138
+
139
+ # True if required arguments were provided
140
+ def arguments_valid?
141
+ true if @arguments.length == 1
142
+ end
143
+
144
+ # Setup the arguments
145
+ def process_arguments
146
+ @options.model = @arguments.first.classify
147
+ @options.title = @options.model.underscore.humanize
148
+ @options.singular = @options.model.underscore
149
+ @options.plural = @options.model.tableize
150
+ end
151
+
152
+ def output_help
153
+ output_version
154
+ usage_and_exit! #exits app
155
+ end
156
+
157
+ def output_usage
158
+ usage_and_exit! # gets usage from comments above
159
+ end
160
+
161
+ # Shamefully stolen from RDoc#usage. We cannot use RDoc::usage because
162
+ # RubyGems will call this from a wrapper, and #usage is hardcoded to look
163
+ # at the top-level file instead of the current one. I have changed this
164
+ # code to instead just parse a string.
165
+ def usage_and_exit!
166
+ markup = SM::SimpleMarkup.new
167
+ flow_convertor = SM::ToFlow.new
168
+
169
+ flow = markup.convert($usage, flow_convertor)
170
+
171
+ options = RI::Options.instance
172
+ formatter = options.formatter.new(options, "")
173
+ formatter.display_flow(flow)
174
+
175
+ exit(0)
176
+ end
177
+
178
+ def output_version
179
+ puts "#{$0} version #{VERSION}"
180
+ end
181
+
182
+ def process_command
183
+ key = @options.rails ? :rails : :sinatra
184
+ file_mapping = MAPPING[key][:files]
185
+ help = MAPPING[key][:help]
186
+
187
+ # Templates
188
+ file_mapping.each do |filename, target|
189
+ template = File.join(File.dirname(__FILE__), '..', 'templates', filename)
190
+ target = target.gsub(':singular', @options.singular).gsub(':plural', @options.plural)
191
+ if File.exists?(target) and not @options.force
192
+ puts "Skipping existing file #{target}" if @options.verbose
193
+ else
194
+ puts "Writing file #{target}" if @options.verbose
195
+ FileUtils.mkdir_p(File.dirname(target))
196
+ File.open(target, "w") do |f|
197
+ f.write(ERB.new(File.read(template), nil, "<>").result(binding))
198
+ end
199
+ end
200
+ end
201
+
202
+ # Javascript files
203
+ Dir[File.join(File.dirname(__FILE__), '..', 'javascripts', '*.js')].each do |jsfile|
204
+ filename = File.basename(jsfile)
205
+ target = "public/javascripts/#{filename}"
206
+ if File.exists?(target) and not @options.force
207
+ puts "Skipping existing file #{target}" if @options.verbose
208
+ else
209
+ puts "Writing file #{target}" if @options.verbose
210
+ FileUtils.mkdir_p(File.dirname(target))
211
+ FileUtils.cp jsfile, target
212
+ end
213
+ end
214
+
215
+ # Help
216
+ puts
217
+ puts(help.gsub(':singular', @options.singular).gsub(':plural', @options.plural))
218
+ end
219
+ end
220
+ end
221
+
222
+ # Create and run the application
223
+ app = Gadgeteer::App.new(ARGV, STDIN)
224
+ app.run
@@ -0,0 +1,230 @@
1
+ /*! Copyright (c) 2009 Virgo Systems Kft. (http://virgo.hu)
2
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3
+ *
4
+ * Version: 1.0.2
5
+ * Requires opensocial-jQuery 0.5+
6
+ */
7
+
8
+ (function($) {
9
+
10
+ $.gadgeteer = function(callback, options) {
11
+ // If called with callback, notify it if we're ready
12
+ if ($.isFunction(callback)) {
13
+ if ($.gadgeteer.options) {
14
+ return false;
15
+ } else {
16
+ $.gadgeteer.options = options = options || {};
17
+ }
18
+ $.gadgeteer.defaultTarget = options.defaultTarget || '#page';
19
+ $.gadgeteer.host = options.host || '';
20
+
21
+ // Setup link behaviours
22
+ $.gadgeteer.linkBehaviours = options.linkBehaviours || {};
23
+ if (!options.noAjaxLinks) {
24
+ $('a').livequery('click', function(e) {
25
+ $.gadgeteer.handleLinkBehaviour.call($(this), e);
26
+ }).removeAttr('onclick');
27
+ }
28
+
29
+ if (!options.noAjaxForms) {
30
+ // All forms will submit through an ajax call
31
+ $('form').livequery('submit', function(e) {
32
+ e.preventDefault();
33
+ var form = $(this);
34
+ var action = form.attr('action');
35
+ var target = form.hasClass('silent') ? null : $.gadgeteer.defaultTarget;
36
+ $.ajax({
37
+ url: action.charAt(0) == '/' ? $.gadgeteer.host + action : action,
38
+ type: form.attr('method') || 'GET',
39
+ data: [$.param(form.formToArray()), $.param($.gadgeteer.viewer.osParams()), $.param($.gadgeteer.owner.osParams())].join("&"),
40
+ dataType: 'html',
41
+ auth: 'SIGNED',
42
+ target: target
43
+ });
44
+ });
45
+ }
46
+
47
+ // Setup ajax event callbacks
48
+ $(document).ajaxSend(function(e, request, settings) {
49
+ if (settings.target) {
50
+ // TODO: make this customizable by `loading` callback in options
51
+ $(settings.target).append($.gadgeteer.loadingElem());
52
+ }
53
+ }).ajaxSuccess(function(e, request, settings) {
54
+ $.gadgeteer.currentUrl = request.url;
55
+ if (settings.target) {
56
+ var html = request.responseText;
57
+ $(settings.target).html(html);
58
+ }
59
+ // !iframe
60
+ $(window).adjustHeight();
61
+ // Do another adjustHeight in 250ms just to be sure
62
+ setTimeout(function() {$(window).adjustHeight();}, 250);
63
+ }).ajaxError(function(e, request, settings, exception) {
64
+ console.log(request, settings, exception);
65
+ if (settings.target) {
66
+ var html = request.responseText;
67
+ $(settings.target).html(html);
68
+ }
69
+ // !iframe
70
+ $(window).adjustHeight();
71
+ // Do another adjustHeight in 250ms just to be sure
72
+ setTimeout(function() {$(window).adjustHeight();}, 250);
73
+ }).ajaxComplete(function(e, request, settings) {
74
+ if (request.status.toString().charAt(0) == '3') {
75
+ var href = request.getResponseHeader('Location') || request.getResponseHeader('location');
76
+ // hackish way to determine if we have an array (depends on the fact that the real href must be longer than 1 char)
77
+ if (!href.charAt) href = href[0];
78
+ $.ajax({
79
+ url: href.charAt(0) == '/' ? $.gadgeteer.host + href : href,
80
+ type: 'GET',
81
+ data: $.param($.gadgeteer.viewer.osParams()) + '&' + $.param($.gadgeteer.owner.osParams()),
82
+ dataType: 'html',
83
+ auth: settings.auth,
84
+ target: settings.target
85
+ });
86
+ }
87
+ });
88
+
89
+ // Wait for everything to load then call the callback
90
+ setTimeout(function() {
91
+ if ($.gadgeteer.viewer && $.gadgeteer.owner) {
92
+ // Navigate away if params tell so
93
+ var params = gadgets.views.getParams();
94
+ var navTo = params.navigateTo;
95
+ if (navTo) {
96
+ $.gadgeteer.simpleRequest(navTo, params.signedNavigate);
97
+ } else {
98
+ callback();
99
+ }
100
+ } else {
101
+ setTimeout(arguments.callee, 50);
102
+ }
103
+ }, 50);
104
+
105
+ } else { // if called with no arguments it means we're initializing
106
+ // Get information about the viewer and owner
107
+ $.getData('/people/@viewer/@self', function(data, status) {
108
+ $.gadgeteer.viewer = data[0];
109
+ $.gadgeteer.viewer.osParams = function() {return $.gadgeteer._osParams.call($.gadgeteer.viewer, 'viewer')};
110
+ });
111
+ $.getData('/people/@owner/@self', function(data, status) {
112
+ $.gadgeteer.owner = data[0];
113
+ $.gadgeteer.owner.osParams = function() {return $.gadgeteer._osParams.call($.gadgeteer.owner, 'owner')};
114
+ });
115
+ }
116
+ }
117
+
118
+ $.extend($.gadgeteer, {
119
+ _osParams: function(name) {
120
+ var params = {};
121
+ for (var attr in this) {
122
+ if (!$.isFunction(this[attr])) {
123
+ var underscore = attr.replace(/([A-Z])/, '_$1').toLowerCase();
124
+ params['os_'+name+'_'+underscore] = this[attr];
125
+ }
126
+ }
127
+ return params;
128
+ },
129
+
130
+ loadingElem: function() {
131
+ if ($.gadgeteer.LOADING_ELEM) return $.gadgeteer.LOADING_ELEM;
132
+
133
+ // TODO: make this customizable
134
+ var loading = $('#loading');
135
+ if (loading.length < 1) {
136
+ loading = $('<div id="loading">Az oldal tölt <span class="ellipses">…</span></div>');
137
+ }
138
+ return $.gadgeteer.LOADING_ELEM = loading;
139
+ },
140
+
141
+ simpleRequest: function(href, signed) {
142
+ var params = {}
143
+ if (href.indexOf('os_viewer_id') == -1) params.os_viewer_id = $.gadgeteer.viewer.id;
144
+ if (href.indexOf('os_owner_id') == -1) params.os_owner_id = $.gadgeteer.owner.id;
145
+ if (signed) {
146
+ params = $.extend(false, params, $.gadgeteer.viewer.osParams(), $.gadgeteer.owner.osParams());
147
+ }
148
+ $.ajax({
149
+ type: 'GET',
150
+ data: $.param(params),
151
+ url: href.charAt(0) == '/' ? $.gadgeteer.host + href : href,
152
+ dataType: 'html',
153
+ auth: signed && 'SIGNED',
154
+ target: $($.gadgeteer.defaultTarget)
155
+ });
156
+ },
157
+
158
+ regularRequest: function(e) {
159
+ // regular request (i.e. normal anchor click through) is a no-op
160
+ },
161
+
162
+ ajaxRequest: function(e) {
163
+ e.preventDefault();
164
+ var host = document.location.host;
165
+ var link = $(this);
166
+ var href = link.attr('href');
167
+ var _href = link[0].getAttribute('href');
168
+
169
+ //hack for IE href attr bug
170
+ if (_href.match(host)) {
171
+ var l = _href.search(host)+host.length;
172
+ href = _href.substring(l);
173
+ }
174
+
175
+ if (href.charAt(0) == '/') href = $.gadgeteer.host + href;
176
+
177
+ var params = {};
178
+ var method = link.hasClass('post') ? 'post' : link.hasClass('put') ? 'put' : link.hasClass('delete') ? 'delete' : 'get';
179
+ if (method != 'get') params._method = method;
180
+ if (link.hasClass('signed'))
181
+ params = $.extend(false, params, $.gadgeteer.viewer.osParams(), $.gadgeteer.owner.osParams());
182
+ else
183
+ params = $.extend(false, params, {os_viewer_id: $.gadgeteer.viewer.id, os_owner_id: $.gadgeteer.owner.id});
184
+
185
+ var target = link.hasClass('silent') ? null : $.gadgeteer.defaultTarget;
186
+ $.ajax({
187
+ type: method == 'get' ? 'GET' : 'POST',
188
+ url: href,
189
+ data: params,
190
+ dataType: target ? 'html' : null,
191
+ auth: link.hasClass('signed') ? 'SIGNED' : null,
192
+ target: target
193
+ });
194
+ },
195
+
196
+ navigateRequest: function(view, params, ownerId, e) {
197
+ e.preventDefault();
198
+ view = gadgets.views.getSupportedViews()[view];
199
+ gadgets.views.requestNavigateTo(view, params, ownerId);
200
+ },
201
+
202
+ handleLinkBehaviour: function(e) {
203
+ var link = $(this);
204
+ var matched = false;
205
+ $.each($.gadgeteer.linkBehaviours, function(behaviour, callback) {
206
+ var match;
207
+ if ($.isFunction(callback) && (match = callback.call(link, e))) {
208
+ var params = match === true ? [] : ($.isFunction(match.push) ? match : Array(match));
209
+ params.push(e);
210
+ //console.log('calling ', behaviour, ' link behaviour for ', link, ' with ', params);
211
+ var handler = behaviour+'Request';
212
+ handler = $.gadgeteer.linkBehaviours.handlers && $.gadgeteer.linkBehaviours.handlers[handler] || $.gadgeteer[handler];
213
+ handler.apply(link, params);
214
+ matched = true;
215
+ return false;
216
+ }
217
+ });
218
+ if (!matched) {
219
+ var def = $.gadgeteer.linkBehaviours.defaultBehavior || 'ajax';
220
+ //console.log('calling DEFAULT ', def, ' link behaviour for ', link, ' with ', e);
221
+ $.gadgeteer[def+'Request'].call(link, e);
222
+ }
223
+ }
224
+
225
+ });
226
+
227
+ // Initialize gadgeteer
228
+ $($.gadgeteer);
229
+
230
+ })(jQuery);