Rails optimistic locking manual lock check: any issues? - ruby-on-rails-3

I have a rails app where a particular form submit for a model is a two step process: on the first submit, the rails controller issues a render of a modal confirmation form, from which either the update action is invoked or the whole thing cancelled.
Rails optimistic locking seems like the answer here for dealing with out of date updates.
But for the user experience, if the Rails controller can compare the user A's model version against the current version without waiting until the update action to determine if the model is already out of date (due to user B updating the model in question concurrently), then another sort of view could be rendered indicating to user A that he needs to examine the updated model.
Are there any issues or gotchas associated with manually checking the :lock_version field in the controller and comparing it to the params[] version? Is there some built-in or "formal" way of doing this or should the controller just do the check explicitly?

I think the main issues with lock_version is that it's a one size fits all (i.e. you can't have different locks for different contexts) and that lock failure throws a stale object exception which is not particularly nice for the user.
Ryan Singer has presented an alternate approach that uses the updated_at timestamp in conjunction with validations to perform the check and then uses rails dirty tracking to show the user what changed. You can see it in the Rails Cast: http://railscasts.com/episodes/59-optimistic-locking-revised
I've been using this technique and am happy with it, although the example given doesn't cover the more complex cases where multiple models are involved in the action, or where you need to ensure model associations haven't changed as well etc.

Related

Prevent duplicated form submission MVC 4

I know this has been asked several times may be, but most (if not all) solutions I've seen were involving a javascript solution or disabling form/submit button, which I'm not preferring.
I have couple of huge forms that may take some time to complete submission which may let the user to re-submit many times during the first submission, which eventually will cause for many records in the database.
Proposed solution:
A proposed solution is to have a generic method that have an array including all submitted forms IDs, each time a user submit a form, it's ID will be added to that array if it's not already existed, after the submit complete the ID will be popped out the array. A drawback can be counted on this solution in case a user opened two or more pages of the same form and submit them synchronously, but that can be worried about later.
My question is how feasible is my proposed solution and how good it can be? and how can I implement that generic method?
I understand that Apache Struts 2 framework has some solution to prevent such thing (Token Interceptor), how to do that in MVC 4?:
Just add a GUID field to the ViewModel the view is bound to (You are using ViewModels right?) and every time the empty form is asked for, generate a new GUID (solves the issue of downloading two or more copies by same user) for it and have it included in the view as a hidden field (string representation of it). Then you can implement the array solution you are talking about in a session variable or even a database based solution in which all processed GUIDs are saved in a table that gets cleared every so often, a text file that contains the list of GUIDs, etc. kind of hard to pick a complete solution based on just what you provide. But the above should provide you with all the requirements you have outlined.

different permissions based on workflow state

I need to set up different permission on an object based on its workflow state. For instance, 'manager group' can edit the object only if state=draft but 'super manager group' can edit it also if state=validated.
It seems that's not possible using ir.model.access and I'm evaluating if it could be done using ir.rule. It seems not...
Is there a official way to get this or do I need to implement this feature (maybe by adding a condition into ir.model.access machinery).
This is not possible by default with ir.model.access, because this permission model is designed to act like simple Unix permission on CRUD operations, and it is statically defined, per-model and per-group.
You may be able to implement something like this using ir.rule, as it implements dynamic per-record access control based on field values. By having a set of rules defined only on the write and unlink operations and based on the state field, you will be able to prevent some groups from modifying records in certain states. By using the technique of an always-true rule [(1,'=',1)] you can then relax a non-global rule for users who have a "super-access" group. See also this answer.
This option will have important caveats however:
Be careful not to make those rules apply for read, as it will make the records completely disappear, and generally wreak havoc in your processes
The interface will not become read-only when the rule is in effect, and if you want to make the fields and buttons read-only you will have to find a way to specify this via attrs in a manner that depends on the user's groups. See also this Launchpad question.
the Save button in the UI will not be disabled
The standard error reporting in case of ir.rule restriction is not very clear, so it will certainly confuse users (note: it's being improved for 7.0)
As you see, using ir.rule filters for this purpose is far from a perfect solution, and you will first need to find appropriate solutions for the above issues.
Ultimately, you might have an easier task of implementing your own logic for this, plugging a new mechanism in the ORM primitive API methods: fields_view_get (for making fields dynamically read-only based on the user groups) and the CRUD methods (for actually restricting the operations)
There is another way instead of hacking web-client.
You can always have 2 views for the same Object .
For manager group.
For super manager group.
In manager group you can use attrs = {'readonly': [('state', '!=', 'draft')]}
or any condition as you needed.
And in the same way in super manager group, you can put his condition for fields.
I have this feature working on a production environment, using just Record Rules: in Project Issues, "basic users" can create and cancel issues, but can't open or close them.
Despite the GUI limitations mentioned by #odony, it works perfectly.
These are the Record Rules used::
There is a special case that needs attention: changing from a read-write State to a read-only State:
In the action's method, if the the State is changed after other write operations, the user will be able to change the State; but if there are some write operations after the State update, the user will not be able to change State.
In my example, the Project Issue method to Open an issue is case_open(). It first changes State and then does additional changes, like settting Open Date, User and Message history. Because of this, basic users can't Open issues. If you want them to be able to do so, case_open() must be overridden so that it changes State after all other write operations are done.
I got a similar requirement...
My requirement was to make a char field(say "test_123") readonly in the sale.order if the user comes under the group "sale user" otherwise editable for the group "Sale Manager".
That is, if the sale order is in draft state then anyone can edit, but it the sale order is confirmed then this field "test_123" is only editable for "Sale Manger"
What I did is I added a functional field (is_group_manager) which returns True if the user comes under the group "sale manager" and the state is not "draft" otherwise false.
Then in the xml view I added the field "test_123" with attribute attrs="{'readonly':[('is_group_manager','=',0)]}"
for example
<field name="is_group_manager" invisible="1"/>
<field name="test_123" attrs="{'readonly':[('is_group_manager','=',0)]}"/>
This will work only in openerp v6.0. Maybe this will be helpful for you. :)

Basic design for rich (multi-step) search in Rails

A core piece of the application I'm working on is allowing the user to explore a complex dataset by progressively add search terms. For example, you might start with a free-text search, then progressively add (or remove) some facetted search terms, move a slider to constrain some dimension of the returned results, etc.
Conceptually, it seems to me that the user is incrementally defining a set of constraints. These constraints are used to search the dataset, and the rendering of the results provides the UI affordances to add further search refinements. So building this in Rails, I'm thinking of having one of the models be the current set of search constraints, and controller actions add to or remove constraint terms from this model.
Assuming this is a sensible approach (which is part of my question!), I'm not sure how to approach this in Rails, since the search is an ephemeral, not persistent, object. I could keep the constraints model in the session store, but it seems rather a complex object to be marshalled into a cookie. On the other hand, I could put store the constraints model in a database, but then I'll have a GC problem as the database fills up with constraint models from previous sessions.
So: how best to build up a complex interaction state in Rails?
Here's some pointers
create a class XxxSearch with accessors for all the search facets: keywords, category, tags, whatever. This class should be ActiveModel compatible, and it's instances are going to be used in conjunction with form_for #xxx_search. This class is not meant for persistence only for temporaryly holding search params and any associated logic. It may even act as a presenter for data: #xxx_search.results, or implement search data validations for each faceting step.
incrementaly resubmit the form via wizard technique, or even ad-hoc data insertion on a large form.
allways submit the search via GET, as such:
the search is bookmarkable
you can chain the params to pagination links easily like: params_for(params[:search].merge(:page => 3))
you need NOT use the session, the data is forwarded via GET params, as such:
can keep using cookie session store
escapes you from a lot of headaches when the last search is persisted and the user expects a new search context (I say this from experience)
I had to solve this problem for several apps so I wrote a small gem with a DSL to describe these searches:
https://github.com/fortytools/forty_facets

Ruby On Rails: Ask for Confirmation When Table Entry Associated With Another Is Destroyed

I would like some assistance with the following problem:
I have a table of groups that is self-associated with itself, so each group is (optionally) linked to another in a hierarchical fashion. I want to write some code that will somehow check before the destruction of a group entry, if it has any children, and ask the user for confirmation, or whether they wish to delete the child groups as well.
I've looked at callbacks, but I don't know how to get the confirmation request to the end user in the view, and then get the response back to the model's callback.
Thanks
(Note: I'm a Rails noob)
Maybe you could probably use, say, a "before_destroy" wrapper in your models somehow:
def before_destroy
raise flag unless okay_to_destroy?
end
But since this decision is pushed to the user before you even destroy any models, it seems to make the most sense to me to just handle this straight forwardly. Present the user with the relevant information (# children, confirmation of parent to delete), let them select a radio button or whatever to destroy children/just parent/cancel, then destroy the appropriate records.

CakePHP: model-based permissions?

Struggling with a decision on how best to handle Client-level authentication with the following model hierarchy:
Client -> Store -> Product (Staff, EquipmentItem, etc.)
...where Client hasMany Stores, Store hasMany Products(hasMany Staff, hasMany EquipmentItem, etc.)
I've set up a HABTM relationship between User and Client, which is straightforward and accessible through the Auth session or a static method on the User model if necessary (see afterFind description below).
Right now, I'm waffling between evaluating the results in each model's afterFind callback, checking for relationship to Client based on the model I'm querying against the Clients that the current User is a member of. i.e. if the current model is Client, check the id; if the current model is a Store, check Store.clientid, and finally if Product, get parent Store from Item.storeid and check Store.clientid accordingly.
However, to keep in line with proper MVC, I return true or false from the afterFind, and then have to check the return from the calling action -- this is ok, but I have no way I can think of to determine if the Model->find (or Model->read, etc.) is returning false because of invalid id in the find or because of Client permissions in the afterFind; it also means I'd have to modify every action as well.
The other method I've been playing with is to evaluate the request in app_controller.beforeFilter and by breaking down the request into controller/action/id, I can then query the appropriate model(s) and eval the fields against the Auth.User.clients array to determine whether User has access to the requested Client. This seems ok, but doesn't leave me any way (afaik) to handle /controller/index -- it seems logical that the index results would reflect Client membership.
Flaws in both include a lengthy list of conditional "rules" I need to break down to determine where the current model/action/id is in the context of the client. All in all, both feel a little brittle and convoluted to me.
Is there a 3rd option I'm not looking at?
This sounds like a job for Cake ACL. It is a bit of a learning curve, but once you figure it out, this method is very powerful and flexible.
Cake's ACLs (Access Control Lists) allow you to match users to controllers down to the CRUD (Create Read Update Delete) level. Why use it?
1) The code is already there for you to use. The AuthComponent already has it built in.
2) It is powerful and integrated to allow you to control permissions every action in your site.
3) You will be able to find help from other cake developers who have already used it.
4) Once you get it setup the first time, it will be much easier and faster to implement full site permissions on any other application.
Here are a few links:
http://bakery.cakephp.org/articles/view/how-to-use-acl-in-1-2-x
http://book.cakephp.org/view/171/Access-Control-Lists
http://blog.jails.fr/cakephp/index.php?post/2007/08/15/AuthComponent-and-ACL
Or you could just google for CakePHP ACL