Here's the link to the news release on stonean.com.
btw, if you'd like to get this type of information from twitter, you can.
Saturday, November 15, 2008
Thursday, November 13, 2008
Ruby: inject
Inject is a powerfully cool method provided by the Enumerable module. At first I was a little confused by the name. I didn't know what it meant. Here's a simple example:
So, by passing [] into the inject method, the variable sum is initialized as an empty array. Now the inject method will iterate over the array an add the value return by x10 to the new array (sum).
Calling inject on a hash:
**Notice that inject when called on a hash will pass the key and value in one array: [key, value]. Hence the aray[0] and aray[1] calls.
Of course, since you are creating an entirely new hash, you could change the key if you wanted. This was just a simple example.
Anyway...just a short update. It's been a while since I shared. :)
an_array = [1, 2, 3, 4, 5, 6]
def x10(v)
v * 10
end
an_array.inject([]) do |sum, num|
sum << x10(num)
end
=> [10, 20, 30, 40, 50, 60]
So, by passing [] into the inject method, the variable sum is initialized as an empty array. Now the inject method will iterate over the array an add the value return by x10 to the new array (sum).
Calling inject on a hash:
def convert_hash(hsh)
hsh.inject({}) do |sum, aray|
sum.merge( { aray[0] => some_conversion_method(aray[1]) } )
end
end
**Notice that inject when called on a hash will pass the key and value in one array: [key, value]. Hence the aray[0] and aray[1] calls.
Of course, since you are creating an entirely new hash, you could change the key if you wanted. This was just a simple example.
Anyway...just a short update. It's been a while since I shared. :)
Labels:
Ruby
Friday, October 3, 2008
Dynamic Content and Action Caching
I recently deployed a Rails application that, for various reasons, required caching dynamic content. All dynamic content is represented by a core "Content" model that contains a url attribute. In order for my app to show these pages I have the following as my last route:
As you can see, the contents/show is my catchall action. If I can find a content url matching :p1, I show the content.
Another feature of the application is that you can restrict access to this content to one or many user groups via my Lockdown gem. At a basic level, Lockdown works by testing for access in a before_filter it adds to all controllers. To ensure I don't expose protected content, I needed to use action caching to have my before filters execute before showing the page.
Action caching typically creates a cache file that corresponds to the action. However, in this case, the show action represents multiple urls. Here's how you get around that:
If the url for the contents was "/about-us", the cache would be "about-us". If the content url was "/reunion-pictures?page=2", the cache would be "reunion-pictures_2". So forth and so on.
I was going setup a route that would turn "/reunion-pictures?page=2" to "/reunion-pictures/2", but for some reason, adding the route to do this broke the display of the page links in will_paginate. I'm not sure why and the url wasn't that important to me. The value provided by will_paginate was more important to me than pretty routes in this instance. I may revisit this issue later...but I need to get back on point.
So...I'm caching now, cool. Now I just have to account for changing content, enter the sweepers:
One type of dynamic content is a gallery that can contain multiple images. In order to make sure my galleries are kept up-to-date, I needed this sweeper to expire the caches of the galleries that contained the images I was creating/updating/deleting.
In order for this ImageSweeper to clean house, I needed to add the following to the ImagesController:
Now my galleries will stay current and I won't be locked into showing cached galleries that represent old/incorrect information.
The cached pages are ridiculously faster...and yes, that's a technical term similar to "ludicrous speed".
map.dynamic ':p1',
:controller => "contents",
:action => "show",
:requirements => {:p1 => /[\w|-]*/}
As you can see, the contents/show is my catchall action. If I can find a content url matching :p1, I show the content.
Another feature of the application is that you can restrict access to this content to one or many user groups via my Lockdown gem. At a basic level, Lockdown works by testing for access in a before_filter it adds to all controllers. To ensure I don't expose protected content, I needed to use action caching to have my before filters execute before showing the page.
Action caching typically creates a cache file that corresponds to the action. However, in this case, the show action represents multiple urls. Here's how you get around that:
class ContentsController < ApplicationController
caches_action :show,
:cache_path => proc{|c| c.send(:cached_page, c.params[:p1], c.params[:page]) }
def show
#logic to render multiple content types
end
private
# This method will return my cache name
def cached_page(url, page)
page ||= "1"
"#{url}_#{page}"
end
end
If the url for the contents was "/about-us", the cache would be "about-us". If the content url was "/reunion-pictures?page=2", the cache would be "reunion-pictures_2". So forth and so on.
I was going setup a route that would turn "/reunion-pictures?page=2" to "/reunion-pictures/2", but for some reason, adding the route to do this broke the display of the page links in will_paginate. I'm not sure why and the url wasn't that important to me. The value provided by will_paginate was more important to me than pretty routes in this instance. I may revisit this issue later...but I need to get back on point.
So...I'm caching now, cool. Now I just have to account for changing content, enter the sweepers:
class ImageSweeper < ActionController::Caching::Sweeper
observe Image
def after_create(post)
expire_cache_for(post)
end
def after_update(post)
expire_cache_for(post)
end
def after_destroy(post)
expire_cache_for(post)
end
private
def expire_cache_for(record)
record.galleries.each do |gal|
for i in 1..((gal.artworks.length / 10) + 1)
expire_action("#{gal.url}_#{i}")
end
end
end
end
One type of dynamic content is a gallery that can contain multiple images. In order to make sure my galleries are kept up-to-date, I needed this sweeper to expire the caches of the galleries that contained the images I was creating/updating/deleting.
In order for this ImageSweeper to clean house, I needed to add the following to the ImagesController:
cache_sweeper :image_sweeper,
:only => [:create, :update, :destroy]
Now my galleries will stay current and I won't be locked into showing cached galleries that represent old/incorrect information.
The cached pages are ridiculously faster...and yes, that's a technical term similar to "ludicrous speed".
Labels:
RubyOnRails
Saturday, September 13, 2008
Adobe Air + Dojo
I've been spending entirely too much time (days and days) reviewing javascript frameworks. What I'm looking for is something I can use within both Adobe Air and a standard web site.
If you search for Adobe Air + javascript frameworks the obvious winner would be Ext JS. I had already heard of this framework and have some friends using it with (apparently) no major gripes. I took this as a good sign.
So, from the discussions of using Air + Ext, I downloaded Aptana Studio. Long story short, I created a new Air project and noticed something not found in the Ext JS 2.2 download, the Air support for Ext. Me being me, I had to find out where these javascript libraries originated because I wanted to be certain the libraries were 'officially' maintained by either Ext or Adobe.
I do not know why, but Air libraries that were included in Ext 2.1 were not included in Ext 2.2. They have Ext copyright notices so I'm assuming Ext would maintain them, but I could not find a clear answer as to why they weren't included in 2.2. As good as Ext may be, this uncertainty over the Air support forced me to look elsewhere.
After many nights of looking I've settled on Dojo. It appears to be the most polished framework offering the most high-level components and Air support.
I'm just starting to dig into this, but it should be fun. Who knows, I may even change from Dojo to something else after digging deeper. I hope that will not be the case.
If you know the reasons behind the missing Air libraries in Ext 2.2 or feel strong about another framework, I'd love to hear why.
Update: Well, the Air libraries missing from Ext 2.2 are now back. Now I'll have to decide between Dojo and Ext
If you search for Adobe Air + javascript frameworks the obvious winner would be Ext JS. I had already heard of this framework and have some friends using it with (apparently) no major gripes. I took this as a good sign.
So, from the discussions of using Air + Ext, I downloaded Aptana Studio. Long story short, I created a new Air project and noticed something not found in the Ext JS 2.2 download, the Air support for Ext. Me being me, I had to find out where these javascript libraries originated because I wanted to be certain the libraries were 'officially' maintained by either Ext or Adobe.
I do not know why, but Air libraries that were included in Ext 2.1 were not included in Ext 2.2. They have Ext copyright notices so I'm assuming Ext would maintain them, but I could not find a clear answer as to why they weren't included in 2.2. As good as Ext may be, this uncertainty over the Air support forced me to look elsewhere.
After many nights of looking I've settled on Dojo. It appears to be the most polished framework offering the most high-level components and Air support.
I'm just starting to dig into this, but it should be fun. Who knows, I may even change from Dojo to something else after digging deeper. I hope that will not be the case.
If you know the reasons behind the missing Air libraries in Ext 2.2 or feel strong about another framework, I'd love to hear why.
Update: Well, the Air libraries missing from Ext 2.2 are now back. Now I'll have to decide between Dojo and Ext
Thursday, August 14, 2008
Rails Routes
I don't frequently have the need to create anything more than the basic "map.resources :model" route. As things go, this means I don't remember how to anything other than the basic routes and forget the difference between :collection and :member and how that translates to urls and helper methods. So, I'm creating this page as a reference for myself so I don't have to google and piece together bits of information.
Basic:
Nested:
Adding methods outside the standard REST methods.
The member option means you're going to access a 'member' of the collection. Why this didn't stick the first time, I have no idea. I'm just slow I guess.
There will be more added as I run across more usage needs. Let me know if I messed anything up here.
Basic:
map.resources :users
#Which gives you the following helpers:
users_path
users_url
new_user_path
new_user_url
user_path(@user)
user_url(@user)
edit_user_path(@user)
edit_user_url(@user)
#Accessed via:
/users
/users/new
/users/1
/users/1/edit
Nested:
map.resources :posts do |posts|
posts.resources :comments
end
#Which gives you the following helpers:
post_comments_path
post_comments_url
new_post_comment_path
new_post_comment_url
post_comment_path(@post,@comment)
post_comment_url(@post,@comment)
edit_post_comment_path(@post,@comment)
edit_post_comment_url(@post,@comment)
#Accessed via:
post/1/comments
post/1/comments/new
post/1/comments/1
post/1/comments/1/edit
Adding methods outside the standard REST methods.
map.resources :users, :collection => {:active => :get}
#Which gives you the following helpers:
active_users_path
active_users_url
#Accessed via:
/users/active
The member option means you're going to access a 'member' of the collection. Why this didn't stick the first time, I have no idea. I'm just slow I guess.
map.resources :users, :member => {:activate => :post}
#Which gives you the following helpers:
activate_user_path(@user)
activate_user_url(@user)
#Accessed via:
/users/1/activate
There will be more added as I run across more usage needs. Let me know if I messed anything up here.
Labels:
RubyOnRails
Subscribe to:
Posts (Atom)
