XP on Rails Extreme Programming Blog

24May/100

How to model a custom search form in Rails

ORIGINAL POST

Often you need to create a search form to filter the rows in a table that corresponds to a specific model.

SearchLogic can be a valid solution but maybe you want to bet on a more customizable alternative.

The solution I propose is to create a Search.rb class that is able to collect the search parameters and to create the “where conditions” to be applied on our find query.

Suppose you want to filter events records defined by an Event.rb model with the following attributes:

  • name, string
  • address, string
  • start_at, datetime
  • end_at, datetime

The search mask will propose two text fields for name and address and possibly two DatePicker for start_at and end_at.

You may decide to put AND conditions rather than OR conditions or to define intervals based on the presence of one or both date fields.

Let’s now see how to shape our support class Search.rb (eg create app/models/)

class Search
  attr_reader :options

  def initialize(model, options)
    @model = model
    @options = options || {}
  end
 
  def name
    options[:name]
  end

  def address
    options[:address]
  end

  def event_date_after
    date_from_options(:event_date_after)
  end

  def event_date_before
    date_from_options(:event_date_before)
  end

  def has_name?
    not name.nil? and not name.empty?
  end

  def has_address?
    not address.nil? and not address.empty?
  end

  def conditions
    conditions = []
    parameters = []

    return nil if options.empty?
   
    if has_name?
      conditions << "#{@model.table_name}.name LIKE ?"
      parameters << "%#{name}%"
    end
   
    if has_address?
      conditions << "#{@model.table_name}.address LIKE ?"
      parameters << "%#{address}%"
    end

    if event_date_after
      conditions << "#{@model.table_name}.start_at >= ?"
      parameters << event_date_after.to_time
    end

    if event_date_before
      conditions << "#{@model.table_name}.end_at <= ?"
      parameters << event_date_before.to_time.end_of_day
    end

    unless conditions.empty?
      [conditions.join(" AND "), *parameters]
    else
      nil
    end
  end

  private

  def date_from_options(which)
    part = Proc.new { |n| options["#{which}(#{n}i)"] }
    y, m, d = part[1], part[2], part[3]
    y = Date.today.year if y.blank?
    Date.new(y.to_i, m.to_i, d.to_i)
  rescue ArgumentError => e
    return nil
  end
 
end

Now we can see how to write the controller that will apply the search.

Specifically, I would make a search that responds to the action index of EventsController.

At this point, an URL without parameters (such as http://localhost:3000/events) will call an Event.find(:all), a request with parameters (such http://localhost:3000/events?name=rock+contest) will apply the search.
This requires that our search form responds to the GET method. We will see this aspect in detail later.

The controller code looks like this:

class EventsController < ApplicationController

  def index
    @events = []
    @search = Search.new(Event, params[:search])
    if is_search?
      @events = Event.search(@search, :page => params[:page])
    else
      @events = Event.paginate(:page => params[:page])
    end
  end
 
  private
 
  def is_search?
    @search.conditions
  end
 
end

As you can see, in the case of search, the class method search will be called.
Let’s see how it was defined in the model Event.rb

class Event < ActiveRecord::Base

  def self.search(search, args = {})
    self.build_search_hash search, args
    self.paginate(:all, @search_hash)
  end
 
  private
 
  def self.build_search_hash(search, args = {})
    @search_hash = {:conditions => search.conditions,
                    :page => args[:page],
                    :per_page => args[:per_page],
                    :order => 'events.created_at'}
  end
end

At this ponit we can code the search form in this way (using formtastic).

<% semantic_form_for :search, @search, :html => { :method => :get } do |form| %>
    <% form.inputs do %>
        <% form.inputs do %>
            <%= form.input :name, :label => t('search_form.name') %>
            <%= form.input :address, :label => t('search_form.address') %>
            <%= form.input :event_date_after,
                          :as => :date,
                           :label => t('search_form.event_date_after') %>
            <%= form.input :event_date_before,
                          :as => :date,
                           :label => t('search_form.event_date_before') %>
        <% end %>
    <% end %>

    <% form.buttons do %>
        <%= pretty_positive_button t('search') %>
    <% end %>
<% end %>

The search method is GET, so it will append search parameters to the url and will invoke the index method of EventsController.

15Apr/100

The top 20 plugins to create a Rails application

ORIGINAL POST

Rails is now closer to its official third release and all plugins and gems authors are working to ensure compatibility of their products with the new version of the framework.

Among the many Rails plugins released by the community, I make a list of 20 most used (and useful) in our projects at DevInterface.

Authentication

  • Authlogic: this plugin is definitely the more common among Rails 2.x applications. It was releases as release replacement for the powerful but verbose “RESTful Authentication” http://github.com/technoweenie/restful-authentication, pioneer of Rails authentication during the 1.2.x version.
  • Devise: provides a complete implementation of all authentication problems with support for sign up, password recovery and more. Its modularity, combined with the fact of being Rack based, candidates this plugin to become the reference for authentication in Rails 3.
  • Subdomain_fu: great plugin for managing subdomains. Take a look at our authlogic_subdomain_fu_startup_app to get an idea of how it works together with Authlogic. Rails 3 provides native support for conditional routing based on subdomains, so this feature of the plugin will be removed in future versions.

User Interface

  • LessCSS is a library that essential gives a way to write stylesheets quickly and DRY. The syntax provided by this framework allows to define variables and to include other existing chunks of style. It’s also perfectly compatible with the CSS syntax, so just rename the css file into a .less one to be already half done.
  • More: This plugin allows you to automatically use LessCSS in Rails applications. Together with Blueprint CSS allows the development of templates in a agile and cross-browser way.
  • HAML and “Sass” http://sass-lang.com/ represent an alternative to ERB templating by giving the possibility to write HTML pages in a concise and fast way. As well as LessCSS, Sass provides an excellent solution to quickly write CSS with a concise and reusable syntax.
  • Formtastic: plugin that allow you to build forms for your model in an elegant and functional way. Note that the plugin automatically translates the labels of attributes using the language file associated with ActiveRecord.
  • Show_for is the complementary of Formtastic to view our models details in “show” pages.
  • Paperclip: great plugin for handling attachments and well handled by Formtastic.

Business Logic

  • Inherited Resources is an excellent plugin for writing controllers quickly and DRY. The use of this plugin will left our controller classes a little “empty” …
  • Active Scaffold: this plugin is useful especially to manage the CRUD of any models in the administration pages of the site. It’s easy to configure and offers a great versatility to handle all situations.
  • CanCan is a small plugin for managing ACL (access control list) quickly and easily. Useful if you want to restrict access to resources without the need to build a more complex solution.
  • Will_paginate: a must for anyone who wants to paginate his dataset.

Miscellaneous

  • Configatron: With this plugin you can configure application parameters without constants or global variables.
  • Delayed Job, plugins that can handle long asynchronous processes that run in the background. Useful for newsletter management.

Testing

  • Cucumber: large library for developing Rails applications following the BDD (behaviour driven development). It ’s a great tool if used continuously and extensively, but requires a bit high learning curve. You could easily duplicate features and steps if you have no knowledge of what has already been implemented by other team members.
  • RSpec represented a revolution in the world of testing for the Rails community, shifting the focus from TDD (test driven development) to BDD. Excellent to test models and controllers. The stories have been replaced by Cucumber’s features.
  • Shoulda: plugin, originally created to apply BDD in Test::Unit classes, that perfectly fits into RSpec and very useful for testing of models.
  • FactoryGirl, plugin created as an alternative to the verbose and not very maintainable fixtures.
  • Mocha: needed to make the tests decoupled from the database, provides a clean syntax to create stubs and mock of our models.

This is only a short list of the most useful plugins available. Probably someone will choose some others for his top 20, someone else will agree with me. What do you think?

Please share your preferences.

Tagged as: , No Comments
14Mar/102

Lazy Loading DataTable with IceFaces

Original Post

IceFaces provides numerous components to facilitate the development of web applications.
Among these, one of the most ‘useful’ is definitely ice:dataTable.

This component, together with ice:dataPaginator makes it possible to paginate the entire data set of a table, showing only N rows per page.

The main weakness of this component lies in the difficulty to handle large data sets: ice:dataTable component need to receive a list containing all rows that will gradually showed.
As long as we are in the order of some hundreds of records, there is no problem to provide the entire results list to the component; but if they begin to be thousands, keep in memory such a quantity of objects can be an issue.

The solution is to manage the list of results in a lazy way: the list itself will retrieve the records to show in the current page and only when there will be a real need.

Besides this, we need also to manage the total page number: the paginator in fact invokes the method size() of the list supplied to the table calculates the number of pages.

Let’s see three possible implementations to implement the lazy loading of the list.

They will be initialized in the page backingBean and used in this way in the .jsf template:

<ice:dataTable id="data"
              value="#{backingBean.lazyLoadingList}"
              rows="10" >
....
</ice:dataTable>
<ice:dataPaginator id="scroll_1"
                  for="data"
                  fastStep="10"
                  pageCountVar="pageCount"
                  pageIndexVar="pageIndex"
                  paginator="true"
                  paginatorMaxPages="9">
...
</ice:dataPaginator>

LazyLoadingList

This list keeps in memory a list of objects corresponding to the first page. When the user navigates in the following pages, they will be retrieved by method get() and saved in a different list. Note that the constructor of the list accepts as incoming parameter totalResultsNumber, that is the total number of results to show in the table. This parameter ‘is usually the result of a count query.

package com.devinterface.lazyloading;

import java.util.AbstractList;
import java.util.List;

/**
 * This list loads and stores only the first page and the current page.
 * If pageSize is equals to totalResultsNumber, the dataTable will be non paginated: the first query will retrieve all the dataset.
 *
 * @param <T>
 */

public class LazyLoadingList<T> extends AbstractList<T>
{
  private IDataProvider<T> dataProvider;

  private List<T> firtsPageData;
  private List<T> currentPageData;

  private int currentPage = -1;
  private int totalResultsNumber;
  private int pageSize;


  /**
   * @param dataProvider, the object that will perform the query
   * @param pageSize, the number of rows to be showed in a table page
   * @param totalResultsNumber, the total number of rows as result of the database count query.
   */

  public LazyLoadingList(IDataProvider<T> dataProvider, int pageSize, int totalResultsNumber)
  {
    this.dataProvider = dataProvider;
    this.totalResultsNumber = totalResultsNumber;
    this.pageSize = pageSize;
  }


  @Override
  public T get(int i)
  {
    if (i < pageSize)
    {
      if (firtsPageData == null)
        firtsPageData = dataProvider.getBufferedData(i, pageSize);
      return firtsPageData.get(i);
    }
    int page = i / pageSize;
   
    if (page != currentPage)
    {
      currentPage = page;
      currentPageData = dataProvider.getBufferedData(i, pageSize);
    }
   
    return currentPageData.get(i % pageSize);
  }


  @Override
  public int size()
  {
    return totalResultsNumber;
  }


  public void setTotalResultsNumber(int totalResultsNumber)
  {
    this.totalResultsNumber = totalResultsNumber;
  }


  @Override
  public void clear()
  {
    firtsPageData.clear();
    currentPageData.clear();
  }
 
}

LazyLoadingMapList

This list keeps retrieved records in a HashMap. Note that the map is never cleared, so in the worst case (the user scrolls one by one all the pages) it will contains all elements of the dataset.

package com.devinterface.lazyloading;

import java.util.AbstractList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This list stores all records in a map with key = "table row index" and value = "T"
 * If pageSize is equals to totalResultsNumber, the dataTable will be non paginated: the first query will retrieve all the dataset.
 * Note that if a user moves from first to last page, one page at time, all record will be in memory.
 *
 * @param <T>
 */

public class LazyLoadingMapList<T> extends AbstractList<T>
{
  private IDataProvider<T> dataProvider;

  private int totalResultsNumber;
  private int pageSize;

  /** cache of loadedData items */
  private Map<Integer, T> loadedData;


  /**
   * @param dataProvider, the object that will perform the query
   * @param pageSize, the number of rows to be considered as "a page"
   * @param totalResultsNumber, the total number of rows as result of the database count query.
   */

  public LazyLoadingMapList(IDataProvider<T> dataProvider, int pageSize, int totalResultsNumber)
  {
    this.dataProvider = dataProvider;
    this.totalResultsNumber = totalResultsNumber;
    this.pageSize = pageSize;
    loadedData = new HashMap<Integer, T>();
  }


  @Override
  public T get(int i)
  {
    if (!loadedData.containsKey(i))
    {
      int pageIndex = i / pageSize;
      List<T> results = dataProvider.getBufferedData(i, pageSize);
      for (int j = 0; j < results.size(); j++)
      {
        loadedData.put(Integer.valueOf(pageIndex * pageSize + j), (T) results.get(j));
      }
    }
    return loadedData.get(i);

  }


  @Override
  public int size()
  {
    return totalResultsNumber;
  }


  public void setTotalResultsNumber(int totalResultsNumber)
  {
    this.totalResultsNumber = totalResultsNumber;
  }


  @Override
  public void clear()
  {
    loadedData.clear();
  }
 
}

LazyLoadingBufferedMapList

This list represents an evolution of the previous one, keeping in memory only the elements corresponding to the current page, the previous and the next.

package com.devinterface.lazyloading;


import java.util.AbstractList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * This list stores records in a map with key = "table row index" and value = "element"
 * It keeps a buffer of 3 pages: the first time it will load pages 1,2,3.
 * When user moves to page 4, all stored data will be cleared and will be retrieved page 3,4,5.
 * Basically, this list will store current page, previous page and next page.
 * If bufferSize is equals to totalResultsNumber, the dataTable will be non paginated: the first query will retrieve all the dataset.
 *
 * @param <T>
 */

public class LazyLoadingBufferedMapList<T> extends AbstractList<T>
{
  private IDataProvider<T> dataAdapter;

  private int totalResultsNumber;
  private int pageSize = 10;
  private int bufferSize = 30;

  /** cache of loadedData items */
  private Map<Integer, T> loadedData;


  /**
   *
   * @param dataAdapter, the object that will perform the query
   * @param pageSize, the number of rows to be considered as "a page"
   * @param totalResultsNumber, the total number of rows as result of the database query.
   */

  public LazyLoadingBufferedMapList(IDataProvider<T> dataAdapter, int pageSize, int totalResultsNumber)
  {
    this.dataAdapter = dataAdapter;
    this.totalResultsNumber = totalResultsNumber;
    this.pageSize = pageSize;
    this.bufferSize = pageSize * 3;
    loadedData = new HashMap<Integer, T>();
  }


  @Override
  public T get(int i)
  {
    if (!loadedData.containsKey(i))
    {
      clearMap();

      int startRow = getStartRow(i);

      int numElementToFind = bufferSize;
      if ((startRow + numElementToFind) > totalResultsNumber)
        numElementToFind = totalResultsNumber - startRow;

      List<T> results = dataAdapter.getBufferedData(startRow, numElementToFind);
      for (int j = 0; j < results.size(); j++)
        loadedData.put((startRow + j), (T) results.get(j));
    }
    return loadedData.get(i);

  }


  /**
   * clears the map except the first element that MUST be kept
   */

  private void clearMap()
  {
    T firstElement = loadedData.get(0);
    loadedData.clear();
    loadedData.put(0, firstElement);
  }


  /**
   * Calculates the index of the previous page's first element
   * @param i, the current row index
   * @return the index of the previous page's first element
   */

  private int getStartRow(int i)
  {
    int currentPage = (i / pageSize) + 1;

    int firstIndexOfCurrentPage = pageSize * (currentPage - 1);

    int firstIndexOfPreviusPage = firstIndexOfCurrentPage - (bufferSize / 3);

    if (firstIndexOfPreviusPage < 0)
      firstIndexOfPreviusPage = 0;

    return firstIndexOfPreviusPage;
  }


  @Override
  public int size()
  {
    return totalResultsNumber;
  }


  public void setNumResults(int numResults)
  {
    this.totalResultsNumber = numResults;
  }


  @Override
  public void clear()
  {
    loadedData.clear();
  }
}

Conclusions

All implementations give the opportunity to retrieve records in a lazy way. Surely the second solution is the most potentially weak as in the worst case will keep in memory all the dataset.
Surely the third implementation is the best solution from all points of view. It allows to have in memory only a limited number of records and to meet any potential “back and forth” of the user.

Sources on my GitHub repository LazyLoadingDataTable

18Jan/100

Building a Web 2.0 startup: Part 2 – analysis of the idea

Previous posts:

In this second post about the startup of a web application we will discuss how to analyze our first idea.

In the previous post we have discussed about possible competitors and we have understood how our idea can produce business.

At this point, before moving to the analysis phase, we must answer three questions:

  • What is the goal of our project? I mean, what we’re going to carry out?
  • Why our project is valuable?
  • What are the project’s success criteria?

Answering these questions is crucial in the analysis phase of our idea.
If even one of these questions will not get a valid answer, it means that our idea will be to reconsider or to totally discard.

The first question is necessary to closely focus project goals and needed to better understand what we want to do and how it will be implemented.
At first we will probably don’t have a clear idea of all projects features, however, the important thing is to focus on key issues, milestones, to be achieved.

The second question is linked to the question of previous post, “Good idea vs Good Business Idea” but it is essential to understand why an end user will use our application.
We must get a user’s perspective to understand whether our product will actually have a glimmer of success, or will simply forgot.
So it is better to ask who will use our product and how much; these two factors will be key points to calculate the business value of our application.

The third question requires to set up goals: decide what are the criteria for a successful implementation is crucial to understanding how much to invest on the application after the releasing date.
Success criteria can be for example X number of people using the application on a regular basis after an Y time period; to reach X euros for a service fee; the interest aroused by the media in general.

Once you have answered these three questions, we will be ready to go on with planning and analysis, the subject of our future articles.
——————-

Post precedenti:

In questo secondo post riguardante lo startup di una web application parleremo di come analizzare la nostra idea.

Nel post precedente abbiamo analizzato i possibili competitors e abbiamo compreso in che modo la nostra idea puo’ produrre del business.

A questo punto, prima di passare alla fase di analisie pianificazione, è opportuno rispondere a tre domande:

  • Quale è l’obiettivo del nostro progetto? Cioè, cosa andremo a realizzare?
  • Quale sarà il valore aggiunto che introdurrà il nostro progetto?
  • Quali saranno i criteri del suo successo?

Rispondere a queste domande è fondamentale in fase di analisi della propria idea.
Se anche una sola di queste domande non otterrà una valida risposta significa che la nostra idea sarà da ripensare o da gettare.

La prima domanda è necessaria per poter focalizzare attentamente gli obiettivi del progetto. Serve specialmente per avere bene l’idea di cosa si vuole ottenere e per ipotizzare come verrà implementata. All’inizio probabilmente non avremo una chiara idea di tutto ciò che verrà prodotto, l’importante è comunque focalizzare l’attenzione sui punti chiave da raggiungere.

La seconda domanda si allaccia alla questione del post precedente, “Good idea vs Good Business Idea” ma è comunque fondamentale da porsi anche per cercare di capire perchè un utente finale utilizzerà la nostra applicazione. E’ necessario mettersi nell’ottica di un utente in modo da capire se effettivamente il nostro prodotto potrà avere uno spiraglio di successo o se verrà semplicemente snobbato.
Quindi è meglio chiedersi chi utilizzerà il nostro prodotto e in che misura: questi due fattori saranno dei punti chiave per calcolare il guadagno che verrà prodotto dalla nostra applicazione.

La terza domanda ci obbliga invece a porci degli obiettivi: stabilire quali sono i criteri di successo di una applicazione è fondamentale per capire fino a che punto investire su di essa una volta terminato lo sviluppo.
Criteri di successo possono essere un numero X di utenti che utilizzano l’applicazione con regolarità dopo un tempo Y; il raggiungimento di X euro per un servizio a pagamento; l’interesse suscitato dai media in generale.

Una volta risposto a queste tre domande, saremo pronti a partire con la fase di pianificazione e analisi, oggetto dei prossimi articoli.

4Jan/100

Building a Web 2.0 startup: Part 1 – idea and analysis of competitors

Previous posts:

One of the fundamental aspects for the start of a startup, especially in the web world, is to have a good initial idea.

Often you think you have found the right idea for a successful application, but things then prove otherwise.

Happened to us too many times to wake up any minute and say “This time I get the idea!” And then realize that this idea has already been implemented by other companies in several ways.

Anyone who has tried knows it’s really hard to find the innovative idea that nobody has ever thought, it’s easier to design an evolution of other existing applications or even thinking about the fork of something already implemented to realize it in a differently better way.

I would like at this point to distinguish two types of good ideas for a startup:
* take something existing and add innovative features
* take something existing and rework in order to obtain a new, better and winning implementation.

In both cases we start from something existing as a starting point: it has no sense to “reinvent the wheel”; try to have innovative or unique ideas is really hard.

We should therefore focus on what already exists on the market and prepare small changes and innovations to make our application “unique” and valuable.

The next point to consider is certainly the answer to the question: “Good Idea or Good Business Idea?”.

A Good Idea can certainly does not create immediate benefit to the team that develops, but at the same time it should not be rejected beforehand: a good idea can give to the company visibility reputation and prestige.

Take for example two companies that have had some “good ideas”:

  • Thoughtbot: During their application development they have made available to the Ruby on Rails community a number of good plugins that have ensured visibility and reputation to the team. These plugins have also given them the image of competent and reliable company, probably increasing the number of customers for other applications.
  • Gravatar: Gravatar also proved to be a good idea for its developers. Completely free, certainly the application wasn’t able to gain immediate: the profit came later, when Automattic has bought the entire application.

Let’s look at a company that has instead had a good business idea:

  • Internaut Design has launched in recent months ScrumNinja, another tool to manage projects according to the SCRUM framework. This application is part of the second type of ideas where an existing model was revised to create a better product and more appealing than those offered by competitors (the competitors are however numerous and gain market will not be so simple anyway). In any case, users will pay to use this application, and that creates a “good business idea.”

Let’s return back to us of DevInterface and try to categorize our kind of idea:
the project on which we want to “invest” definitely belongs to the world of “good idea”: certainly will not give us an immediate profit, all depend on what will be appreciated and used by the final users.
We hope that this application is able to give us the visibility we need, and then maybe the profit that, at this stage of analysis, it is only a hypothesis.

Let say that this is not and will not be the only application in the pipeline.
In fact we are working on these days even a “good business idea that can be proposed DevInterface to a specific slice of the market and create a more immediate tangible benefit.
This second application will not be the subject of our discussion in these articles, since we believe is much more exciting for us and for you to talk about a startup “bet” that can bring us to success or that can only be another forgot application after the review on KillerStartups.

The team DevInterface

————————

Post precedenti:

Uno degli aspetti fondamentali per l’avvio di una startup, specialmente nel mondo web, è avere una valida idea di partenza.

Talvolta si pensa di avere trovato l’idea giusta per un’applicazione di sicuro successo, ma il più delle volte poi le cose vanno diversamente.

Anche a noi è capitato molte volte di svegliarci da un momento all’altro e dire “Stavolta ho l’idea!” per poi realizzare che questa idea è già stata implementata da altre company in svariate modalità.

Chiunque ci abbia provato sa che è veramente difficile trovare l’idea innovativa a cui nessuno ha mai pensato; riesce più facile ideare un’evoluzione di altre applicazioni esistenti o addirittura pensare al fork di un qualcosa di già implementato da realizzare in modo diverso o in modo migliore.

Possiamo cosi riassumere le due tipologie di idee valide per una startup:
* prendere come modello qualcosa di esitente ed aggiungere funzionalità innovative
* prendere come modello qualcosa di esitente e rielaborarlo in modo da ottenere un’implementazione nuova, migliore e più accattivante.

In entrambi i casi si parte da qualcosa di esistente: non ha senso infatti “reinventare la ruota”; cercare di avere idee così innovative da essere uniche è veramente un’impresa.

Conviene quindi focalizzarci su quanto già esiste sul mercato ed approntare piccole modifiche ed innovazioni per rendere comunque la nostra applicazione “unica” e di valore.

Il punto successivo da prendere in considerazione è la risposta alla domanda: “Good Idea or Good Business Idea?”.

Una Good Idea puo’ non creare profitto immediato al team che la sviluppa, ma allo stesso tempo non è da scartare a priori: una buona idea puo’ creare visibilità per l’azienda, notorietà e prestigio.

Prendiamo ad esempio due company che hanno avuto delle “good ideas”:

  • Thoughtbot: durante lo sviluppo di applicazioni web2.0 ha messo a disposizione della comunità Ruby on Rails una serie di validi plugin che hanno garantito loro visibilità e notorietà. Questi plugin hanno dato inoltre loro l’immagine di società affidabile e competente, aumentando probabilmente il loro numero di clienti per le successive applicazioni.
  • Gravatar: anche gravatar si è rivelata una buona idea per i suoi sviluppatori. Completamente gratuita, non è stata sicurmanete un’applicazione in grado di dare guadagno nell’immediato: il profitto è venuto successivamente, quando Automattic ha comprato l’intera applicazione.

Analizziamo ora una company che ha invece avuto una good business idea:

  • Internaut Design: ha lanciato in questi mesi ScrumNinja, l’ennesimo tool per gestire progetti secondo il framework SCRUM. Questa applicazione fa parte della seconda tipologia di idee: ha preso un modello esistente e lo ha rielaborato creando un prodotto migliore e più accattivante di quello proposto dai competitors( i competitors sono infatti innumerevoli e acquisire una fetta di mercato non si rivelerà comunque cosi semplice). Essendo comunque l’utilizzo di questa applicazione a pagamento, ecco creata una “good business idea”.

Torniamo ora a noi di DevInterface e cerchiamo di categorizzare la nostra tipologia di idea:
il progetto su cui vogliamo “investire” appartiene sicuramente al mondo delle “good idea”: sicuramente non ci darà un profitto immediato; tutto dipenderà da quanto verrà apprezzata ed utilizzata dal pubblico finale.

La nostra speranza è che tale applicazione sia in grado di darci la visibilità di cui abbiamo bisogno e in seguito magari anche il profitto che, nella fase attuale di analisi, è solo un’ipotesi.

C’è da dire che questa non è e non sarà l’unica applicazione in cantiere.
Stiamo infatti elaborando in questi giorni anche una “good business idea”, in grado da proporre DevInterface ad una fetta specifica di mercato e di creare un profitto tangibile più nell’immediato.

Questa seconda applicazione non sarà comunque oggetto della nostra trattazione in questi articoli, in quanto riteniamo sia molto più stimolante per noi e per voi parlare di startup “scommessa”, in grado di garantirci il successo o, nel caso peggiore, di essere l’ennesima applicazione dimenticata dopo la recensione su KillerStartups.

Il team di DevInterface

4Jan/100

Building a web 2.0 startup: Introduction

Original post:

This will be the first one of a series of articles describing the way will lead DevInterface to the creation of a Web 2.0 application for the public.

In particular, we will discuss in detail all the key points that will result from the initial concept, requirements analysis, through development, testing, marketing up to the application into production.

In each post we will try to detail and to justify the most possible our choices in order to show the philosophy of DevInterface about web 2.0 development and make all of you readers as close as possible to us to make up the goal.

We wish you a good reading.

——————

Post originale:

Questo sarà il primo di una serie di articoli che descriveranno il cammino che porterà DevInterface alla realizzazione di una applicazione web 2.0 destinata al grande pubblico con la speranza di farne un prodotto di successo.

In particolare, tratteremo in dettaglio tutti i punti chiave che porteranno dall’idea iniziale, all’analisi dei requisiti, passando per lo sviluppo, il testing, il marketing fino ad arrivare alla messa in produzione dell’applicazione.

In ogni post cercheremo di dettagliare e motivare il più possibile le nostre scelte in modo da mostrare la filosofia di DevInterface per quanto riguarda lo sviluppo web 2.0 e rendere tutti voi lettori il più possibile vicini a noi fino al raggiungimento del traguardo.

Vi auguriamo quindi una buona lettura.

7Dec/092

Add current_user to Cucumber step definitions

Cucumber provides a great way to apply BDD during your rails development.

Sometimes you need to populate your testing database with background data and sometimes you need to access the

current_user

method.

Generally

current_user

is a method defined in the

ApplicationController

class or in a user related module.

So it’s not accessible to Cucumber’s scenarios: let’s add it and share to all step definition’s classes.

#authentication_steps.rb
module AuthenticationHelpers
  def current_user_session
    return @current_user_session if defined (@current_user_session)
    @current_user_session = UserSession.find
  end

  def current_user
    return @current_user if defined?(@current_user)
    @current_user = current_user_session && current_user_session.user
  end
end
World(AuthenticationHelpers)
Tagged as: , 2 Comments
25Oct/090

How to implement a viewing system in Rails

Hello everyone.

Today I will show how to implement a viewing system for any model of your Rails application.

Suppose we have a model News and we would like to keep track of how many times a single news has been displayed, in order to implement box like “the most ’seen’” etc..

Suppose we have a model News created in this way:

myapp/db/migrate/001_create_news.rb:

class CreateNews < ActiveRecord::Migration
  def self.up
    create_table :news do |t|
      t.string  :title
      t.text    :content
      t.date    :online_date_start, :null => true
      t.date    :online_date_end,   :null => true
      t.boolean :online,            :null => false, :default => true
      t.timestamps
    end
  end

  def self.down
    drop_table :news
  end
end

myapp/app/models/news.rb:

class News < ActiveRecord::Base
  validates_length_of :title, :within => 2..255
  validates_presence_of :title, :content, :online_date_start
end

The basic idea is to increment a counter displayed every time a user views the news.

Some considerations:

an implementation of this type would allow a user to press the refresh button and constantly increase the counter.

So the goal is to have a clever mechanism that increases the counter:

- 1 once for each registered user
- 1 only once per IP in the case of guest

We would also like that this mechanism could be applied not only to model News, but to any model of our application.

Then we create the migration of our model Viewing:

myapp/db/migrate/002_create_viewings.rb:

class CreateViewings < ActiveRecord::Migration
  def self.up
    create_table   :viewings do |t|
      t.string     :ip
      t.string     :viewable_type
      t.integer    :viewable_id
      t.references :person
      t.datetime   :created_at
    end
  end

  def self.down
    drop_table :viewings
  end
end

and the corresponding model:

myapp/app/models/viewing.rb:

class Viewing < ActiveRecord::Base
  # RELATIONSHIPS
  belongs_to :viewable, :polymorphic => true, :counter_cache => :popularity
  belongs_to :viewer, :class_name => "Person", :foreign_key => "person_id"

  # VALIDATIONS
  validates_uniqueness_of :ip, :scope => [:viewable_id, :viewable_type, :person_id]
  validates_uniqueness_of :person_id, :allow_nil => true

  # OTHER
  def viewable_type=(sType)
    super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

As you can see, the model Viewing is not closely tied to model News, but given its polimorphic nature, it may be associated with any model.

At this point, we can associate the model Viewing to the News :

myapp/app/models/news.rb:

class News < ActiveRecord::Base
  validates_length_of :title, :within => 2..255
  validates_presence_of :title, :content, :online_date_start
  has_many :viewings, :as => :viewable
end

and add the column “counter cache to the table News:

myapp/db/migrate/003_add_popularity_to_news.rb:

class AddPopularityToNews < ActiveRecord::Migration
  def self.up
    add_column :news, :popularity, :integer, :default => 0
  end

  def self.down
    remove_column :news, :popularity
  end
end

Now we have everything necessary to implement the mechanism for viewing in our model News.

Let’s show how to increment the counter to “show” of the news.

myapp/app/controllers/news_controller.rb:

class NewsController < ApplicationController
  after_filter  :record_view, :only => :show
 
  def show
    @news = News.find(params[:id]
  end
 
  private
 
  def record_view
    @news.viewings.create(:ip => request.remote_ip, :viewer => current_user) unless @news.nil?
  end
end
Tagged as: , No Comments
29Sep/090

Rails Authlogic + Subdomain_fu Template

The second free Rails startup skeleton written by DevInterface and releases under Rails MIT license is Rails Authlogic + Subdomain_fu Template.

Rails Authlogic + Subdomain_fu Template

This template works as a skeleton for any new Rails application (using Rails 2.3) that aims to provide authentication with subdomain support.

To install and run this template perform following actions:

git clone git://github.com/devinterface/authlogic_subdomain_fu_startup_app.git
cp config/database.yml.example config/database.yml
rake gems:install
rake gems:install RAILS_ENV=test
rake db:migrate

Skeleton Behaviour

This template sets up a working application with support for authentication and subdomain.

Application workflow

  • Guest access a public section of the site (http://localhost:3000)
  • Guest chooses to register new account (http://localhost:3000/account/new), including itself as user, and becomes account’s owner
  • Accounts owner goes to his account (subdomain) url (http://useraccount.localhost:3000/login) and logs into his account
  • Accounts owner can add more users to it’s account (http://useraccount.localhost:3000/users/new)
  • Each created user can log into the account they belong

Skeleton features

Here’s a list of what this template sets up:

Rails

Javascript

  • jQuery as javascript framework instead of prototype

CSS

Testing

Other

Some other useful gems/plugins such as:

29Sep/090

Rails Startup Template

Hi all.

Today I would like to show the first of two free Rails startup applications written by DevInterface.

They are both hosted on GitHub and releases with Rails MIT licese. So feel free to clone/fork/patch them.

Rails Startup Template

This template lets you to create quickly new Rails applications using Rails 2.3. To use it, just specify the -m switch when creating a Rails application:

rails new_app_name -m http://github.com/devinterface/Rails-Startup-App/raw/master/rails_startup.rb

Generated Application

Here’s a list of what this template sets up:

SCM
* git repository

Javascript
* Choice of Prototype or jQuery

CSS
* Blueprint as css framework

Rails
* Choice of Authlogic with or without OpenID support (includes models, controllers and views)

Testing
* BDD testing support using RSpec, Cucumber, and FactoryGirl

Other
Some other useful gems/plugins such as: