As I said in the last post as ruby/rails developers is very common to start our projects with something very similar to the Rails scaffold…
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
end
def index
@posts = Post.all
end
#....
end
… In that post I show you an example of a simple blog app, with a functionality very similar to what a Rails scaffold can do but without coupling our code to Active Record.
The example was using Sinatra, but not to tell you not tu use Rails…
But to tell you that you can write your code in a way, where you can delay decisions like “Which framework should I use?”, or “Which ORM library should I use?”…
You don’t have to make does decisions since the beginning… Or more than that… You don’t have to keep your first day decisions forever!…
If you design your software in a different way…
You can see the app working here and the code here.
The behavior of the app is exposed by stateless module functions in the module
Blog
that lives on lib/blog.rb.
And is actually the same code used in the last example.
module Blog
def self.list_posts(store)
store.all.sort_by(&:created_at).reverse
end
def self.get_post(id, store)
store.find(id)
end
def self.new_post_form
PostForm.new
end
def self.create_post(params, store, current_time = Time.now)
ProcessPostForm.(params) do |form|
store.create(form.to_h.merge(created_at: current_time))
end
end
def self.edit_post_form(id, store)
PostForm.new(store.find(id))
end
def self.update_post(post_id, params, store)
ProcessPostForm.(params) do |form|
store.update(post_id, form.to_h)
end
end
def self.delete_post(post_id, store)
store.destroy(post_id)
end
#...
end
There is a test file for each CRUD action…
And there is a test file for our “store”…
And now the behavior of our app is exposed on the PostsController of our Rails app…
class PostsController < ApplicationController
def index
posts = Blog.list_posts(store)
render :index, locals: { posts: posts }
end
def new
form = Blog.new_post_form
render locals: { form: form }
end
def create
status = Blog.create_post(params, store)
if status.success?
redirect_to posts_path
else
render :new, locals: { form: status.form }
end
end
def show
post = Blog.get_post(params[:id], store)
render locals: { post: post }
end
def edit
form = Blog.edit_post_form(params[:id], store)
render locals: { form: form, post_id: params[:id] }
end
def update
status = Blog.update_post(params[:id], params, store)
if status.success?
redirect_to posts_path
else
render :edit, locals: { form: status.form, post_id: params[:id] }
end
end
def destroy
Blog.delete_post(params[:id], store)
redirect_to posts_path
end
private
def store
BlogStore
end
end
Don’t you think that is nice to be able to change our framework decision? Don’t you think that is nice to be able to express our business logic decoupled from a specific database technology?
I send an email each week, trying to share knowledge and fixes to common problems and struggles for ruby on rails developers, like How to fetch the latest-N-of-each record or How to test that an specific mail was sent or a Capybara cheatsheet. You can see more examples on Most recent posts or All post by topic.