How to model a custom search form in Rails
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/)
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:
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
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).
<% 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.
The top 20 plugins to create a Rails application
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.
Lazy Loading DataTable with IceFaces
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:
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.
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.
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.
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
Building a Web 2.0 startup: Part 2 – analysis of the idea
Previous posts:
- Building a Web 2.0 startup: Part 1 – idea and analysis of competitors
- Building a web 2.0 startup: Introduction
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:
- Creare una startup web 2.0: Parte 1 – idea e analisi competitors
- Creare una startup web 2.0: Introduzione
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.
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
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.
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
method.
Generally
is a method defined in the
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.
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)
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:
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:
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:
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:
# 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:
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:
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:
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
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:
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
- Blueprint as css framework
Testing
- BDD testing support using RSpec, Cucumber, and FactoryGirl
Other
Some other useful gems/plugins such as:
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:
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: