A situational application generally has a short lifespan and sidesteps formal requirements so that it can be developed quickly with a narrow focus. Cloud-based application development and deployment platforms lend themselves very well for such applications. This blog post will demonstrate a situational application that I developed and guide you to use cloud platform services to aid in your web development.
This app was built to register participants for a VMware Service Foundation pilot project called #ContributingCode, a program that involves an intense coding competition for students that is set to begin July 13, 2012. While there are several existing web applications available to do basic registration functionality, my custom application had additional requirements to provide:
- Seamless user experience without having to navigate across sites
- Flexibility in the registration set up
- A highly customizable user interface
I developed this app to be very generic so it can be used for other events by just changing the content and CSS as required.
A web application for handling registrations where users can create teams or join existing ones. Specifically, the owner of the team has the ability to:
- Add or delete members on the team
- Provide the ability for members to leave a team
- Send email notifications for registration, add requests and any modification to the team
- Send team announcements and view team summary using an admin panel
My application architecture called for an array of data services to be accessible by a core and standalone worker app. I considered using an IaaS like Amazon EC2, but realized I would have to take a raw Ubuntu Amazon Machine Image (AMI) and install Ruby, Rails, MySQL, Redis, MongoDB and then configure all of them to communicate with both the core and standalone worker application. Noting the amount of time, effort and possibility of errors involved in that approach, I chose to use Cloud Foundry PaaS. All the above steps could either be avoided or performed within seconds. Having such a platform with an array of data services and frameworks, supporting auto reconfiguration, and an NGNIX web server made my job far faster and easier.
The application is a Rails 3.2.6 app using MySQL for storing data, Mongo GridFS to store images, SendGrid for email delivery and Redis for Resque operations. There is a main app and a standalone worker app to perform the Resque operations in the background. The applications run on CloudFoundry.com and are bound to MySQL and Redis service instances provided by CloudFoundry.com. Using the services provided by CloudFoundry.com means that we can leverage Cloud Foundry’s ability to auto-reconfigure service bindings automatically.The reasoning behind this and the broader system environment is explained below.
Choice of Application Framework
The Rails framework includes everything I needed to create a database-backed web application using the Model-View-Controller (MVC) pattern. Also the ActiveRecord, the base for the models in a Rails application provides database independence, basic CRUD functionality, advanced query capabilities, and the ability to relate models to one another. ActionMailer is a component of Rails which provides a framework for email services. I also leverage a number of existing Ruby gems for this web application to perform various tasks.
A standalone app executes background tasks using Redis and Resque to email the users. This was primarily done to improve the user experience by sending the emails lazily in the background and responding to post requests as soon as the database entries have been made. For example, while registering as a new user, we need to send in a confirmation email which delays the response that the server has to send to the client as it takes a while to send the email. So when the job of sending an email is queued to Redis and later processed by the worker app the response is instantaneous. To learn how to run Resque workers on Cloud Foundry refer to this blog and there is more on Resque and Redis here.
We use the SendGrid email delivery system to deliver the email. There is clear documentation on how to use SendGrid with Rails here. In order to make the emails richer for our customization needs, we used HTML layouts. The notifier.rb file shows how we can include the layout in the email.
def register_email(name, email) @name = name mail( :to => email, :subject => "Thank you for registering for contributingcode", :template_path => 'mailer', :template_name => 'register' ) end
Relational persistance made the most sense for most of the data on this application. Each logical model, and operations on the model, are well defined. The models for this app are quite straightforward; User, Team, Member (which associates the users to the teams they belong to), JoinRequest, and AddRequest. The MySQL database was picked for its popularity. Additionally, we thought that a team avatar option would make the app more user friendly, so we extended the storage services to include MongoDB to use GridFS. The carrierwave gem for uploading the images and storing them with MongoLab and GridFS using the mongoid gem. Setting this up requires reading environment variables on application startup. This can be accomplished by creating a file in the initializer folder as seen here.
Single Sign On via GitHub
Github can be used for Single Sign On into apps using OAuth2 and REST APIs. OAuth2 is a protocol that lets third party clients or apps request authorization to protected resources, such as a user’s profile data, in a particular service without exchanging sensitive usernames and passwords. Instead, it uses interactions via the browser to obtain a code which is then exchanged for an access token and can be revoked by the service at any point in time. Think of the access token as a valet key which gives access to a subset of features. This is preferred over Basic Authentication, because tokens can be limited to specific types of data and can be revoked by users at any time. Specifically, we used omniauth-github gem for authentication through Github. This reduces the burden of having a custom login system. More on OAuth2 can be found here. Github was used for authentication as the participants of our code competition are required to have an account to submit the code to Github. More importantly, the authentication is done by Github and the app does not store any passwords. This is where we may encounter a drawback to OAuth2, which is the unavailability of the service you use for authentication, e.g., Github in our case, will in turn affect the users’ ability to log into your app.
The use of modal box and having a single page app improves the user experience as the user is never more than two clicks away from the target section.
Twitter Bootstrap was used to enhance the look and feel by utilizing the type-ahead feature for autocomplete functionality and carousel was used to display the teams and contributors. The front-end form validator, native jquery plugin, validationhelper is written as a wrapper around validate.js to validate forms in the frontend with the help of Twitter Bootstrap. This was made as a separate open source code and can be used irrespective of the backend framework. Using front end validation reduces unwanted server requests. For example, the email entered while registering can be validated before the user registration form is submitted to the server.
Deploying the App on CloudFoundry.com
Deployment was the easiest step of all and took just seconds to push the app to production. Here are the steps you can follow to push this app to Cloud Foundry using your credentials. Fork the project.
git clone email@example.com:<your_name>/contributingcode.git contributingcode cd contributingcode bundle install;bundle package
Before precompiling the assets make sure mongo is running in the local and set the following environmental variables.
export mysql_pwd='your mysql password' export github_client_id= 'github client id' export github_client_secret= 'github client secret' rake assets:precompile
Start the Core app.
$ vmc push --runtime ruby19 --nostart Would you like to deploy from the current directory? [Yn]: y Application Name: contributingcode Detected a Rails Application, is this correct? [Yn]: y Application Deployed URL [contributingcod.cloudfoundry.com]: y Memory reservation (128M, 256M, 512M, 1G, 2G) [256M]: 256M How many instances? : 1 Create services to bind to 'contributingcode'? [yN]: y 1: mongodb 2: mysql 3: postgresql 4: rabbitmq 5: redis What kind of service?: 2 Specify the name of the service [mysql-3e25d]: mydb Create another? [yN]: y 1: mongodb 2: mysql 3: postgresql 4: rabbitmq 5: redis What kind of service?: 5 Specify the name of the service [redis-2a4c2]: myque Create another? [yN]: n Would you like to save this configuration? [yN]: y Manifest written to manifest.yml. Creating Application: OK Creating Service [mydb]: OK Binding Service [mydb]: OK Creating Service [myque]: OK Binding Service [myque]: OK Uploading Application: Checking for available resources: OK Processing resources: OK Packing application: OK Uploading (558K): OK Push Status: OK
Start the worker app. Rename the manifest file if you have one.
vmc push ccworker --nostart Would you like to deploy from the current directory? [Yn]: y Detected a Rails Application, is this correct? [Yn]: n 1: Rails 2: Spring 3: Grails 4: Lift 5: JavaWeb 6: Standalone 7: Sinatra 8: Node 9: Rack 10: Play Select Application Type: 6 Selected Standalone Application 1: java 2: node 3: node06 4: ruby18 5: ruby19 Select Runtime [ruby18]: 5 Selected ruby19 Start Command: bundle exec rake VERBOSE=true QUEUE="*" resque:work Application Deployed URL [None]: Memory reservation (128M, 256M, 512M, 1G, 2G) [128M]: 128M How many instances? : 1 Bind existing services to 'ccworker'? [yN]: y 1: mydb 2: myque Which one?: 1 Bind another? [yN]: y 1: mydb 2: myque Which one?: 2 Create services to bind to 'ccworker'? [yN]: n Would you like to save this configuration? [yN]: y Manifest written to manifest.yml. Creating Application: OK Binding Service [mydb]: OK Binding Service [myque]: OK Uploading Application: Checking for available resources: OK Processing resources: OK Packing application: OK Uploading (39K): OK Push Status: OK
Set the configuration. Set the Github and Mongolab credentials for core app.
vmc env-add contributingcode github_client_id= 'github client id' vmc env-add contributingcode github_client_secret= 'github client secret' vmc env-add contributingcode mongodb_host= 'host' vmc env-add contributingcode mongodb_port= 'port' vmc env-add contributingcode mongodb_username= 'username' vmc env-add contributingcode mongodb_password= 'password' vmc env-add contributingcode mongodb_db= 'db name'
Similarly, set the environmental variables to the worker app too.
The standalone worker app requires the same environment as the core app and thus all the environment variables should be set for the worker app as well. In addition, set the SendGrid credentials.
vmc env-add ccworker github_client_id= 'github client id' vmc env-add ccworker github_client_secret= 'github client secret' vmc env-add ccworker mongodb_host= 'host' vmc env-add ccworker mongodb_port= 'port' vmc env-add ccworker mongodb_username= 'username' vmc env-add ccworker mongodb_password= 'password' vmc env-add ccworker mongodb_db= 'db name' vmc env-add ccworker sendgrid_username= 'sendgrid username' vmc env-add ccworker sendgrid_password= 'sendgrid_password'
While running on localhost set mysql_pwd as follows:
export mysql_pwd='mysql password'
Finally to start your app on CloudFoundry.com.
vmc app start contributingcode vmc app start ccworker
Visit contributingcode.cloudfoundry.com to view my web application. Your application should look similar, but as the core app and worker app names are subject to availability, your app name will be different from ‘contributingcode’.
To get the app running, fork the app from here.
The Right Tools For the Job
The bootstrapping of the app was done in one day with the basic registration system in place. Relying on some expert website mockups, I had all the functionality and HTML in place in just a couple of weeks. With some further testing and fine tuning, the app was easily deployed to CloudFoundry.com and open for registrations. While, the website needed several updates with new content and sections (e.g. FAQ’s) it was all done with barely any downtime. Using a single Cloud Foundry command “vmc update” reduced the overhead in updating the website in multiple iterations to one step. This confirmed that we had made the right choice in using a PaaS rather than standing up our own infrastructure. This registration app has registered over 100 students, helping them to connect with each other and learn about the theme of the competition. I hope this blog helps you jumpstart your own web application development as well.
— Magizharasu Thirunavukkarasu, Developer Advocate Intern
Don’t have a Cloud Foundry account yet? Sign up for free today
About the Author
BiographyMore Content by Magizharasu Thirunavukkarasu