functional widgets in rails
DESCRIPTION
TRANSCRIPT
![Page 1: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/1.jpg)
Functional widgets in Rails
Sebastian Sito
![Page 2: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/2.jpg)
Co to jest widget?
mała aplikacja wbudowana w stronę WWWmoże być funkcjonalna lub statyczna
![Page 3: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/3.jpg)
Przykład?
AlleWidgetGoogle GadgetsFacebook like buttons
![Page 4: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/4.jpg)
Na początek statyczny content
<script type="text/javascript" src="http://hostapp.com/api/widget/main"></script>
![Page 5: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/5.jpg)
Uwaga na Operę!
<script type="text/javascript" src="http://hostapp.com/api/widget/main.js"></script>
![Page 6: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/6.jpg)
Po stronie aplikacji
app/controllers/widget_controller.rb
class Api::WidgetController < ApplicationController layout :nil def main respond_to do |format| format.js end endend
![Page 7: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/7.jpg)
Po stronie aplikacji
app/views/api/widget/main.js.erb
document.write('<div id="widget">');<%= @title %>document.write('</div>');
![Page 8: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/8.jpg)
Personalizacja widgetu
app/controllers/widget_controller.rb
class Api::WidgetController < ApplicationController layout :nil before_filter :validate_api_key def main respond_to do |format| format.js end endend
![Page 9: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/9.jpg)
Personalizacja widgetu
config/routes.rbnamespace :api do match '/widget/:action/:api_key', :controller => 'widget', :api_key => /.*/end
app/controllers/api/widget_controller.rbdef validate_api_key render :text => 'Invalid API key' unless params[:api_key] and return render :text => 'Wrong API credentials' if not User.find_by_key(params[:api_key]) and returnend
![Page 10: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/10.jpg)
Personalizacja widgetu
<script type="text/javascript"> var __apiKey = "qcg35cgwhojpm839v5";</script><script type="text/javascript" src="http://hostapp.com/api/widget/main"></script>
![Page 11: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/11.jpg)
Widget funkcjonalny
Mamy problem: aplikacja i widget to dwie różne domeny!
![Page 12: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/12.jpg)
JSONP z pomocą
Często używany, żeby obejść problemy związane z komunikacją między domenami.
JSONvar xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { // success };};
xhr.open("GET", "http://somewhere", true);xhr.send();
![Page 13: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/13.jpg)
JSONP z pomocą
JSONPvar tag = document.createElement("script");tag.src = 'http://somewhere?callback=foo';
document.getElementsByTagName("head")[0].appendChild(tag);
![Page 14: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/14.jpg)
Jaka jest różnica?
Różnica w zapytaniu JSONP polega na tym, że odpowiedź jest przekazywana jako argument wywołania funkcji - stąd parametr callback.
WAŻNE!W odróżnieniu od zwykłego zapytania XHR w zapytaniu JSONP nie można wyłapać błędów - dostajemy poprostu ParseError
![Page 15: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/15.jpg)
Jaka jest różnica?
Różnica w zapytaniu JSONP polega na tym, że odpowiedź jest przekazywana jako argument wywołania funkcji - stąd parametr callback.
WAŻNE!W odróżnieniu od zwykłego zapytania XHR w zapytaniu JSONP nie można wyłapać błędów - dostajemy poprostu ParseError
Na szczęście jQuery robi to za nas!
![Page 16: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/16.jpg)
JSONP i Rails 3
Musimy obsłużyć:
http://hostapp.com/api/widget/action?callback=foo
![Page 17: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/17.jpg)
JSONP i Rails 3
Musimy obsłużyć:
http://hostapp.com/api/widget/action?callback=foo
Odpowiedź można sformułować tak:
render :json => @items.to_json, :callback => params[:callback]
![Page 18: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/18.jpg)
JSONP i Rails 3
Musimy obsłużyć:
http://hostapp.com/api/widget/action?callback=foo
Odpowiedź można sformułować tak:
render :json => @items.to_json, :callback => params[:callback]
Otrzymujemy błędy z powodu innej domeny z którą chcemy się połączyć?
![Page 19: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/19.jpg)
SameOriginPolicy
class Api::WidgetController < ApplicationController after_filter :set_access_control_headersend
![Page 20: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/20.jpg)
SameOriginPolicy
class Api::WidgetController < ApplicationController after_filter :set_access_control_headersend
def set_access_control_headers headers['Access-Control-Allow-Origin'] = '*' headers['Access-Control-Request-Method'] = '*'end
![Page 21: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/21.jpg)
Wszystko razem
Jeśli widget ma wiele funkcji, trzeba jakoś sprytnie ustalić sposób komunikacji.
![Page 22: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/22.jpg)
Wszystko razem
Jeśli widget ma wiele funkcji, trzeba jakoś sprytnie ustalić sposób komunikacji.
![Page 23: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/23.jpg)
Ładowanie widgetu
Pomysł polega na umieszczeniu całej logiki komunikacji i widoku w samym widgecie, dzięki czemu zaoszczędzimy na requestach (będziemy wysyłać i odbierać tylko dane)
<script type="text/javascript" src="http://hostapp.com/api/widget/main"></script>
app/views/api/widget/main.js.erb
if(window.Widget == undefined) { window.Widget = { // logic here }}
![Page 24: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/24.jpg)
Logika widgetu
if(window.Widget == undefined) { window.Widget = { Settings: {}, API: {}, Handlers: {}, GUI: {} }}
$(document).ready(function() {Widget.API.call('init', {});});
![Page 25: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/25.jpg)
Ustawienia
Settings: { api: { url: "http://hostapp.com/api/widget" }}
![Page 26: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/26.jpg)
API
API: { call: function(action, data) { var url = Widget.Settings.api.url + '/' + action; data['api_key'] = __apiKey; $.ajax({url: url,data: data,crossDomain: true, dataType: "jsonp", success: Widget.API.responseHandler });}
![Page 27: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/27.jpg)
Rails response
def widget_response(action, data) response_hash = {:action => action, :data => data} render :json => response_hash.to_json, :callback => params[:callback]end
![Page 28: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/28.jpg)
API
API: { responseHandler: function() { Widget.Handlers[action](response.data) }}
![Page 29: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/29.jpg)
Handlers
Handlers: { init: function(data) { // populate widget with data // and draw some GUI Widget.GUI.render(data); }}
![Page 30: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/30.jpg)
Jeszcze ciekawiej: IFRAME w widgecie
Co jeśli w swoim widgecie uruchomimy jeszcze inną stronę w IFRAME i będziemy chcieli mieć możliwość odpowiedzieć z niego na pewne zdarzenie w IFRAME?
![Page 31: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/31.jpg)
Jeszcze ciekawiej: IFRAME w widgecie
Co jeśli w swoim widgecie uruchomimy jeszcze inną stronę w IFRAME i będziemy chcieli mieć możliwość odpowiedzieć z niego na pewne zdarzenie w IFRAME?
Hash pooling
![Page 32: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/32.jpg)
Jeszcze ciekawiej: IFRAME w widgecie
Co jeśli w swoim widgecie uruchomimy jeszcze inną stronę w IFRAME i będziemy chcieli mieć możliwość odpowiedzieć z niego na pewne zdarzenie w IFRAME?
Hash pooling
Widget.interval = setInterval(handleWindowHash, 1000)
teraz z wewnątrz IFRAME zmieniamy hash okna nadrzędnego
window.location.hash = "trigger_sth"
![Page 33: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/33.jpg)
Przykład
Host Apphttp://rails-widget-example.heroku.com
Widget<!doctype html><html><head><title>Widget Client</title> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"> var __userSecret = '8102260836164404'; </script> <script type="text/javascript" src="http:// rails-widget-example.heroku.com/api/widget/main.js"> </script></head> <body><div><h1>Widget example</h1></div><div id="widget_placeholder"></div> </body></html>
![Page 34: Functional widgets in Rails](https://reader033.vdocuments.mx/reader033/viewer/2022052504/54941fa6b47959514d8b4a0c/html5/thumbnails/34.jpg)
Dzięki :)
E-mail [email protected]
Twitter @sebastiandreake
Kod aplikacji live https://github.com/dreake/widget