Hoe Faye te gebruiken als een real-time push-server in Rails

Er zijn meerdere manieren om push-functionaliteit aan een applicatie toe te voegen, zoals Pushr en Pub-Nub, die vrij elegante en eenvoudige oplossingen zijn. Er zijn ook enkele meer geavanceerde opties. In dit artikel laat ik je zien hoe je Faye kunt gebruiken, een berichtensysteem dat zowel op Ruby als Node.js draait.


Stap 1 - Alles klaar maken

We gaan een eenvoudige chatservice bouwen. Ryan Bates heeft dit nu besproken op Railscast # 260, maar we gaan in deze zelfstudie een iets andere aanpak volgen. Eerst maken we een chatservice waarbij gebruikers een openbare ruimte binnengaan en iedereen met elkaar kan chatten. De tweede functionaliteit die we zullen toevoegen, zijn privéberichten. Daarnaast zullen we wat beveiliging integreren in onze implementatie met behulp van Ryan Bates private_pub edelsteen.

Zorg dat je een werkende opstelling hebt van Ruby en Ruby on Rails 3.1. Afgezien van dat, heb je dun nodig. Thin is een veelgebruikte Ruby-webserver en Faye vereist dat deze wordt uitgevoerd (dit werkt niet met de ingebouwde server van WEBrick, Rails). Je kunt Thin installeren, zoals:

edelsteen installeer dun

We moeten helemaal klaar zijn, dus laten we de applicatie maken:

rails nieuwe faye-tutorial

Voeg nu de faye edelsteen voor jou Gemfile:

 gem 'faye'

en loop bundel installeren om het te installeren. Faye moet op een afzonderlijke webserver worden uitgevoerd vanuit de webapplicatie zelf; om dit te bereiken, moet je een Rackup-configuratiebestand maken. Voeg een ... toe faye.ru bestand naar de root van het project en zorg ervoor dat het er zo uitziet:

 vereisen 'faye' bayeux = Faye :: RackAdapter.new (: mount => '/ faye',: timeout => 25) bayeux.listen (9292)

Dit bestand vertelt Rackup eenvoudig hoe de Faye-server moet worden opgestart. Probeer het uit om te zorgen dat het correct werkt. Voer dit uit in uw Terminal:

rack-up faye.ru -E-productie -s dun

Als u geen fouten ontvangt, bent u klaar om te gaan!


Stap 2 - Enkele basisverificatie

Om een ​​chattoepassing te maken, hebben we twee basisdingen nodig: gebruikers en een chatroom. We gaan onze gebruikers niet verifiëren, maar we moeten hen wel een manier bieden om hun gebruikersnaam in te stellen. Laten we dat creëren. Voer de volgende opdracht uit om een ​​sessie te maken controleur zodat we ze kunnen inloggen:

rails g controller-sessies nieuw maken

Hiermee worden sessies gemaakt controleur en twee methoden: nieuwe en creëren. Voeg deze routes toe aan uw routes.rb het dossier:

 get '/ login' => 'session # new',: as =>: login post '/ login' => 'sessions # create',: as =>: login

Deze twee routes zouden voor u duidelijk moeten zijn. Ga je gang en wijzig de app / views / sessies / new.html.erb het dossier:

 <%= form_tag login_path do |f| %> <%= label_tag :username %> <%= text_field_tag :username %> <%= submit_tag "Enter" %> <% end %>

En heb de creëren methode binnen de sessies controleur zien eruit als de volgende:

 def create session [: gebruikersnaam] = params [: gebruikersnaam] render: text => "Welcome # session [: gebruikersnaam]!" einde

Ga je gang en probeer het uit! Rennen rails server in de Terminal en wijs uw browser naar localhost: 3000 / login en voer een gebruikersnaam in. Je moet worden begroet door je aanvraag nadat je het formulier hebt verzonden.

Als je een uitdaging wilt, kun je Omniauth gebruiken om een ​​Twitter- of Facebook-integratie toe te voegen voor deze stap!


Stap 3 - De chatroom

Nu we wat basisverificatie hebben, laten we een chatruimte toevoegen. Voer de volgende opdracht uit om een ​​chat te maken controleur:

rails genereren controller chats room

Dit genereert onze chats controleur en een kamer methode. We moeten een aantal routes toevoegen om onze chat te laten werken, dus voeg deze regel toe aan uw routes.rb het dossier:

 get '/ chatroom' => 'chats # room',: as =>: chat

Deze route leidt onze gebruiker naar de chatroom en laat ze berichten plaatsen via een eenvoudig formulier. Wijzig de kamer methode voor de chats controleur en laat het er zo uitzien:

 def kamer redirect_to login_path tenzij sessie [: gebruikersnaam] eindigt

Dit zorgt ervoor dat gebruikers een gebruikersnaam instellen als ze willen chatten. Laten we nu de kamer zelf maken! Voeg dit toe aan de app / views / chats / room.html.erb:

 

Welkom in de chat room <%= session[:username] %>!

Dit is een eenvoudige structuur voor de kamer. Het formulier aan het einde wordt beheerd door een JavaScript-code die het bericht naar de chatroom publiceert.

Om berichten naar de ruimte te plaatsen, moeten we JavaScript toevoegen aan onze weergave. Voeg eerst Faye's bibliotheek toe aan app / views / layouts / application.html.erb:

 <%= javascript_include_tag "http://localhost:9292/faye.js" %>

Voeg vervolgens het volgende toe aan het begin van de room.html.erb uitzicht:

 

Deze methode neemt het bericht op in het formulier dat we eerder hebben toegevoegd (wanneer het is ingediend) en stuurt de gebruikersnaam en het bericht van de auteur naar het kanaal "/ messages / public" in een JSON-object. Kanalen zijn Faye's manier om berichten te verzenden. Een gebruiker abonneert zich op een kanaal en ontvangt alle berichten die eraan zijn verzonden. In ons geval is er maar één kanaal, namelijk het openbare kanaal. Laten we de code een beetje meer ontleden:

  1. Eerst starten we een nieuwe Faye-client op en verbinden deze met onze Faye-server.
  2. Vervolgens verwerken we de inzending van het formulier. Wanneer de gebruiker op de enter-toets drukt of op 'Verzenden' klikt, publiceren we het bovengenoemde JSON-object met de afzender van het bericht en het bericht zelf naar het openbare kanaal.
  3. Hierna wissen we het berichtvenster en return false, om te voorkomen dat het formulier daadwerkelijk wordt ingediend en dat de pagina wordt vernieuwd.

Dit zal berichten publiceren naar de chatroom, maar onze verbonden gebruikers kunnen deze niet ontvangen, omdat hun browsers niet zijn geabonneerd op het kanaal. Dit wordt bereikt door een beetje meer JavaScript. Maak het JavaScript-blok aan app / views / chats / room.html.erb ziet er zo uit:

 

Dit maakt eenvoudig verbinding met de Faye-server en schrijft zich in voor het "/ messages / public" -kanaal. De callback die we bieden, ontvangt de verzonden berichten. gegevens zal het JSON-object zijn dat we eerder hebben gepubliceerd, dus gebruiken we dat eenvoudig om een ​​te maken tag met het bericht in, en voeg het toe aan de chatroom-container.

Je zou nu een eenvoudige chat moeten hebben! Voer zowel Faye- als de Rails-server uit en open twee browsers (of een incognitovenster op Chrome bijvoorbeeld). U kunt twee verschillende gebruikersnamen invoeren en uw chat testen. Berichten moeten vrijwel direct in de chatroom verschijnen zodra u ze verzendt.


Stap 4 - Persoonlijke berichten toevoegen

Op dit moment kunnen gebruikers met elkaar chatten, maar alle berichten zijn openbaar. Laten we een aantal eenvoudige functies toevoegen waarin mensen persoonlijke berichten kunnen sturen, door de gebruikersnaam van de ontvanger te noemen - een soort van like in Twitter. Om dit te laten werken, gaan we onze gebruikers abonneren op hun eigen kanalen, dus zij zijn de enigen die er berichten van kunnen ontvangen. Maak jouw app / views / chats / room.html.erb JavaScript ziet er zo uit:

 

Zoals je ziet, abonneren we op twee Faye-kanalen: één is het openbare kanaal en de tweede is een kanaal, genaamd "/ messages / private / USERNAME" (merk op dat we de gebruikersnaam in de Rails-sessie gebruiken). Op deze manier, wanneer iemand die gebruiker vermeldt, in plaats van het bericht via het openbare kanaal te verzenden, verzenden we het via het privékanaal van de ontvanger, zodat alleen die persoon het kan lezen. We voegen er ook enkele eenvoudige stijlen aan toe, zodat deze vetgedrukt worden weergegeven.

Een ander ding dat is veranderd, is de code die berichten publiceert. We controleren eerst of het bericht een privébericht is, door een eenvoudige reguliere expressie toe te passen die op zoek is naar een vermelding. Als dat zo is, publiceren we een bericht naar het specifieke kanaal van de ontvanger. Zo niet, dan doen we precies wat we eerder deden - publiceer het op het openbare kanaal.

Probeer het nu! Stuur berichten naar andere gebruikers door ze te vermelden, je zou ze alleen in het chatvenster van de ontvanger moeten zien.


Stap 5 - Sommige kanttekeningen

Deze implementatie heeft zijn gebreken. Ten eerste controleren we niet of de gebruikersnaam die een persoon kiest, al in gebruik is. Dit zou betekenen dat iedereen dezelfde gebruikersnaam als iemand anders kan invoeren en berichten kan verzenden die doen alsof ze ze zijn en zelfs hun privéberichten kunnen ontvangen! Dit probleem kan eenvoudig worden opgelost door een soort verificatiesysteem toe te voegen of door de gebruikersnamen die momenteel in gebruik zijn in een database op te slaan. Ik ga dit niet behandelen in de tutorial van vandaag, maar het zou redelijk gemakkelijk voor je moeten zijn om het te implementeren. Als je hulp nodig hebt, kun je een reactie achterlaten en we helpen!

Het tweede voorbehoud is niet zo voor de hand liggend. Het probleem met onze implementatie is dat iedereen de JavaScript on the fly zou kunnen manipuleren (bijvoorbeeld met behulp van Firebug) om zichzelf te abonneren op elk gewenst kanaal (zelfs privékanalen) en dat ze berichten aan hen zouden kunnen publiceren die doen alsof ze iemand anders zijn. Dit is niet zo gemakkelijk opgelost als de eerste fout die ik heb aangegeven (als we dit handmatig zouden oplossen), maar Ryan Bates heeft een juweeltje gemaakt dat deze taak tot een makkie maakt en onze app is zeer veilig.

De edelsteen wordt private_pub genoemd; het verbiedt in feite elke gebruiker om te publiceren naar kanalen met JavaScript, wat betekent dat alleen de Rails-app in staat is om naar hen te publiceren. Dit voegt enige beveiliging toe, omdat een kwaadwillende gebruiker niet in staat zou zijn om naar privé-kanalen te publiceren. Een ander ding dat dit juweeltje oplost is abonnementen. Met private_pub kan een gebruiker alleen berichten ontvangen van kanalen waarop we ze abonneren, dus kunnen ze geen abonnement handmatig toevoegen, waardoor het hele probleem wordt opgelost.

Dus laten we het toevoegen aan onze Gemfile:

 gem 'private_pub',: git => 'git: //github.com/ryanb/private_pub.git'

rennen bundel installeren installeer de edelsteen en voer de generator uit om de configuratiebestanden te maken:

rails g private_pub: installeren

U ontvangt een conflictwaarschuwing bij het uitvoeren van deze opdracht (private_pub probeert het te overschrijven faye.ru het dossier). Typ gewoon "Y" en druk op enter, want het is nodig om dat bestand te overschrijven. U moet ook de. Verplaatsen public / private_pub.js bestand naar de app / assets / javascripts map. En het laatste ding: verwijder de regel die inclusief is faye.js op application.html.erb, omdat Private Pub het automatisch bevat. Zorg ervoor dat u beide servers (rails en faye) opnieuw opstart.

Nu moeten we enkele wijzigingen aanbrengen. Eerst wordt het abonneren van een gebruiker op een kanaal anders gedaan met private_pub. Bewerk app / views / chats / room.html.erb en voeg het volgende toe vóór het JavaScript-blok:

 <%= subscribe_to "/messages/public" %> <%= subscribe_to "/messages/private/#session[:username]" %>

Dit is private_pubmanier om je te abonneren op kanalen. Hiermee machtigt u de gebruiker om berichten te ontvangen via het kanaal dat u opgeeft. Nu moeten we de JavaScript-code wijzigen in het volgende:

PrivatePub.subscribe ("/ messages / public", functie (gegevens)
$ (''). html (data.username + ":" + data.msg) .appendTo ('# chat_room');
);

PrivatePub.subscribe ( "/ messages / private /<%= session[:username] %>", functie (gegevens)
$ (''). addClass ('private'). html (data.username + ":" + data.msg) .appendTo ('# chat_room');
);

Het enige verschil hier is dat we PrivatePub gebruiken om zich rechtstreeks te abonneren op kanalen in plaats van de Faye-bibliotheek.

We zullen ook de manier moeten veranderen waarop we berichten publiceren. Met Private Pub kan alleen de Rails-applicatie berichten publiceren. De Javascript-bibliotheek kan geen berichten op zichzelf publiceren. Dit is een goede zaak, omdat we de volledige controle hebben over wie berichten publiceert en naar welk kanaal. Om dit te doen, moeten we het formulier voor het verzenden van berichten wijzigen in het volgende:

 <%= form_tag new_message_path, :remote => waar do%> <%= text_field_tag :message %> <%= submit_tag "Send" %> <% end %>

Dit is een AJAX-formulier, dus het vernieuwt de pagina niet wanneer het wordt ingediend. Het zal ook naar op zoek zijn new_message_path, dus zorg ervoor dat je dit toevoegt routes.rb:

 post '/ new_message' => 'chats # new_message',: as =>: new_message

Je moet ook een nieuwe methode op de chats-controller maken:

 def new_message # Controleer of het bericht privé is als de ontvanger = params [: message] .match (/@(.+) (. +) /) # Het is privé, stuur het naar het privékanaal van de ontvanger @channel = "/ berichten /private/#recipient.captures.first "@message = : gebruikersnaam => sessie [: gebruikersnaam],: msg => recipient.captures.second else # Het is openbaar, dus stuur het naar het openbare kanaal @kanaal = "/ messages / public" @message = : gebruikersnaam => sessie [: gebruikersnaam],: msg => params [: message] endresponse_to do | f | eind van f.js

Dit werkt heel erg op zijn JavaScript-tegenhanger. Het bepaalt of het bericht een vermelding bevat en verzendt het bericht naar het privékanaal van de ontvanger. Als dit niet het geval is, wordt het bericht via het openbare kanaal verzonden. Nu, dit is eigenlijk niet het verzenden van het bericht, het maakt gewoon twee variabelen die we vanuit de weergave moeten gebruiken om een ​​bericht te sturen. Private Pub staat niet toe dat u berichten verstuurt via de controleur (tenminste niet met de versie die ik heb gebruikt voor deze tutorial), dus ga je gang en maak het bestand aan app / views / chats / new_message.js.erb en voeg het volgende toe:

 // Berichtinvoer $ ('# message') wissen. Val ("); // Verzend het bericht <% publish_to @channel, @message %>

Hiermee wordt de eerste regel code uitgevoerd, het berichtvak gewist en gebeld publish_to, Private Pub zal sturen @bericht (die wordt omgezet naar een JSON-object wanneer het arriveert) naar @kanaal. Eenvoudig, he?

Ga je gang en probeer het eens. Het zou moeten werken zoals voorheen, alleen met nieuwe toegevoegde beveiliging!


Conclusie

Ik hoop dat dit artikel u enig inzicht heeft gegeven in hoe u Faye kunt gebruiken voor uw projecten. Ik zou adviseren dat je altijd Private Pub gebruikt, omdat het een belangrijk beveiligingsniveau toevoegt, tenzij je het echt niet nodig hebt.

Als je vragen hebt, stel ze dan gerust in de comments of stuur me een tweet!