play vs rails
DESCRIPTION
Comparison between Play Framework (Scala) and RubyOnRails (Ruby)TRANSCRIPT
Play 2.0
RubyOn
RailsX
by @danicuki
Documentationhttp://www.playframework.com/documentation/ http://guides.rubyonrails.org/
Installing
$ apt-‐get install ruby$ apt-‐get install rubygems$ gem install rails
$ apt-get install java$ wget play-2.1.0.zip$ unzip play-2.1.0.zip$ export PATH=$PATH:/pathToPlay
http://www.playframework.com/documentation/2.1.0/Installing
First App
$ rails new myapp$ play new myapp
Project Structureapp/ ! assets ! stylesheets ! javascripts ! controllers ! models ! views conf/ ! application.conf ! routes public/ project ! build.properties ! Build.scala ! plugins.sbt lib/ logs/ target/ ! scala-2.10.0 test/
app/ ! assets ! stylesheets ! javascripts ! controllers ! helpers ! mailers ! models ! views config/ ! application.rb ! routes.rb ! database.ymlconfig.ru dbGemFilelib/ log/ public/script/test/ tmp/vendor/
Console
Dependenciesproject/Build.scala
val appDependencies = Seq( "postgresql" % "postgresql" % "8.4-702.jdbc4" "org.mockito" % "mockito-all" % "1.8.5" % "test")
Gemfile
gem "mysql2"group :test do gem "webmock"end
Maven
RubyGems
Dependencies Support429.000 jars
mvnrepository.com
52,468 gemsrubygems.org
Views TemplatesSTE (Scala Template Engine)
ERB (Embedded Ruby)
<title> <%= t("site.title") %> -‐ <%= yield(:title) %></title><%= video['title'] %>
@(title: String)(content: Html)
<title> @Messages("site.title") - @title</title>@{(video \ "title")(0).text}
js / css
<%= javascript_include_tag :all, :cache => true %> <%= stylesheet_link_tag :all, :cache => true %>
<link rel="stylesheet" href="@routes.Assets.at("stylesheets/grid.css")"><link rel="stylesheet" href="@routes.Assets.at("stylesheets/style.css")"><link rel="stylesheet" href="@routes.Assets.at("stylesheets/galleriffic.css")"><script src="@routes.Assets.at("javascripts/applicaton.js")" type="text/javascript" /><script src="@routes.Assets.at("javascripts/jquery.js")" type="text/javascript" />
Links
<li class="menu"> <%= link_to "#{t('site.singer')}", {:controller => "cantora"} %></li>
<li class="menu"> <a href="@routes.Application.cantora">@Messages("site.singer")</a></li>
Routes
map.connect ':controller/:action/:id'
GET /cantora controllers.Application.cantoraGET /musicas controllers.Application.musicGET /agenda controllers.Application.showsGET /novidades controllers.Application.news
GET /clients/:id controllers.Clients.show(id: Long)
Controllers
class CantoraController < ApplicationControllerdef index
endendclass NewsController < ApplicationController def index endend
def cantora = Action { implicit request =>; Ok(views.html.cantora()) }def music = Action { implicit request =>; Ok(views.html.music()) }def shows = Action { implicit request => Ok(views.html.shows()) }def news = Action { implicit request => Ok(views.html.news()) }
/app/views/[controller_name]/index.erb.html
/app/views/[method_name].scala.html
Databases
config/database.yml:
development: adapter: mysql2 database: blog_development username: root password:
conf/application.conf:
db.default.driver=com.mysql.jdbc.Driverdb.default.url="jdbc:mysql://localhost/playdb"db.default.user=playdbuserdb.default.pass="a strong password"
Databases
Active Record:
Country.all.map do |c| if (c.name == “France”) France.new else if (c.pop > 1000000) BigCountry.new(c.name) else SmallCountry.new(c.name) endend
SQL API:
val countries = SQL("Select name,population from Country")().collect { case Row("France", _) => France() case Row(name:String, pop:Int) if(pop > 1000000) => BigCountry(name) case Row(name:String, _) => SmallCountry(name) }
val result: Int = SQL("delete from City where id = {id}").on(“id” -> 99).executeUpdate()
Country.delete(99)
Databases
Active Record:
Country.all.map do |c| if (c.name == “France”) France.new else if (c.pop > 1000000) BigCountry.new(c.name) else SmallCountry.new(c.name) endend
Country.delete(99)
OGH(Old Good Hibernate)
Migrations / Evolutions
class CreateUsers < ActiveRecord::Migration def up create_table :users do |t| t.string :email t.string :password t.timestamps end end def down drop_table :users endend
# --- !Ups CREATE TABLE Users ( id bigint(20) NOT NULL AUTO_INCREMENT, email varchar(255) NOT NULL, password varchar(255) NOT NULL, PRIMARY KEY (id)); # --- !DownsDROP TABLE Users; conf/evolutions/{database name}/{#}.sql
db/migrate/{timestamp}_create_users.rb
run: AUTOMATIC!
run: rake db:migrate
Web Services
class VideosController < ApplicationController def index uri = "http://gdata.youtube.com/feed.xml" video_feed = RestClient.get(uri) @videos = Hash.from_xml(video_feed)['feed']['entry'] endend
def videos = Action { implicit request => Async { val uri = "http://gdata.youtube.com/feed.xml" WS.url(uri).get().map { response => Ok(views.html.videos(response.xml \ "entry")) } }}
XML
@videos = Hash.from_xml(video_feed)['feed']['entry']
<% @videos.each do |video| %> <a href="<%= video['group']['player']['url'] %>"> <%= video['title'] %> </a><% end %>
Ok(views.html.videos(response.xml \ "entry"))
@(videos: scala.xml.NodeSeq)
@videos.map { video => <a href="@{ ((video \\ "group") \\ "player")(0).attribute("url")}"> @{(video \ "title")(0).text} </a>}
JSON
WS.url("https://graph.facebook.com/daniella.alcarpe/albums").get().map { response => val albuns = (response.json \ "data"). as[List[JsObject]].filter(album => (album \ "description").toString.equals("\"*\"")) val photos = albuns.map { album => WS.url("https://graph.facebook.com/" + (album \ "id").toString.replace("\"", "") + "/photos").get().map { response2 => (response2.json \ "data").as[List[JsObject]].map { photo => ((photo \ "images")(3) \ "source").toString.replace("\"", "") } } } Ok(views.html.photos(photos))}
@albuns_photos = {} @albuns = [] albuns = JSON.parse(RestClient.get("https://graph.facebook.com/daniella.alcarpe/albums"))["data"] albuns.each do |album| if (album['description'] == "*") photos = JSON.parse(RestClient.get("https://graph.facebook.com/#{album['id']}/photos"))["data"] albuns_photos = photos.map {|p| p["images"][3]["source"]} album['photos'] = albuns_photos @albuns << album end end
Cache
class VideosController < ApplicationController caches_action :index, :expires_in => 1.dayend
def videos = Cached("videos", 18000) { Action { ... }}
Unit Tests
test "my test" do array = [1, 2, 3] assert_equal 1, array.firstend
@Test def myTest() { val array = List(1, 2, 3) assert(array(0) === 1)}
Specs
describe "HelloWorldSpec" docontext "The 'Hello world' string should" do it "contain 11 characters" do "Hello world".size.should eq(11) end it "end with 'world'" do "Hello world".should end_with("world") endend
end
class HelloWorldSpec extends Specification { "The 'Hello world' string" should { "contain 11 characters" in { "Hello world" must have size(11) } "end with 'world'" in { "Hello world" must endWith("world") } }}
specs2
rspec
$ autospec
autotest:$ ~ test
Deploy
$ heroku create$ git push heroku master
In Procfile:web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.url=${DATABASE_URL} -Ddb.default.driver=org.postgresql.Driver
In project/Build.scala:val appDependencies = Seq( "postgresql" % "postgresql" % "8.4-702.jdbc4")
$ heroku create$ git push heroku master
Scala RubyX
by @danicuki
Adendo
Lambda
list.select do |el| el < 100end
list.filter(_ < 100)
list.filter { el: Int => (el < 100)}
Types
Dynamic:
a = Hash.newa = “BOO!”
Static:
val a = new HashMap[Int, String]
Pattern Matchingrequire 'case'def matchTest x case x when 7 "seven" when "string" 0 when Case::All[Integer] "no." when Case::Array[2,Case::Any] x[1..-‐1] endend
def myMatch(x: Any): Any = x match { case 7 => “seven” case “string” => 0 case y:Int => “no.” case 2 :: tail => tail}
Monkey Patch
class String def my_method "yess!!!" endend
puts "a".my_method # => yess!!!
class MySuperString(original: String) { def myMethod = "yess!!!"}
implicit def string2super(x: String) = new MySuperString(x)
println("a".myMethod) // => yess!!!
Dynamic Calls
class Animal def method_missing name, *args if args.empty? puts "Animal says " + name.to_s else puts "Animal wants to " + name.to_s + args.join(", ") end self endend
class Animal extends Dynamic { def _select_(name: String) = println("Animal says " + name) def _invoke_(name: String, args: Any*) = { println("Animal wants to " + name + args.mkString(", ")) this }}
val animal = new Animalanimal.qualk // => Animal says qualkanimal.say("hello") // => Animal wants to say hello
animal = Animal.newanimal.qualk # => Animal says : qualks !animal.say("hello") # => Animal wants to say hello
Modules / Traits
module PimpMyClass def my_method puts "my_method" endendclass IncludeModule include PimpMyClassendIncludeModule.new.my_method
trait PimpMyClass { def myMethod = println("myMethod")}class IncludeTrait extends PimpMyClass(new IncludeTrait).myMethod
Duck Typingclass Duck { def quack = ... def walk = ...}class Platypus { def quack = ... def walk = ...}
val duck = new Duckval platypus = new PlatypusActAsADuck(duck)ActAsADuck(platypus)
def ActAsADuck(a: { def quack; def walk })= { a.quack a.walk}
class Duck def quack; end def walk; endend
class Platypus def quack; end def walk; endend
duck = Duck.newplatypus = Platypus.newact_as_a_duck(duck)act_as_a_duck(platypus)
def act_as_a_duck animal animal.quack animal.walkend
Actors
Performance
0
20
40
60
80
2.07
39.74
70.74ScalaJRubyRuby
Source : http://shootout.alioth.debian.org
References✦ http://www.playframework.com/documentation✦ http://guides.rubyonrails.org/✦ http://www.agileandart.com/✦ http://www.slideshare.net/El_Picador/scala-vs-ruby
Obrigado!@danicuki
www.agileandart.com