Hi Roland,

I managed to save a copy of the forum. See below quote from LarryK:

" larryk
Posted: Fri Aug 17, 2007 2:59 pm        Reply with quote
Joined: 08 May 2007 Posts: 70 Location: New York, NY
Hi Everybody,

As part of my performance tuning of my Hobo app, I discovered a number
of issues. See my posts over in the Hobo Dev forum if you wish.

Along the way, I realized that I was implementing my viewable_by?
methods in a slow way that was causing an n^2 array scanning problem.
I'm documenting my work here to help others...If this post's content
seems obvious to you then, "never mind."

viewable_by? is hot
Viewable_by? is very hot code, called a zillion times upon the display
of any SHOW action. At a minimum, it is called once per field that
you're displaying. The current 0.5.3 version of Hobo calls viewable_by
five times for each field. (Tom has agreed that this is an issue. I
agree with his plan of working on this type of performance issue later
on in the Hobo march to 1.0 status.)

In the meantime, and even later, it is very important for us Hobo
users to create fast viewable_by code.

Per-model access control
If your permission scheme is that some people have access to all
fields in a model and others have no access, then viewable_by is
straight-forward:
Code:
def viewable_by?(viewer, field)
  # only people with "team" role have view access...
  !viewer.guest? && viewer.role == "team"
end
While db data is looked at, viewer (a user object) is already loaded.
If the permission decision depends on a table or tables that are
associated with the user table then you better make sure that they are
preloaded. Or use memcache or both. Remember that this method is
called for each field, not for each screen/controller action

Per-field access control
For a given model, if Bob can view some fields and Susie can view
others or Susie can see all of Bob's fields plus others, then you're
using per-field access control. This type of access control is very
powerful for apps and Hobo's built in support for it is a major major
win.

But you need to be careful not to cause an n^2 array scanning:
Code:
Wrong way:
def viewable_by?(viewer, field)
  # People with "team" role  see many fields
  # People with "manager" role see private fields
  # Guests see a couple of fields
  guest_fields = [:field_a, :field_b, :field_c]
  team_fields = [:field_a, :field_b, :field_c, :field_d, :field_e]
  manager_fields =
[:field_a, :field_b, :field_c, :field_d, :field_e, :field_f, :field_g]

  (field.in?(guest_fields) ||
     (field.in?(team_fields) && !viewer.guest? && viewer.role ==
"team") ||
       (field.in?(manager_fields) && !viewer.guest? && viewer.role ==
"manager")
end

# It's the wrong way because you're doing array scans using the "in?"
operator. (in? is a VERY handy Hobo extension to include? See /hobo/
lib/extensions.rb)
# It's an n^2 problem because the method is called for every field.

# Better way:
# To eliminate array scans, everything must be keyed off of the data
we have, the field name...
def viewable_by?(viewer, field)
  # People with "team" role  see many fields
  # People with "manager" role see private fields
  # Guests see a couple of fields
  # guest_fields = [:field_a, :field_b, :field_c]
  # team_fields = [:field_a, :field_b, :field_c, :field_d, :field_e]
  # manager_fields =
[:field_a, :field_b, :field_c, :field_d, :field_e, :field_f, :field_g]

  field_permissions = {
    :field_a => :always_viewable,
    :field_b => :always_viewable,
    :field_c => :always_viewable,
    :field_d => :viewable_by_team_or_manager,
    :field_e => :viewable_by_team_or_manager,
    :field_f => :viewable_by_manager,
    :field_g => :viewable_by_manager}

# NOTE: you must define a method for EVERY field.
# Using a rescue clause or checking for "other"
# fields are probably slower patterns.
# (Use Ruby prof to find out for sure.)

  send(field_permissions[field], viewer)
end
def always_viewable(viewer)
  true
end
def viewable_by_team_or_manager(viewer)
   !viewer.guest? && (viewer.role.in?(["team", "manager"])
end
def viewable_by_manager(viewer)
  !viewer.guest? && viewer.role == "manager"
end

# Remember that db pre-loading and caching are critical if
# your permission decision requires any models other than User.

# Next make it go even faster!
# 1) Add a method to app/models/guest.rb :
def role
  nil
end

# 2) Eliminate the extra calls to viewer.guest?
def viewable_by_team_or_manager(viewer)
   viewer.role.in?(["team", "manager"])
end
def viewable_by_manager(viewer)
  viewer.role == "manager"
end

# Don't use respond_to?  !! It's dog-slow. Either of the above is far
faster than respond_to

# Next make it go even faster!
# If you're checking against a long list of permissions, then replace
any permissions array scans with hash key lookups. Or re-factor so a
role has many permissions. Then you only need to see if the person has
the one permission or not.
Updateable_by?
Updateable_by? has a worse issue of n^2 lookups because Hobo 0.5.3
doesn't give you the field name. Instead, during SHOW actions, one of
the fields in the new object passed to Updateable_by? is different.
You need to scan to find out which it is, then determine if the user
has the permission to change it.

To eliminate the n^2 attribute scan problem, you can install my patch.
(Posted in the Hobo dev forum.) But note that the patch requires you
to change all of your Updateable_by? methods. Not a big deal for the
performance gain but be forewarned.

Note that caching doesn't help with the first display of a form since
the cache isn't yet loaded. And the caching is on a per-user basis
since all permissions are per-user.

Bottom line: computers are fast, but n^2 problems are very powerful at
slowing things down.

Regards,

Larry
ps. Was this of use to you? Comments appreciated..."

***********End quote....

On Dec 13, 6:52 am, roland oth <[email protected]> wrote:
> Hi Sean
>
> Unfortunately the link to the forum is broken.
>
> The way it seems from your code is that you use the logged_in method
> to determine whether a field should be viewable or not.
>
> I am trying to do this on a record level, so this is something I  
> expect to happen
> in the controller or ideally from within the permission system. But I  
> would be happy
> to know where to place the controller code
>
> roland
>
> Am 12.12.2008 um 18:25 schrieb Hobo_Fan:
>
>
>
> > Hi rolando ,
>
> > I found a good write out written by larryk on hobocentral in regards
> > of viewable_by? and updateable_by? methods. You might want to take a
> > look.
>
> >http://hobocentral.net/forum/viewtopic.php?p=5108&sid=eca9b87ea696a74...
>
> > Unfortunately, I was not able to understand it and do not how to
> > implement it with my app.
>
> > But I was able to allow user who logged in to see certain fields, for
> > example:
>
> > <aside:>
> >    <h2>Licensing Info</h2>
> >    <% for license in @product.licenses %>
> >    <ul>
> >        <li>
> >        License Key: <%= license.key if logged_in? %>
> >        <br/>Client: <%= h(license.client.last_name) if !
> > license.client.nil? %>
> >        </li>
> >    </ul>
> >    <% end %>
> >    <br/>
> >    <p><a to="&License" action="new">Add New License</a></p>
> > </aside:>
>
> > Let me know if you figure out how to use the viewable_by method.
>
> > Thanks,
> > Sean
>
> > On Dec 12, 2:15 am, solars <[email protected]> wrote:
> >> On Fri, Dec 12, 2008 at 10:09:14AM +0000, James Garlick wrote:
>
> >>> I think you'd be better off constraining the collection in the
> >>> controller based on the status of the user rather than using the
> >>> permission system. Otherwise you'll be retrieving a whole load of
> >>> unnecessary data from the database which isn't very efficient.
>
> >> Yeah that would be smarter anyway, I guess that's the reason Tom
> >> only included the can_view? check in the (default) collection tag.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Hobo 
Users" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/hobousers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to