Category Archives: Tquila Labs

MEET THE TQUILA TEAM: Tim Davies


Tim Davies, UI / UX Practice DirectorMeet Tim Davies, Practice Director UI / UX. Tim joined Tquila in July 2012.

A design geek at heart, if you don’t see him engrossed in the latest online design gallery then, as a big gamer, you will find him playing military airsoft! Why not challenge him to a game…

Here’s what Tim has to say about his role at Tquila:

What I do

“User Interface concept and designs for mobile apps. Mapping a user journey and creating sales concepts and pitch materials to present to clients. I take part in client consultation and workshops to help clients get maximum impact from their applications and interfaces. I also deal with project management and oversee road mapping.”

Why I love my job

“It sounds like a cliche, but no two days are the same. This role has opened my eyes to a whole new way of working, I work fast, I work agile and I work fast again! Alongside some of the most talented people in the industry. Every day brings a new challenge with diverse projects.”

Why I love Tquila

“Tquila is a fun and creative brand that pushes the boundaries of traditional technology and helps customers achieve more from their Salesforce solution. We get to help customers see the bigger picture – and for me in particular – get to convince them of the importance of mobile! There is such a great culture here and the atmosphere is electric.”

Follow Tim on twitter @RandomTrashy

Want to be part of this? Check out our job openings or send your CV to careers@tquila.com

Tagged , , , , ,

Sencha App with Salesforce API using a Sinatra Proxy


In my last article, I presented the Sencha Framework and some of the advantages this product has over other mobile solutions. Convinced that I had a wonderful tool in my hands I decided to create a simple app. The aim is to have an app with two tabs. The first with an intro page and the second with a list of leads retrieved through the Salesforce REST API. The list must be sorted and provide basic interaction by displaying additional details of a selected lead. As soon as you start playing with mobile applications and a remote API you have to face a common javascript limitation: the Same Origin Policy. You may like to read the Wikipedia article if you want to know more about that rule. Briefly, “The policy permits scripts running on pages originating from the same site to access each other’s methods and properties with no specific restrictions, but prevents access to most methods and properties across pages on different sites”. Said differently, your cute javascript mobile application hosted under xyz.com domain _can not_ request any data from salesforce.com domain. Such a shame but imperative to keeping the web safer.

Continue reading

Using Twitter Bootstrap with Visualforce pages


Building beautiful websites used to take patience and skill. If one were trying to create a visually appealing website from scratch, good knowledge of CSS3 would have been a must. Additionally, skill with jQuery would be another hurdle for completing dynamic aspects. Putting everything in the proper order would have taken days to weeks.

Now, there are fast UI frameworks for front-end development. I will be introducing you to Twitter Bootstrap, specifically how to integrate it with Visualforce Pages.

Some people have compared the Force.com platform (APEX programming language) to Visual Basic. Even a good application needs an attractive UI. Most applications nowadays are web facing. Browsers use CSS + HTML5 + JavaScript to render text, images, layout, and user interaction. Bootstrap is a UI framework with all those elements pre-fabricated. You just need to mark HTML elements with the proper CSS 3 classes and voila, it’s done.

It is extremely easy to use Bootstrap.

  1. Go to http://twitter.github.com/bootstrap/ and download the framework. It is a ZIP file, which already contains CSS, image (glyphicons), and JavaScript. (The required jQuery JavaScript is a separate download.)
  2. Upload as Static Resource to your Force.com org. (Setup > Develop > Static Resources). You can give it a name, for example, bootstrap.
  3. Create your visual page and include the required bootstrap components.
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.0/jquery-ui.min.js"></script>

<apex:stylesheet value="{!URLFOR($Resource.bootstrap, '/css/bootstrap.css')}"/>

<apex:stylesheet value="{!URLFOR($Resource.bootstrap, '/css/bootstrap.min.css')}"/>

<apex:includeScript value="{!URLFOR($Resource.bootstrap, '/js/bootstrap.js')}"/>

<apex:includeScript value="{!URLFOR($Resource.bootstrap, '/js/bootstrap.min.js')}"/>

That’s all the setup that’s needed.

By default, bootstrap uses a 12-column layout. If you want a div that spans across 4 columns, just indicate it is a “span4”. The convention is {name#columns}. Many useful UI classes are already pre-fabricated, i.e.

  • Navigation bar
  • Buttons
  • Pagination
  • Labels
  • Input boxes
  • Breadcrumbs
  • Progress bar

There is even a photo carousel built into it. See http://twitter.github.com/bootstrap/javascript.html#carousel

Furthermore, this UI framework supports mobile devices and will automatically resize elements accordingly. It’s really easy to use it. In 10 minutes, I created a demo page, showing VF pages + Bootstrap = Like.

The URL is: http://tquila-ray-developer-edition.na11.force.com/

Additionally, there are many bootstrap themes available on the Internet. Go ahead, try it and I am sure you will enjoy creating a polished website.

Image

Tagged , , , ,

Sencha Touch: More than a trendy mobile JS framework


Mobile appsA few weeks ago, I started to think about my next project, a mobile dashboard app for Salesforce. As I’m a Ruby/Javascript dev, not an IOS dev, I spent some time investigating which framework would best meet my requirements:

  • The ability to generate IOS/Android apps, to remain device agnostic
  • Using a language I know, improving my skills rather than diluting them
  • Not requiring a heavy env install (it’s never as simple as they say)
  • Well documented, to avoid banging my head against the wall from looking into a raw source code of thousands of lines
  • A promising future – I don’t want to waste my time learning already obsolete technologies

Some of the candidates were RubyMotion, PhoneGap – Cordova, JqueryMobile, Titanium, and RhoMobile. My final conclusions are not based on intensive trials but more on web investigations and personal intuitions. Here are the pros and cons of these different solutions:

  • RubyMotion: promising and really trendy. RubyMotion’s creator was one of the invited speakers at the last Ruby Lugdunum conference. The product is still too young, expensive (not free) and only generates IOS apps
  • PhoneGap – Cordova: a complete framework, now part of the Apache foundation. Able to generate apps for all devices, but looks complicated (too much recent evolution, with Adobe then Apache, heavy IDE with XCode, Eclipse, plugins …).
  • JqueryMobile: everyone loves this framework but it’s a little bit old now. Furthermore, JqueryMobile apps are known to be slow and are obviously not even fake native apps but pure web apps designed for mobile.
  • Titanium: I have no real explanation for why I didn’t choose this option. Maybe because they’ve changed their business model, presentation, name, and documentation too many times in recent years. Maybe because when I have tried it years ago I was really disappointed.
  • RhoMobile: another player using Ruby – and well-established. Positive points, but then it was bought by a big company (Motorola), and furthermore, performance is known to be less than average.

SenchaIn the end, one candidate kept my attention: Sencha. If you work in the Salesforce ecosystem you might have heard about it. Sencha has been blogged about a lot and its presence at Dreamforce 2012 was impressive.

My first experience with Sencha had me hesitate. As with Phonegap – Cordova, there is a lot of documentation available. Furthermore, Sencha’s offering is split into multiple components (ExtJS 4, Sencha Touch 1, Sencha Touch 2, Sencha Architect…). It’s hard to figure out initially which one to use: are they working together, or do they have different destinations?

Even if I am not a huge fan of an integrated environment I thought that maybe, in 2012, Sencha might have achieved something phenomenal with their architecture. Using the 30-day free trial, I started to create my first mobile app to display points of interest on Yelp!. It was cool, the tutorial was easy to follow, but at the end, even if my app worked, I didn’t understood any of the JS code that was generated: it was a bunch of hashes with a lot of obscure configuration options.

A few days later, I started a new app with Sencha Touch 2. Sencha Touch 2 is the latest version of Sencha Touch, and is the component to use if you want to build mobile apps. It’s Open Source and free to use even for commercial purpose. Sencha Touch 2 has the ability to compile your code into fast native Android, Blackberry and iOS apps. Obscure configuration options generated by the architecture became much easier to understand, as Sencha Touch 2 is in fact a classic MVC framework. I recommend watching the following videos to discover what Sencha Touch has to offer, and how to create your first applications cleverly:

  • Discover Sencha Class System. Understand how structurally different Sencha Touch 1 and 2 are, and why the new version is so pleasant to use, with a much more robust and compact syntax.
  • The Sencha MVC framework architecture part 1 and part 2

A lot of people complain about the Sencha Touch documentation. I agree that the main documentation site is a rudimentary API description, and that tutorials are usually not detailed enough. However, Sencha released a learning platform for Touch with access to great content. You can also find interesting blog posts on their website.

In my next blog post, I will explain how I created a simple app that displays records from Salesforce, using Heroku, Sinatra and Database.com.

Tagged , , ,

Unraveling data( ) vs attr( ) JQuery methods


In theory, you can use $(elem).data() and $(elem).attr() methods interchangeably, but in practice, this may lead to confusion and hard to debug defects. Although both methods are designed to save and retrieve values, they accomplish this task in different ways. The data() method stores the provided data in a DOM element , whereas attr() sets a (string) value to the corresponding html element.

To see the difference in practice please refer to the example below:

<html>
  <head>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"/>
  </head>
  <body>
    <span id="sp1" data-attr1="attr-1">span1</span><br>
    <span id="sp2">span2</span>
    <script type="text/javascript">
      $("#sp2").attr("data-attr2", "attr-2");
      $("#sp1").data("attr3", "data-3");
      $("#sp1").attr("data-attr3", "attr-3");
      $("#sp1").data("data-attr4", "data-4");
    </script>
  </body>
</html>

Corresponding javascript code:

console.debug($("#sp1").attr("data-attr1"));
// will return attr-1
console.debug($("#sp1").data("data-attr1"));
// will return undefined
console.debug($("#sp1").data("attr1"));
// will return attr-1
console.debug($("#sp2").attr("data-attr2"));
// will return attr-2
console.debug($("#sp1").data("data-attr4"));
// will return data-4
console.debug($("#sp1").attr("data-attr4"));
// will return undefined
console.debug($("#sp1").data("attr3"));
// will return data-3
console.debug($("#sp1").attr("data-attr3"));
// will return attr-3

Additional notes:

- Values set by attr() method will be visible on the html element (you can verify this by inspecting the element using Firebug or the Chrome developer console), whereas the data() method adds it only to the DOM object.

- The data() method stores and returns an object, but the attr() method always operates on a string, so be careful if you are expecting a a float or an integer result. (In this instance, use the javascript parseFloat() method to convert the string result to a float.)

- Also, for performance reasons, use the data() method on the element directly instead of using the generic data() method where the target element is one of the parameters.

for example, use this:

theInput.data('someData', 'newValue');

instead of this:

$.data(theInput, 'someData', 'newValue');

In summary, use only one of the two methods, if possible, to avoid confusion and make the code more maintainable.

Football Fever & Social Enterprise


Football fever is definitely here in Europe. Strolling down the street, you see many bars, placed TVs on the sidewalk. In front of those flat screens, you see soccer fans glued to UEFA games and chucking down jugs of beers. When their teams score, you hear instantaneous celebrations. It’s simply an awesome experience.

Image

After Germany’s 2:1 win over Denmark last night, I decided checking out football betting sites. According to users, the Spain/Germany (5 to 2)  has the highest odds of facing off in the UEFA 2012 final game. (40% speculation tips that.) Followed next by Germany/Portugal (6 to 1) or !6.67% odds. In the third places, it’s Spain / France, (10 to 1).

Image

Same thing is happening in the IT world, Salesforce has Chatter and bought Radian 6 (social intelligence tool) for $326 million and BuddyMedia (Social Marketing tool) for $689 millions. Oracle bought Virtue (~$300 millions). And, Microsoft bought Yammer for cool $1 billion. Those bets are certainly much bigger than the UEFA pools. :-p

Who shall dominate the social enterprise? This is a very interesting topic? Will it be the one with biggest wallet? Most resources? Most sales channel? The ultimate victor is still to be determined.

According to the gaming theory, the general public loves to participate. Given there are incentives, their participation will be  even more active and may even attempt to sway the outcome. A group of people usually has more knowledge and resource than one individual, and hence can make better prediction and/or influence the outcome.

I am a technologist at heart and naturally interested in programming languages. Over the years, programming languages evolved from Assembly, to Cobol, to Fortran, to C, to C++, to Java, to Python, to Ruby, and now to Node.Js & Clojure. (Not in any specific order.) The process of evolution is ongoing. I have even experimented with non-english construct programming languages like Linotte (http://langagelinotte.free.fr/wordpress/). It is evident that human nature languages are richest way of communication. Programming languages are bridges between human and machines.

I ran into Mark Zuckberg in 2008 and asked him what he saw 5 years into the future. His answer was very simple, “People desire to communicate.” A few months ago, he bought Instagr.am for a cool $1 billion.

Image

I am curious why isn’t there already a programming language that is social by nature? Until now, most software development paradigms are inward focused – Software developers write some application, testers quality check it, and the users validate or invalidate the application. BDD has started looking at the problem from the outside-in approach.

I am curious why can’t we go a step further and develop a truly social programming language. Initially, the development will broadcast expected feature specifications to the public, who can vote up and vote down on those features, and affecting the path of application development. A scenario could be described as following.

1. Specifications are drawn up and broadcasted on Chatter, Twitter, Facebook, or internal social enterprise platform.

2. Expected user population gives their feedback and vote up / down those specification.

3. Application is developed according to user’s feedbacks.

4. Certain features are released; and, receiving more user voting.

5. Modification to features are implemented.

The cycle of development progress according to users’ input. Special prize to user who gives best input or suggestion to the development. (gaming theory).

It’s clearly, this new programming language would provide richer user interaction experience from day one and present a radically different approach of constructing applications. In another word, why couldn’t one have a programming language that utilizes social networks from the onset?

UEFA 2012 is still under way. I don’t like gambling. Hence, my input is muted. But, it’s certainly fun to watch and share my opinions with other spectators.

Tagged , , ,

Heroku Pro Tips 1 – Running your app in various environments


Awesome Heroku platform is Awesome. We are starting a new series of blog posts today about Ruby and Heroku Pro tips. Discover tips from Tquila Labs Team to leverage the power of Ruby and Heroku.

FormStorm.co is a SaaS application to build marketing micro-sites and Salesforce-connected forms. Last week we launched the private beta and you’re invited to take part! FormStorm is hosted on Heroku and generates consumer-facing Heroku apps on the fly. During the development process Heroku has been used to host the TEST ENV then, when ready, both STAGING and PRODUCTION ENV. Here are some tips we’ve discovered along the way.

Running your app on various ENV:

A classical approach to maintaining any app is to have 3 or 4 different environments. Each environment is backed up by a specific Git Branch and you usually end up with something like :

  • development env and branch, for local use and local work
  • staging env and branch, for Production-like test
  • Production env and branch, for the live app

This approach is common but it is not immediately obvious how to implement this on Heroku. By default you are in Production mode and only the master branch can be deployed. Here is how to recreate your various ENV on Heroku.

  • First create your apps for staging and production ENV:
heroku create --remote staging
heroku create --remote production
  • Run your app with the correct ENV

Ruby Heroku apps respect the RACK_ENV variable value. Rails apps use the RAILS_ENV variable. To change them, run this in you terminal:

heroku config:add RACK_ENV=staging --remote staging
heroku config:add RAILS_ENV=staging --remote staging
  • Deploy your branches

After that, to push your staging or production branches to Heroku do (respectively):

git push staging staging:master
git push production production:master

That command will push and merge either your staging/production branches on the corresponding remote master.

Next Pro tips on Ruby and Heroku:

  • Ruby – Testing with databasedotcoom gem
  • Heroku – Useful commands
  • Ruby – Extending your SF objects
  • Heroku – Use Thor to handle your VARS
  • Ruby – Performance boost
  • Heroku – Adding a SSL Certificate
Tagged , , , , , ,

Being Social is Savvy!


This week, I am in London and attended one of the biggest cloud computing trade shows in Europe. Over 15,000 attendees gathered at the ICC EXCEL center for a fun-packed one day event. During the keynote presentation, we heard success stories from:

  1. Burberry – one of the premier luxury goods providers in the world,
  2. O2 – a leading telecommunication company,
  3. Spotify – one of the hottest social music experience sits that had won kudos and investments from Sean Parker,
  4. Kimberly Clark – a giant American conglomerate and the maker of many brands of consumer goods,
  5. HP – The grand-daddy of startup in California. All other companies, i.e. Apple, Sun, Oracle, came after it.
  6. Toyota – World’s largest auto manufacturer.

What do they have in common? Two things.

  1. They are believers in Social Enterprise;
  2. They are transforming their business processes to leverage the power of cloud computing / SaaS solutions.

Of course, they are all using Salesforce technologies as well.

In only one year, the attendance at CloudForce/ClouStock @ London doubled from 7,000 to 15,000. That’s a 100% growth rate, liking to a hockey stick!

If you are a sport lover like me, you probably have heard Wayne Gretzky said: “I skate to where the puck is going to be, not where it has been.”

Yes, move yourself to where the action will be at and make a goal.

It is pretty clear that, acting social and agile, will be cornerstones for the upcoming projects.

Being Social lets project owners and developers clearly projecting purposes and benefits of what they will be doing or currently working on. If you are investing a considerable amount of your time on a project, being passionate probably improves the odds of achieving success. Agile is just another way of saying that, I am a good listener to what my users are saying. I will take their interests in heart to build wonderful things.

I have been in IT world close to 18 years. RAD language, i.e. ROR, Django, Groovy are hot technologies right now. And, functional languages, i.e. Clojure, Scala, F#, are beginning to emerge. While their programming paradigms are quite different, the common need for efficient communication and clear thought process, similar to how people communicate in the real world, will drive further innovation. Wouldn’t it be wonderful, if we can develop new methods / approaches to building applications and engaging users from day one? Clearly explaining application features and benefits thorough social venues, i.e. Chatter, Twitter, Facebook, and hearing users’ feedbacks will be trump cards for success. Wouldn’t it be nice to see programming frameworks and languages that plug directly into the Social Enterprise directly?

6 great examples for Social Enterprise in one action-packed day. Being Social is Savvy!

Tagged , , ,

(Re)Introducing FormStorm


FormStormWe’ve blogged about FormStorm before – but you’ve never really been formally introduced.

So before you get all up close and personal, please allow us to introduce you properly:

Meet FormStorm!

FormStorm is a flexible, user-friendly tool that makes it quick and easy to set up web forms to capture data.

FormStorm creates forms that replace traditional web-to-lead and web-to-case forms in a cost-effective, scalable way.

(Joining us @ Cloudforce London tomorrow? See a live demo at one of our booths – in the Platinum Partner area, and in Cloudstock.)

Need to keep costs down? FormStorm doesn’t require upfront investment – instead, it operates on a pay-per-use basis, so you’ll only pay for the data you collect.

How quick and easy is Formstorm? You can create and deploy a form in 3 easy steps:

  1. Create a form in the user-friendly interface.
  • Publish it to your website or Facebook.
  • Log in to Salesforce to view and use the data that’s been submitted!

Want to see how quick and easy FormStorm really is? See it for yourself:

Ready to get started? Sign up for the private Beta of FormStorm here

Tagged , , , ,

Ruby External CRON manager on Heroku with Sinatra, Resque and Redis


In todays post we’ll have look at task scheduling using Heroku and the famous Resque gem. I have presented it at last London Skillsmatter Salesforce Dev Meetup. Watch the video:

Delayed tasks on Salesforce have some well known weaknesses. For example: “Use extreme care if you are planning to schedule a class from a trigger. You must be able to guarantee that the trigger will not add more scheduled classes than the 25 that are allowed”.

With the help of Heroku, most of your background tasks might be externalized and delegated to a dedicated app on Heroku. To build that solution we’ll be using 4 different gems. The objective is to create a simple web app that is able to create, handle and monitor tasks build by registered users. To be really functional, that web app should be reachable through an API (coming in a future post).

  1. Sinatra to build a basic web app
  2. Databasedotcom: to get the secure connection to Salesforce API
  3. Omniauth to get the authorization token from your ORG
  4. Resque to handle Job Queues and monitor them

Sinatra

Sinatra is the trendy DSL to build Web App in Ruby maintained by the brilliant RKH aka the cool kid. If you are bored by the heaviness of Rails, just pick Sinatra and as they say: “Put this in your pipe and smoke it”. In few words, Sinatra is a simple framework looking like rails, usually written in single file. It looks like Rails because you’ll find some MVC sprinkles and multiple typical Rails features all around Sinatra (redirect, helper, before filter, routing, asset management etc.).

In our app, we’ll use the basic Sinatra abilities to define the initial URL, the callback and the URL to create a new task. As everything is handled in a single file, you’ll see at the end all the code packed into a sinatra app.rb file.

Just for your pleasure, that’s how to build the simplest web server in Sinatra in 4 lines.

require 'sinatra'

get '/hi' do
  "Hello World!"
end

Databasedotcom

Database.com has been presented a couple of times on the web. The most recent and appealing posts and apps have been written by Fractastical, Metadaddy and Dburkes. They explain in a really readable way how to install, configure and use that magic library. In a few words, databasedotcom is a Ruby wrapper on top of SFDC REST API. Said differently – after having opened a secure connection to your ORG, you can CRUD any standard or custom object in an ActiveRecord-like way.

Here is how to install and use it in our app

require 'databasedotcom'

config = YAML.load_file("config/salesforce.yml") rescue {}
@client_id = config["client_id"]
@client_secret = config["client_secret"]
@username = config["username"]
@password = config["password"]

dbdc = Databasedotcom::Client.new(:client_id => @client_id, :client_secret => @client_secret)
dbdc.authenticate :username => @username, :password => @password.
dbdc.materialize('Lead')
leads = Lead.all
leads.each{ |lead| lead.update_attribute('LeadSource',leadsource) }

Omniauth

To get the authorization token, we’ll use omniauth. Many people keep using the standard OAuth and OAuth2 gems. However, Intridea built a really practical gem to simplify your OAuth handshake. After 19 months of dev V1.0 was released. Omniauth comes with a lot of provider strategy. A strategy is a basically an end-point description. The list of existing providers is increasing regularly and you can easily add a new strategy and bundle it as a new gem in few lines.

To use omniauth you need to store the session. We’ll use Rack::Session::Cookie. We require the Salesforce strategy usage in these lines:

require 'omniauth'
require 'omniauth-salesforce'

  use Rack::Session::Cookie
  use OmniAuth::Builder do
    provider :salesforce, config["client_id"], config["client_secret"]
  end

Don’t forget to add a callback URL matching the one described on the “Remote Access” record created on your ORG. Also please note that as your callback must be over HTTPS, you must enforce SSL on your app. This is automatically done on Heroku as of a few weeks ago, but must be handled on your local machine (check Rack::SSL).

Resque

We’ve kept the best for the end. Resque was written a few years ago by Defunkt from Github after multiple failures using background.rb and Delayed job. Read the interesting history of Resque on Github Blog. In Resque, jobs queues are usually stored on a Redis DB. Any other storage solution might nevertheless be used. Resque workers (you can use multiple and dedicate to specific tasks/queues) handle them. On Heroku, a worker is a resource just like Dynos are and need to be added to your app using either a Procfile or the heroku CLI

heroku scale worker=1.

Other worker providers like IronWorker are also available. Locally, workers are running rake tasks.

Additional to its proved robustness and overall quality, one of the strength of Resque is to come with a monitoring web UI. You’ll see there you’re running workers, working and failed jobs in real time.

To load Resque Monitoring app with our sinatra app, here’s the trick:

require './song'
require 'resque/server'

run Rack::URLMap.new \
  "/"       => Sinatra::Application,
  "/resque" => Resque::Server.new

A job is defined as a module/class which declare the associated queue and define a perform method.

 module JobName
    @queue = :queue_name

    def self.perform(params)
      puts "the job does this and that"
      # YOUR LOGIC WILL GO HERE
    end
  end

As we want to trigger actions on Salesforce.com to bulk update lead source, our job code is as follows:

 module Updatelead
    @queue = :lead
    config = YAML.load_file("config/salesforce.yml") rescue {}
    @client_id = config["client_id"]
    @client_secret = config["client_secret"]
    @username = config["username"]
    @password = config["password"]

    def self.perform(leadsource)
      puts "update all Leads"
      dbdc = Databasedotcom::Client.new(:client_id => @client_id, :client_secret => @client_secret)
      dbdc.authenticate :username => @username, :password => @password
      dbdc.materialize('Lead')
      leads = Lead.all
      leads.each{ |lead| lead.update_attribute('LeadSource',leadsource) }
      puts "updated all Leads"
    end
  end

Conclusion

Ok, we now have all the parts of our app. Lets glue them together and deploy it on Heroku.

You can find the final source code on GITHUB, fork it and deploy it with your credentials on Heroku.

Tagged , , , , ,
Follow

Get every new post delivered to your Inbox.

Join 50 other followers