Spock + GEB vs. Robot Framework [closed] - frameworks

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
Previously I used Robot Framework to automate testing of applications, but a new client asked pay attention to Spock + GEB. I've never used it, but I need to quickly compare two of these tools and make a choice. Please help me to understand how they differ and what are the strengths / weaknesses of each.

I tell you about Geb, i use gebish for testing web-applications more 6 months. That's all his benefits:
Cross Browser Automation
jQuery-like API
Page Objects
Asynchronicity
Testing
Build System Integration
Now more details about each of them.
Cross Browser Automation
Geb leverages the WebDriver library for browser automation. This means that Geb works with any browser that WebDriver works with, and the list of browsers that WebDriver works with is growing all the time.
The core supported browsers are:
FireFox
Internet Explorer
Google Chrome
Opera
There is also experimental support for:
Chrome on Android
Safari on iPhone & iPad
WebDriver also supports remote drivers. This allows you to automate a browser running on another machine! This means you can easily run your test suite against an IE browser from the comfort of your Mac or Linux machine (and vice versa).
jQuery-like API
Geb takes inspiration from jQuery to provide a concise and effective way to get at content. This is called the Navigator API.
The dollar function can be used anywhere to select content based on CSS selectors, attribute matchers and/or indexes.
// CSS 3 selectors
$("div.some-class p:first[title='something']")
// Find via index and/or attribute matching
$("h1", 2, class: "heading")
$("p", name: "description")
$("ul.things li", 2)
// 'text' is special attribute for the element text content
$("h1", text: "All about Geb")
// Use builtin matchers and regular expressions
$("p", text: contains("Geb"))
$("input", value: ~/\d{3,}-\d{3,}-\d{3,}/)
// Chaining
$("div").find(".b")
$("div").filter(".c").parents()
$("p.c").siblings()
Page Objects
Geb has first class support for the Page Object pattern, leveraging Groovy's DSL capabilities to allow you the developer to easily define the interesting parts of your pages in a concise, maintanable and extensible manner.
import geb.Page
class LoginPage extends Page {
static url = "http://myapp.com/login"
static at = { heading.text() == "Please Login" }
static content = {
heading { $("h1") }
loginForm { $("form.login") }
loginButton(to: AdminPage) { loginForm.login() }
}
}
class AdminPage extends Page {
static at = { heading.text() == "Admin Section" }
static content = {
heading { $("h1") }
}
}
Asynchronicity
Modern web pages are full of asynchronous operations like AJAX requests and animations. Geb provides built in support for this fact of life.
Any content lookup, or operation can be wrapped in a waitFor clause.
waitFor {
$("p.status").text() == "Asynchronous operation complete!"
}
This will keep testing the condition for a certain amount of time (which is configurable) until it passes. The same technique can be used to just wait for the content, not necessarily for the content to have some characteristic.
def dynamicParagraph = waitFor { $("p.dynamically-added") }
dynamicParagraph.text() == "Added dynamically!"
You can also define that content should be implicitly waited for in the Content DSL for page objects
import geb.Page
class DynamicPage extends Page {
static content = {
dynamicParagraph(wait: true) { $("p.dynamically-added") }
}
}
With this definition, when dynamicParagraph is requested Geb will implictly wait for a certain amount of time for it to appear.
Testing
Geb provides integration modules for popular testing frameworks such as Spock, JUnit, TestNG, EasyB and Cucumber (via Cuke4Duke)
While Geb works great with all of these frameworks, it really shines with Spock. Spock is an innovative testing framework that is a great match for using with Geb. Using Spock + Geb gives you very clear, concise and easy to understand test specifications with very little effort.
import geb.Page
import geb.spock.GebSpec
class LoginSpec extends GebSpec {
def "login to admin section"() {
given:
to LoginPage
when:
loginForm.with {
username = "admin"
password = "password"
}
and:
loginButton.click()
then:
at AdminPage
}
}
Build System Integration
Geb is easy to integrate into any build system, and information and examples on integrating with the following build/project systems is available:
Gradle
Grails
Maven
You can look my Example (Spock+GEB) here: github
Read more about geb here: Official Site
Thanks!!!

Related

How to customize addContentItemDialog to restrict files over 10mb upload in IBM Content Navigator

I am customizing ICN (IBM Content Navigator) 2.0.3 and my requirement is to restrict user to upload files over 10mb and only allowed files are .pdf or .docx.
I know I have to extend / customize the AddContentItemDialog but there is very less detail on exactly how to do it, or any video on it. I'd appreciate if someone could guide.
Thanks
I installed the development environment but I am not sure how to extend the AddContentItemDialog.
public void applicationInit(HttpServletRequest request,
PluginServiceCallbacks callbacks) throws Exception {
}
I want to also know how to roll out the changes to ICN.
This can be easily extended. I would suggest to read the ICN red book for the details on how to do it. But it is pretty standard code.
Regarding rollout the code to ICN, there are two ways:
- If you are using plugin: just replace the Jar file on the server location and restart WAS.
- If you are using EDS: you need to redeploy the web service and restart WAS.
Hope this helps.
thanks
Although there are many ways to do this, one way indeed is tot extend, or augment the AddContentItemDialog as you qouted. After looking at the (rather poor IBM documentation) i figured you could probably use the onAdd event/method
Dojo/Aspect#around allows you to do exactly that, example:
require(["dojo/aspect", "ecm/widget/dialog/AddContentItemDialog"], function(aspect, AddContentItemDialog) {
aspect.around(AddContentItemDialog.prototype, "onAdd", function advisor(original) {
return function around() {
var files = this.addContentItemGeneralPane.getFileInputFiles();
var containsInvalidFiles = dojo.some(files, function isInvalid(file) {
var fileName = file.name.toLowerCase();
var extensionOK = fileName.endsWith(".pdf") || fileName.endsWith(".docx");
var fileSizeOK = file.size <= 10 * 1024 * 1024;
return !(extensionOK && fileSizeOK);
});
if (containsInvalidFiles) {
alert("You can't add that :)");
}else{
original.apply(this, arguments);
}
}
});
});
Just make sure this code gets executed before the actual dialog is opened. The best way to achieve this, is by wrapping this code in a new plugin.
Now on creating/deploying plugins -> The easiest way is this wizard for Eclipse (see also a repackaged version for newer eclipse versions). Just create a new arbitrary plugin, and paste this javascript code in the generated .js file.
Additionally it might be good to note that you're only limiting "this specific dialog" to upload specific files. It would probably be a good idea to also create a requestFilter to limit all possible uses of the addContent api...

Taking webpage screenshot on completion of a Cucumber Step Definition

I'm currently researching a way in which I can implement a screen capture method in my acceptance test suite in Scala Cucumber after each step definition is completed in the scenario.
I have already implemented a method that will take a screenshot of a webpage if one of the automation test fails by invoking the method in the after hooks class. This does work fine but this will only capture the web page once the entire scenario has been completed.
I wasn't sure if there was something like before and after hooks that could be applied to the steps instead of the scenario.
Hooks.scala
#After
def tearDown(result: Scenario){
if (result.isFailed) {
ifCurrentDriverTakesSnapshot {
takesSnapshot =>
Snapshotter.takeErrorSnapshot(takesSnapshot, result)
}
}
Snapshotter.Scala
def takeErrorSnapshot(takesScreenshot: TakesScreenshot, result: Scenario)
= {
try {
val screenshot = takesScreenshot.getScreenshotAs(OutputType.BYTES)
result.embed(screenshot, "image/png")
}
catch {
case e: WebDriverException =>
e.printStackTrace(System.err)
}
}
I would like to be able to do this in a class or method that can be called after each new page is opened or after each step definition. I could right a step definition that would handle the screen capture but I would like to do in a better way as I have 100's of test scenarios so it would be better to avoid adding a step for this in between every step definition as they have done in the below link.
Cucumber Java screenshots
If anyone could share some light on the matter i'd greatly appreciate it as I'm struggling to find much on the subject.
Thanks!!!

What are the options for REST frameworks in D? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I would like to use d to create a RESTful web application.
What are the most actively maintained and contributed projects that are worth considering? A short comparison of these web frameworks with pluses and minuses will be nice.
My search lead me to only one project, which seems like an excellent framework:
vibe.d
Are there other projects which are minimal in nature like sinatra?
I've heard good things about vibe.d http://vibed.org/
Though, I've never personally used it because I wrote my own libraries well before vibe came out.
https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff
vibe is better documented, so you might be better off going there, but here's how my libraries work:
cgi.d is the base web interface (use -version=embedded_httpd when compiling to use its own web server instead of CGI if you want), and I offer some RESTy stuff in a separate file called web.d. It depends on cgi.d, dom.d, characterencodings.d, and sha.d. You might also want database.d and mysql.d for connecting to a mysql database.
The way web.d works is you just write functions and it automatically maps them to url and formats data.
http://arsdnet.net/cgi-bin/apidemo/add-some-numbers
The source code to that portion is:
import arsd.web;
class MySite : ApiProvider {
export int addSomeNumbers(int a, int b) { return a+b; }
}
mixin FancyMain!MySite;
web.d automatically generates the form you see there, parses the url into the types given, and formats the return value into either html, json, or sometimes other things (for example, objects can be made into tables).
There is also an envelopeFormat url param that can wrap it in more json, best for machine consumption:
http://arsdnet.net/cgi-bin/apidemo/add-some-numbers?a=1&b=2&format=json&envelopeFormat=json
web.d.php in my github shows one way you can consume it, and web.d itself automatically generates javascript functions to call from the client:
MySite.addSomeNumbers(10, 20).get(function(answer) { alert("Server replied: " + answer); });
answer would be of the type returned by the D function.
If you don't want/need the automatic function wrapping, cgi.d alone gives access to the basic info and writing functions:
void requestHandler(Cgi cgi) {
// there's cgi.get["name"], cgi.post["name"], or cgi.request("name"), kinda like php
cgi.write("hello ", cgi.request("name"));
}
mixin GenericMain!requestHandler;
But yeah, most the documentation that exists for my library is just me talking about it on forums... I think once you've done one function it isn't hard to figure out, but I'm biased!
edit: copy/paste from my comment below since it is pretty relevant to really getting RESTy:
I actually did play with the idea of having urls map to objects and the verbs go through: web.d also includes an ApiObject class which goes: /obj/name -> new Obj("name"); and then calls the appropriate methods on it. So GET /obj/name calls (new Obj("name")).GET();, same for POST, PUT, etc. Then /obj/name/foo calls (new Obj("name").foo(); with the same rules as I described for functions above.
But I don't use it as much as the plain functions for one because it is still somewhat buggy.... and it is still somewhat buggy because I don't use it enough to sit down and fit it all! lol
You use it by writing an ApiObject class and then aliasing it into the ApiProvider:
import arsd.web;
class MySite : ApiProvider {
export int addSomeNumbers(int a, int b) { return a+b; }
alias MyObject obj; // new, brings in MyObject as /obj/xxx/
}
And, of course, define the object:
class MyObject : ApiObject {
CoolApi parent;
string identifier;
this(CoolApi parent, string identifier) {
this.parent = parent;
this.identifier = identifier;
/* you might also want to load any existing object from a database or something here, using the identifier string, and initialize other members */
// for now to show the example, we'll just initialize data with dummy info
data.id = 8;
data.name = "MyObject/" ~ identifier;
}
/* define some members as a child struct so we can return them later */
struct Data {
int id;
string name;
Element makeHtmlElement() {
// for automatic formatting as html
auto div = Element.make("div");
import std.conv;
div.addChild("span", to!string(id)).addClass("id");
div.appendText(" ");
div.addChild("span", name).addClass("name");
return div;
}
}
Data data;
export Data GET() {
return data;
}
export Data POST(string name) {
parent.ensureGoodPost(); // CSRF token check
data.name = name;
// normally, you'd commit the changes to the database and redirect back to GET or something like that, but since we don't have a db we'll just return the modified object
return data;
}
// property accessors for the data, if you want
export int id() {
return data.id;
}
}
mixin FancyMain!MySite;
Then you can access it:
http://arsdnet.net/cgi-bin/apidemo2/obj/cool/
BTW the trailing slash is mandatory: this is one of the outstanding bugs I haven't gotten around to fixing yet. (The trailing slash code is more complicated than it should be, making this harder to fix that it might look.)
Anyway, you can see the object rendered itself as html via makeHtmlElement. This is a good time to showcase other formats:
http://arsdnet.net/cgi-bin/apidemo2/obj/cool/?format=table
table, also try csv, and of course, json
http://arsdnet.net/cgi-bin/apidemo2/obj/cool/?format=json
or for machine consumption:
http://arsdnet.net/cgi-bin/apidemo2/obj/cool/?format=json&envelopeFormat=json
and the property is available too:
http://arsdnet.net/cgi-bin/apidemo2/obj/cool/id
Another major outstanding bug is that the automatically generated Javascript functions can't access child objects at all. They only work on functions on the top level ApiProvider. Another bug that is harder to fix than it might seem, and I'm not particularly driven to do so because the top-level functions can do it all anyway. Of course, you could make the URLs yourself on the xmlhttprequest and access it that way.
Let's also demo POST by slapping together a quick form:
http://arsdnet.net/cgi-bin/apidemo2/poster
you can submit something and see the POST handler indeed reset the name. (BTW note the action has that trailing slash: without it, it silently redirects you! I really should fix that.)
Anyway, bugs notwithstanding, the core of it works and might be the closest thing to full blown REST D has right now.
At the moment of writing this text there is no framework for building true RESTful web services that I know of. However, you should be able to easily build one on top of vibe.d or Adam's web modules that he already mentioned above.
You can take a look at what I'm building. Still extremely alpha, but I'm attempting to build a Rails-like framework in D: http://jaredonline.github.io/action-pack/
I know this is a really late answer, but I figured someone might come by this one day, since it has been so long and a lot of changes has happened within the D community, especially towards web development.
With Diamond you can write RESTful web applications without hacking something together, since it supports it within the framework.
http://diamondmvc.org/docs/backend/#rest
You can try Hunt Framework.
routes setting
GET /user/{id} user.detail
app/UserController.d souce code:
module app.controller.UserController;
import hunt.framework;
class User
{
int id;
string name;
}
class UserController : Controller
{
mixin MakeController;
#Action
JsonResponse detail(int id)
{
auto user = new User;
user.id = id;
user.name = "test";
return new JsonResponse(user);
}
}
Request http://localhost:8080/user/123
{
"id": 123,
"name": "test"
}

How to convert Zend Framework website to mobile website?

i have made a huge website(front & admin side) in Zend Framework now i want to do the same
project in ZF but for mobile devices.
i will certainly use HTML5 & jQuery mobile but before go ahead & start my development for my mobile website i want to ask experts what is the nice,easy,efficient and optimized way to do so ???
You'll probably want to checkout the ContextSwitch View Helper - which allows you to switch the view script based on a 'format' parameter. You could use that (or extend it) to switch your view to a more mobile friendly template.
Take a look at the JSON Context specifically, it disables the layout - the same technique could be used to switch the layout if a mobile request is detected.
Of course, if you can accomplish what you need with some CSS media queries, that's certainly the way to go.
You might also find Zend_Http_UserAgent useful to detect the available features of the mobile device. Again, similar to what's possible with media queries, but may be useful nonetheless.
But the bottom line is there's not need to 'convert' the site, there are plenty of tools to allow the same site to be accessible to both desktop and mobile users.
Click
me,Read me and i am sure you will like me
As the anwser from Fawad Ghafoor as disappeared from the internet (still available in the web archive https://web.archive.org/web/20110225213957/http://www.web-punk.com/2010/03/zend-framework-applications-for-iphone-blackberry-co/)
I don't know if that's ok, but i'll copy and format the content of the website here.
Zend Framework Applications for iPhone, BlackBerry & Co
Mobile devices have become pretty important during the last years. This tutorial will show you how to pimp up your website and make it ready for mobile devices like iPhone, BlackBerry, etc.
Before we start: as in all other posts of this blog, I expect that you are a software engineer and implemented your web application using the MVC (model / view / controller) pattern and probably also Zend_Layout.
1. What We Need
Basically, the only thing you have to do when making your web application ready for mobile devices is to a) detect if the user surfs to your site using a mobile or non-mobile device and b) change the V in MVC according to the result of a).
However, I find it quite useful to extent this approach a little bit. Beside replacing your views with views for a mobile device, we will do two other things: we will also replace the layout (Zend_Layout) used for your web application and we will use a different translation file (Zend_Translation). It is obvious why replacing the layout is useful, but why do we need to use a different translation file? Well, actually we don’t have to, but I found it quite handy if you have translation file for the big screen (where you might want to use looong textual descriptions) and a translation file for your mobile devices (with crisp descriptions, error messages, labels, etc.)
As we will see later on, Zend Framework’s Context Switch (http://framework.zend.com/manual/en/zend.controller.actionhelpers.html) is (nearly) all we need… The ContextSwitch is an Action Helper that “is intended for facilitating returning different response formats on request”. This action helper comes with two different ready-to-use contexts: JSON and XML. For our example we will create an additional context named “mobile”.
Control Flow Schema is lost... at some point it was here : http://www.web-punk.com/wp-content/uploads/2010/03/mobile_wf-300x94.png
Basically, our control flow has to work as depicted in figure “Control Flow” (click on the figure to enlarge it). If a user surfs to http://mobile.example.com/controller/action, we directly set the correct context “mobile”. If a user surfs to http://www.example.com/controller/action, we check if he is using a mobile device. If he is using a mobile device, we’ll ask the user if he wants to use the mobile or desktop version of our web app. As we don’t want to ask the user whether or not he wants to use the mobile version each time he request a page, we will store her / his decision in a session variable (and only ask her again, if she was inactive for several minutes).
As an example for this workflow, please have a look at http://www.qulpa.com
2. Creating our Mobile Plugin
To achieve our goals, we will implement a small plugin. Before implementing this plugin we need a function that checks whether or not the current user is using a mobile device. You could use a smart solution like WURFL to do this. However, for our example, we will use a simple function that returs true if the user is using a mobile device and false otherwise. You’ll find dozens of functions that will do the job if you google for it. I’ll use a function that I found at Brain Handles.
Now, lets create our plugin. In your \plugins folder, create a file called Mobile.php and copy and paste the following source code:
<?php
class Plugin_Mobile extends Zend_Controller_Plugin_Abstract
{
// instead of defining all these parameters here,
// you could also put them into your application.ini
// if user is inactive for X minutes and surfs to
// www.example.com, we'll ask him again if he wants
// to user mobile or desktop version
private $ask_again_after_x_minutes = 10;
// used to test your mobile layout. Set this
// to 1 to emulate a mobile device
private $test_mobile = 0;
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
// did we already ask the user?
if (isset($_SESSION['mobileasked'])) {
// is mobile session still valid?
if (time() < $_SESSION['mobileasked']) {
// update session
$_SESSION['mobileasked'] = time() + $this->ask_again_after_x_minutes * 60;
// continue with requested page
return $request;
}
}
// otherwise, check if user is using a mobile device
// or if we are in test mode.
if ($this->checkmobile() || ($this->test_mobile == 1)) {
// if requested page != MOBILE.example.com
if (!(Zend_Registry::isRegistered('directmobile') && Zend_Registry::get('directmobile') == '1')) {
// set mobile session
$_SESSION['mobileasked'] = time() + $this->ask_again_after_x_minutes * 60;
// ask user if he wants to use mobile or desktop version
$request->setControllerName('index')
->setActionName('askmobile')
->setParam('format', 'mobile')
->setParams($request->getParams())
->setDispatched(false);
}
}
return $request;
}
/**
* This function returns true if user is using a mobile device. False otherwise.
* (c) by http://www.brainhandles.com/techno-thoughts/detecting-mobile-browsers
*/
private function checkmobile(){
if(isset($_SERVER["HTTP_X_WAP_PROFILE"])) return true;
if(preg_match("/wap\.|\.wap/i",$_SERVER["HTTP_ACCEPT"])) return true;
if(isset($_SERVER["HTTP_USER_AGENT"])){
// Quick Array to kill out matches in the user agent
// that might cause false positives
$badmatches = array("OfficeLiveConnector","MSIE\ 8\.0","OptimizedIE8","MSN\ Optimized","Creative\ AutoUpdate","Swapper");
foreach($badmatches as $badstring){
if(preg_match("/".$badstring."/i",$_SERVER["HTTP_USER_AGENT"])) return false;
}
// Now we'll go for positive matches
$uamatches = array("midp", "j2me", "avantg", "docomo", "novarra", "palmos", "palmsource", "240x320", "opwv", "chtml", "pda", "windows\ ce", "mmp\/", "blackberry", "mib\/", "symbian", "wireless", "nokia", "hand", "mobi", "phone", "cdm", "up\.b", "audio", "SIE\-", "SEC\-", "samsung", "HTC", "mot\-", "mitsu", "sagem", "sony", "alcatel", "lg", "erics", "vx", "NEC", "philips", "mmm", "xx", "panasonic", "sharp", "wap", "sch", "rover", "pocket", "benq", "java", "pt", "pg", "vox", "amoi", "bird", "compal", "kg", "voda", "sany", "kdd", "dbt", "sendo", "sgh", "gradi", "jb", "\d\d\di", "moto","webos");
foreach($uamatches as $uastring){
if(preg_match("/".$uastring."/i",$_SERVER["HTTP_USER_AGENT"])) return true;
}
}
return false;
}
Make sure, that you register this plugin! To do so you need something like this in your bootstrap:
// init PluginLoader. Adopt folder to your application...
$loader = new Zend_Loader_PluginLoader(array(
'Plugin' => APPLICATION_PATH . '/application/controllers/plugins',
));
// define plugin names and classes
$pluginList = array(
'plugin1' => $loader->load('Plugin1'),
'plugin2' => $loader->load('Plugin2'),
// [...]
'mobile' => $loader->load('Mobile'),
);
// get your front controller
$frontController = Zend_Controller_Front::getInstance();
// Register your plugins
foreach ($pluginList as $pluginClass) {
$frontController->registerPlugin(new $pluginClass());
}
3. Context detection
That’s all you have to do in your mobile plugin. Next thing we have to do is to make sure that we detect the correct context. We’ll do this in our bootstrap. Open your bootstrap.php and put something like this inside:
// set correct context
$domains = explode('.', $_SERVER['HTTP_HOST']);
if ($domains[0] == 'mobile' || $frontController->getParam('format') == 'mobile') {
if ($domains[0] == 'mobile') {
// if set, user will be redirected directly to requested page
Zend_Registry::set('directmobile', '1');
}
Zend_Registry::set('context', '\mobile');
} else {
Zend_Registry::set('context', '');
}
4. Asking the User
As we would like to ask the user if he wants to use the mobile or desktop version of our application, we will create a simple action in our index controller. We will redirect the user to this controller / action in our mobile plugin (see chapter 2).
Open your IndexController.php and create an askmobileAction:
public function askmobileAction()
{
// nothing to do here...
}
This action basically does… well, nothing ;-). Now, let’s have a look at the askmobile view. In your views folder, which probably will be \views\scripts\index create a file called askmobile.mobile.view and put something like this inside:
How do you want to use this application?<br/>
MOBILE VERSION
<br></br>
DESKTOP VERSION
That’s not really complicated, isn’t it? As you can see, the name of this view differs from the name of all the other views. As we will see later in this tutorial, ContextSwitch will make sure that the view name.MOBILE.phtlm is being called instead of name.phtml, if we are in the context MOBILE.
5. Your Mobile Layout
The next step is to create a unique layout for our mobile version. Whether or not this is necessary depends on your application. However, in most cases it will make sense to a complex layout for the desktop version of your application and a lightweight layout for the mobile version.
First, create a directory in your \layouts folder called \mobile (the full path with probably be something like \application\layouts\mobile but this depends on your application). Create a file called layout.phtml in this folder and put something this inside:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php echo $this->headTitle() . "\n" ?>
<?php echo $this->headLink() . "\n" ?>
</head>
<body>
<div id="header">This is your header</div>
<div id="content"><?= $this->layout()->content ?></div>
<div id="footer">This is your footer</div>
</body>
</html>
That´s our very simple layout for the mobile context. It is very likely that your layout will be much more complex even if you create it for mobile devices as you will probably include a CSS file, etc.
6. Putting everything together
We are already nearly finished. The last step is to use the context in your controllers. For this tutorial we will use the init() method of the IndexController, which you can use as template for all the other controllers in your application. Actually, instead of copying this code to all your controllers I prefer a smarter way that makes use of OO-Design paradigms (e.g., create a class MyMobileController that extends Zend_Controller_Action and get all the necessary parameters from your application.ini), however, this will do the job for this tutorial.
Open your IndexController.php file and copy and paste the following source code into it:
/**
* Initializes the controller & context
*
* #return void
*/
public function init()
{
parent::init();
// are we in the mobile context?
if (Zend_Registry::get('context') == '\mobile' || $this->getRequest()->getParam('format') == 'mobile')
{
// Mobile format context
$mobileConfig =
array(
'mobile' => array(
'suffix' => 'mobile',
'headers' => array(
'Content-type' => 'text/html; charset=utf-8')),
);
// Init the action helper
$contextSwitch = $this->_helper->contextSwitch();
// Add new context
$contextSwitch->setContexts($mobileConfig);
// This is where you have to define
// which actions are available in the mobile context
// ADOPT THIS TO YOUR NEEDS!
$contextSwitch->addActionContext('index', 'mobile');
$contextSwitch->addActionContext('askmobile', 'mobile');
// enable layout but set different path to layout file
$contextSwitch->setAutoDisableLayout(false);
$this->getHelper('layout')->setLayoutPath(APPLICATION_PATH . '/application/layouts/mobile');
// Initializes action helper
$contextSwitch->initContext('mobile');
}
}
7. Create your Mobile Views
Finally, you have to create your mobile views. For each view that is available in the mobile context (as defined in the init() method of your controllers), you have to create a mobile view. So, if you have an action called myaction you will need a myaction.phtml for the desktop version and a myaction.mobile.phtml for the mobile version of your application.
Congratulations! You’ve just created your first mobile web application ;-)
Appendix: Translation Files for a Mobile Device
As promised in chapter 1 we will use a different translation file for our mobile device / mobile context. This is quite handy as you might want to have shorter labels, description texts, error messages and so on. Of course, if you don’t need something like this you may simply skip this appendix.
Basically, all you need to do is to check in which context the application is and load the corresponding translation file.
Let’s assume that you store your translation files in the \application\translations\ folder and that you have an english and a french version of your application. Beside your fr.php and en.php files you should have a mobile version for each language in your translations folder: mobile_en.php and mobile_fr.php. The followin code snippet will load the corresponding translation file:
// Init Zend_Locale with corresponding language (assuming that $lang is set to 'en' or 'fr')
// and store the Zend_Locale object in the registry
Zend_Registry::set('Zend_Locale', new Zend_Locale($lang));
// Load translation file and store it in the registry
$langFile = APPLICATION_PATH . '/application/translations/';
if (Zend_Registry::get('context') == '\mobile') {
// if context = mobile, get translation file for mobile device
$langFile.= 'mobile_' . Zend_Registry::get('Zend_Locale')->getLanguage() . '.php';
} else {
$langFile.= Zend_Registry::get('Zend_Locale')->getLanguage() . '.php';
}
Zend_Registry::set(
'Zend_Translate', new Zend_Translate('array', $langFile)
);

How are does LESS plugin work in Play Framework?

When I heard about LESS, I originally thought that they're usually pre-processed into css files, so browsers just encounter CSS files, not LESS files.
However, using LESS plugin for Play Framework, I just saw that my Chrome browser actually reads and interprets LESS files. LESS is claimed to be supported in all major browsers. So, do modern websites just serve LESS files, or do they pre-compile them into CSS? How does the LESS plugin work in Play Framework? Does it inject a Javascript snippet that interprets them client side?
What's actually happening here?
If you're using the Lunatech LESS plugin for Play! 1, it looks like it actually compiles the less files on the fly using Asual LESS and then serves it statically. An excerpt:
/**
* Get the CSS for this less file either from the cache, or compile it.
*/
public String get(File lessFile) {
String cacheKey = "less_" + lessFile.getPath() + lastModifiedRecursive(lessFile);
String css = Cache.get(cacheKey, String.class);
if(css == null) {
css = compile(lessFile);
Cache.set(cacheKey, css);
}
return css;
}
protected String compile(File lessFile) {
try {
return lessEngine.compile(lessFile);
} catch (LessException e) {
return handleException(lessFile, e);
}
}
Where lessEngine is a com.asual.lesscss.LessEngine instance.
Like others have pointed out, the more common ways of using LESS is to compile them yourself before serving content, or using the lesscss JavaScript plug-in to have the client compile them at runtime.
LESS css is "~compiled" by a javascript library (and should be done on the server side for production use).
You can read more on their site:
http://lesscss.org/#-client-side-usage
Both are options. Play! 2.0 will have built-in support for compiling LESS on the server side (and also CoffeeScript).