dao
SYNOPSIS
a sa-weet-ass library for structuring rails applications using the 'data
access object' design pattern. dao consists of two main data access
objects, api objects and conducer objects. conducers combine the
presenter pattern with the conductor pattern.
API
class Api < Dao::Api
call('/posts') do
get do
data[:posts] = Post.all.map{|post| post.attributes}
end
post do
post = Post.new(params[:post])
if post.save
data[:post] = post.attributes
else
status 420
end
end
end
end
CONDUCER
wikipedia has this to say about dao in general
In computer software, a data access object (DAO) is an object that
provides an abstract interface to some type of database or other
persistence mechanism. By mapping application calls to the persistence
layer, DAOs provide some specific data operations without exposing
details of the database. This isolation supports the single
responsibility principle. It separates what data accesses the
application needs, in terms of domain-specific objects and data types
(the public interface of the DAO), from how these needs can be satisfied
with a specific DBMS, database schema, etc. (the implementation of the
DAO).
-- http://en.wikipedia.org/wiki/Data_access_object
and this to say about the single responsibility principle
In object-oriented programming, the single responsibility principle
states that every class should have a single responsibility, and that
responsibility should be entirely encapsulated by the class. All its
services should be narrowly aligned with that responsibility.
Responsibility [is defined] as a reason to change, and [single
responsibility means] that a class or module should have one, and only
one, reason to change. As an example, consider a module that compiles and
prints a report. Such a module can be changed for two reasons. First,
the content of the report can change. Second, the format of the report
can change. These two things change for very different causes; one
substantive, and one cosmetic. The single responsibility principle says
that these two aspects of the problem are really two separate
responsibilities, and should therefore be in separate classes or
modules. It would be a bad design to couple two things that change for
different reasons at different times.
-- http://en.wikipedia.org/wiki/Single_responsibility_principle
even though rails is the sweet, its ActiveRecord class violates (or, at
least, encourages a programmer to violate) the single responsibility
principle
this leads to obvious problems
Jim Weirich, at the end of his SOLID Ruby Talk at the 2009 Ruby
Conference, asks the audience: "ActiveRecord objects implement a domain
concept and a persistence concept. Does this violate the SRP (Single
Responsibility Principle)?" The audience agrees that it does violate the
SRP. Jim asks if this bothers them. Many members of the audience say
yes. Why? It makes testing harder. It makes the persistence object a lot
heavier.
-- http://programmers.stackexchange.com/questions/119352/does-the-activerecord-pattern-follow-encourage-the-solid-design-principles#comment293734_119352
and subtle yet sweeping consequences (as described by uncle bob)
The problem I have with ActiveRecord is that it creates confusion about
... two very different styles of programming. A database table is a
data structure. It has exposed data and no behavior. But an ActiveRecord
appears to be an object. It has “hidden” data, and exposed behavior. I
put the word “hidden” in quotes because the data is, in fact, not
hidden. Almost all ActiveRecord derivatives export the database columns
through accessors and mutators. Indeed, the Active Record is meant to be
used like a data structure.
On the other hand, many people put business rule methods in their
ActiveRecord classes; which makes them appear to be objects. This leads
to a dilemma. On which side of the line does the Active Record really
fall? Is it an object? Or is it a data structure?
This dilemma is the basis for the oft-cited impedance mismatch between
relational databases and object oriented languages. Tables are data
structures, not classes. Objects are encapsulated behavior, not database
rows.
...
The problem is that Active Records are data structures. Putting business
rule methods in them doesn’t turn them into true objects. In the end,
the algorithms that employ ActiveRecords are vulnerable to changes in
schema, and changes in type. They are not immune to changes in type, the
way algorithms that use objects are.
...
So applications built around ActiveRecord are applications built around
data structures. And applications that are built around data structures
are procedural—they are not object oriented. The opportunity we miss
when we structure our applications around ActiveRecord is the
opportunity to use object oriented design.
-- https://sites.google.com/site/unclebobconsultingllc/active-record-vs-objects
and a clear solution (again, uncle bob)
I am not recommending against the use of ActiveRecord. I think the
pattern is very useful. What I am advocating is a separation between the
application and ActiveRecord.
ActiveRecord belongs in the layer that separates the database from the
application. It makes a very convenient halfway-house between the hard
data structures of database tables, and the behavior exposing objects in
the application.
Applications should be designed and structured around objects, not data
structures. Those objects should expose business behaviors, and hide any
vestige of the database.
-- https://sites.google.com/site/unclebobconsultingllc/active-record-vs-objects
welcome to the dao
DESCRIPTION
API
applications that are written on dao look like this in ruby
result = api.call('/posts/new', params)
and like this in javascript
result = api.call('/posts/new', params)
in command-line applications they look like this
result = api.call('/posts/new', params)
and in tests this syntax is used
result = api.call('/posts/new', params)
when a developer wants to understand the interface of a dao application she does
this
vi app/api.rb
when a developer of a dao application wants to play with a dao application
interactively she does
(rails console)
> api = Api.new result = api.call('/posts/new', params)
when a remote client wants to understand the api of a dao application she
does
curl --silent http://dao.app.com/api | less
this kind of brutally consistent interface is made possible by structuring
access to data around the finest data structure of all time - the hash.
in the case of dao the hash is a well structured and slightly clever hash,
but a simple hash interface is the basis of every bit of goodness dao has
to offer.
in dao, application developers do not bring models into controllers and,
especially not into views. instead, a unified interface to application
logic and data is used everywhere: in tests, in controllers, from the
command-line, and also from javascript.
this seperation of concerns brings with it many, many desirable qualities:
-
total seperation of concerns between the front and back end of a web
application. when developers are using dao changes to the data model
have zero effect on controllers and views.
-
issues related to having models in controllers and views such as
difficulty reasoning about caching and n+1 queries in views killing
the db simply disappear.
-
bad programming practices like using quasi-global variables
(current_user) or decorating models with view specific attributes
(password_verification) are no longer needed.
-
developers are able to reason over the abilities of an application by
reading only a few source files.
-
databases can be swapped, mixed, or alternate storage/caching
mechanisms added at any time without affecting the application's
controllers or views.
-
transition from form based views to semi-ajax ones to fully-ajax ones
is direct.
-
forms and interfaces that involve dozens of models are as easy to deal
with as simple ones.
-
code can be optimized at the interface.
READING
INSTALL
gem 'dao', :path => File.expand_path('..') ### Gemfile
rails generate dao api
vim -o app/api.rb app/controllers/api_controller.rb
curl --silent http://0.0.0.0:3000/api
curl --silent http://0.0.0.0:3000/api/ping
HISTORY
4.0.0
- dao depends has tied itself to rails, for better or worse...
- drop custom form encoding. just use a rack-like approach.
- dao form parameter encoding has changed slightly to 'dao[/api/path][x,y,z]=42'
- dao form paramters are now preparsed in a before filter