Enhancing Streamlined Enumerations
Recently, I’ve been looking at the Streamlined framework. For those of you who don’t know, Streamlined is an Ajaxified Scaffold currently under development. The edge version shows promise and is stable enough for my personal use as an administration tool.
One area which is particularly interesting is the way that they handle enumerations and the fact that they are called late in the process rather than being instantiated once and then used. This may appear as an inefficiency at first glance, but in tracing through the call progress, I realized that you could make them more dynamic and allow for dynamic changes to the enumeration on a per item basis.
This means that if you have an exclusive list, you can restrict the choices to only those items that haven’t yet been assigned to other rows in the database.
For example, in one of my projects, you can assign a unique number to each row, and my desire was to restrict the view so that only the numbers that are available can be chosen.
So if you have possible numbers of
[1,2,3,4,5,6,7,8]
Assign 1 to the first row and for new items, [2,3,4,5,6,7,8] should be available, but [1,2,3,4,5,6,7,8] would be available for editing the first row.
Assign 5 to the second row and for new items, [2,3,4,6,7,8] should be available with [1,2,3,4,6,7,8] available for editing the first row and [2,3,4,5,6,7,8] available for editing the second row.
Coding this for the model is fairly straightforward:
class DynamicTest < ActiveRecord::Base
def available_nodes
node_list=[1,2,3,4,5,6,7,8]
nodes=DynamicTest.find(:all)
nodes.each do |n|
node_list-=[n.number] unless n.number==number
end
node_list
end
end
The unfortunate thing is that Streamlined doesn’t support this call, you can perform a call to DynamicTest.available_nodes, but that wouldn’t let you know what the current item is and you wouldn’t be able to see it in the list or edit views. Not very useful. What is needed is a way to call this directly from the row rendering code when you have the item in scope.
Since this is new functionality for Streamlined, the guys who maintain the codebase may adopt it, but for those of you who want to monkeypatch your own version or just see my take on it, you can download this sample project.
The monkeypatch (in app/streamlined/dynamic_tests_ui.rb) overrides four of the streamlined functions and adds two more for handling dynamic enumerations. This means that in addition to the original
Streamlined.ui_for(DynamicTest) do
user_columns :number, {:enumeration => Numbers::TYPES}
end
class Numbers
TYPES = [1,2,3,4,5,6,7,8]
end
and its Hash and 2d array counterparts, you can now have:
Streamlined.ui_for(DynamicTest) do
user_columns :number, {:enumeration => {:action=>:available_nodes}}
end
which will perform a late call to the DynamicTest#available_nodes scoped for the current row.
For those of you who just want to look at the code without downloading a full rails project, the relevant monkeypatched pieces are:
#Note: There is a bug in _enumeration.html that prevents non-Fixednum numeric
#indices. This should be updated in the template version
#<% value = item.send(relationship.name) -%>
#<% key_value_pair = relationship.enumeration_key_for(value) -%>
#<%= key_value_pair ? key_value_pair.first : relationship.unassigned_value %>
module Streamlined::Controller::EnumerationMethods
def dynamic_enumeration
dynamic_enumeration_method=nil
@enumeration_name=params[:enumeration]
rel_type=model_ui.scalars[@enumeration_name.to_sym]
rel_type.enumeration.each { |k,v|
dynamic_enumeration_method=v if k==:action
}
dynamic_enumeration_method.nil? ? rel_type.enumeration : instance.send(dynamic_enumeration_method).to_2d_array
end
# Shows the enumeration’s configured +Edit+ view, as defined in streamlined_ui
# and Streamlined::Column.
def edit_enumeration
self.instance = model.find(params[:id])
@enumeration_name = params[:enumeration]
rel_type = model_ui.scalars[@enumeration_name.to_sym]
@all_items=dynamic_enumeration
@selected_item = instance.send(@enumeration_name)
render(:partial => rel_type.edit_view.partial, :locals => {:item => instance, :relationship => rel_type})
end
# Show’s the enumeration’s configured +Show+ view,
# as defined in streamlined_ui and Streamlined::Column.
def show_enumeration
self.instance = model.find(params[:id])
rel_type = model_ui.scalars[params[:enumeration].to_sym]
rel_type.enumeration=dynamic_enumeration
render(:partial => rel_type.show_view.partial, :locals => {:item => instance, :relationship => rel_type})
end
end
class Streamlined::Column::ActiveRecord < Streamlined::Column::Base
def dynamic_enumeration(item)
dynamic_enumeration_method=nil
@enumeration.each { |k,v|
dynamic_enumeration_method=v if k==:action
}
dynamic_enumeration_method.nil? ? @enumeration : item.send(dynamic_enumeration_method)
end
def render_td_show(view, item)
if enumeration
content = item.send(self.name)
@enumeration=dynamic_enumeration(item)
key_value_pair = enumeration_key_for(content) # call wraps enumeration to 2d array, so check unnecessary
content = key_value_pair.first if key_value_pair
content = content && !content.blank? ? content : self.unassigned_value
content = wrap_with_link(content, view, item)
else
render_content(view, item)
end
end
def render_enumeration_select(view, item)
id = relationship_div_id(name, item)
@enumeration=dynamic_enumeration(item)
choices = enumeration #enumeration call wraps to 2d array so extra call is redundant
choices.unshift(unassigned_option) if column_can_be_unassigned?(parent_model, name.to_sym)
args = [model_underscore, name, choices]
args << {} << html_options unless html_options.empty?
view.select(*args)
end
end
Microsoft Surface Parody
Hot on the heels of the Microsoft Surface announcement, and the statement by Tim Berners Lee that various devices and integrated software packages will be talking to each other seemlessly over the internet, and that everything (electronic) will be more tightly integrated in the future (Does anyone want to say Web 4.0 before they are shot down for confusing the internet with the web?), came this spoof voiceover of the advertising that Microsoft has produced to try to show that they are still innovating in hardware.