I'm looking to see if there is a smart way to do something like the following...
In my app I have projects. I want to prevent a user from adding more than 10 projects. My understand after only having used rails for a few weeks is, that I should make a helper in my model for this, does that sound right?
Also should I do this at the model/helper level or is this something that should be done for all models with some type of setting file?
So the idea is, when the user goes to create a new project, before_create, it checks, if the user has 10+ projects already, is says, sorry not at this time? ALso, interested in how to output the error msg, but 1 step at a time for a newbie.
thanks
Doing this as a validation method is pretty straightforward. In Rails 3 you just declare a method to be run during validation and it has an opportunity to add errors if the situation arises:
class Project
validate :user_can_create_projects
protected
def user_can_create_projects
if (user and user.projects.count >= 10)
errors.add_to_base("You have created too many projects.")
end
end
end
This is not an entirely bullet-proof method as there is a very small chance that someone might be able to create a project between the interval when you check the count and when you actually create the project. That sort of thing has a much greater chance of happening when someone double-clicks a form submit button, for instance, but in practice is relatively rare.
Related
I'm in the process of updating a website I made almost 2 years ago. It was my first real website and I made some mistakes (some more serious that others).
What apparently is one of my biggest is making database calls from the view.
I'm pretty damn sure there is a better way to do this:
Use Case:
Someone fills out a form for a new subject, populating the Subject table, and they have been marked "enrolled", Subject.enrolled = 1
Based on that, I now need to create a record in 5 other tables (such as Baseline)
Downhill from here, here is my method
Determine if the record exist based on subject_id from Subject (sub)
<$ if Baseline.where(subject_id: sub.subject_id).first != nil $>
If it does not exist, create the record, (otherwise display the link)
<%= Baseline.create(subject_id: sub.subject_id) %>
This all happens in the view, and creates a front-end table with links to each record in the process. So I'm creating records based on for-loop logic...
Question:
So I'm looking for direction. I don't want to guess how to do this - I'm pretty sure the model/controller should do this - I want to learn how to do it correctly. How do I create records automatically, based on a value in a table?
Thank you for your time.
Not quite sure how your domain and code looks like, but to answer this question: 'How do I create records automatically, based on a value in a table?', it seems that you could use ActiveRecord callbacks, like this:
class Subject < ActiveRecord::Base
after_commit :create_baseline_if_enrolled, on: [:create, :update]
private
def create_baseline_if_enrolled
return unless enrolled?
# enrolled? == true, you may create these models here
end
end
To answer your question:
It depends :) This is just one possible solution. Another one would be to put such a custom logic in your SubjectsController and call it directly from the #create, #update methods. Both approaches have pros and cons. For example, abusing callbacks (anywhere) makes code less readable and harder to debug. On the other hand, putting such logic in controllers puts a burden on you that you have to remember about calling it if you happen to be editing subjects in other places (but is more explicit). Whichever way you choose, remember not to make your classes too fat, for example try to use service object pattern to separate such custom logic as soon as you feel like it is getting out of hand. :) And don't forget about tests - when things go wrong, tests make refactoring easier.
I have a little problem. I found it really hard to create a title for this, I hope I can explain it better:
One of our clients demanded that in every instance of "item_number", when it's from the Product model, the app will have to show it with 5 leading zeroes.
The "item_number" proprety in that model is in fact an integer on the database.
Our first approach would be just change in the model the value:
def item_number
item_number.to_s.rjust(5, '0')
end
Of course we get a:
SystemStackError at /
stack level too deep
And I'm pretty sure this will mess up when creating/updating records, but it doesn't matter, it doesn't work anyways.
The solution would be simple, just create:
def item_number_with_leading_zeroes
item_number.to_s.rjust(5, '0')
end
And replace item_number with it on our views.
But, we have over 5.000 usages of this, and some of them were not from this model (and we only need to replace the item_number on this model) and some of them are inside Iterators that we coded not using the parent object name (so I may not know if it's from this model or not), others are from custom form inputs, we can't just "replace all".
I would have to manually check every single instance of "item_number" and see if it's needed to change it to that new method or not. And I can still make mistakes and impact a lot of stuff.
I only need to change it on the views, I thought about a way to get it working with the helpers but that would still mean I would have to change it case-by-case.
There is another option to fix it, updating all records on the database to string with the zeroes and making a before_save option from now on, but we would like a rails solution first, before going that way (leaving the DB as is right now).
I'm out of ideas, I don't know if there is a solution, I thought about asking here first hoping someone can share some thought.
Is there any way I can do this without having to manually check the whole application?
Ruby 2, Rails 4.0
Thank you!
Try using read_attribute to avoid the recursion:
class Product < ActiveRecord::Base
...
def item_number
read_attribute(:item_number).to_s.rjust(5, '0')
end
...
end
Here's a programming challenge I'm facing. I'm new at this...
I'd like to do this the Rails Way, with elegant code, and learn something in the process.
In my app, Players track an Action (25 minutes of focus starting on the half-hour). The action#new form loads at the beginning, but is not saved until the end of the 25 minutes. The last 2 minutes are a retrospective and include some required "how did it go" questions.
I now want to add an Insights table. I'm picturing a separate, small "new Insight" form that blends into the larger "new Action" form. I want to save the Insight at whatever time it comes to me.
I also want the Insight to be associated with that particular Action... even though the Action hasn't been created yet, and I don't know what id it will have (somebody else may record their Action before I do). If an Insight was created but the Action fails, I'm not yet sure what to do... I could allow Action-less Insights.
How do I implement this? A temp variable? Where would that go?
Again, I'm brand new at this, and I appreciate your support.
Mihai
TheActionGame.com
I'm note sure about what your action represent, but even if you have some time-based principle, nothing prevent your for saving the Action before (for a browser-based application, 25 minutes is a lot of time during which a lot of bad things can happen, like a refresh or a connection failure).
My advice would be to create (save) the Action immediately, thus allowing your Insight to be linked to it. I would add a "status" column to Action, with something like "new" (the base status), "confirmed" and other if needed.
You can then work only on the "confirmed" Actions, and eventually delete the "new" one on a regular basis (like all that are older than 30 minutes, with your thresold of 25).
Hope this make sense (more design than Rails, anyway).
You could add a column to your actions "insight_ids" as an array and implement it as a has_and_belongs_to_many.
Then when you have an insight, record the insight's id in the array
I am looking for a Ruby/Rails tool that will help me accomplish the following:
I would like to store the following string, and ones similar to it, in my database. When an object is created, updated, deleted, etc., I want to run through all the strings, check to see if the CRUD event matches the conditions of the string, and if so, run the actions specified.
When a new ticket is created and it's category=6 then notify user 1234 via email
I am planning to create an interface that builds these strings, so it doesn't need to be a human-readable string. If a JSONish structure is better, or a tool has an existing language, that would be fantastic. I'm kinda thinking something along the lines of:
{
object_types: ['ticket'],
events: ['created', 'updated'],
conditions:'ticket.category=6',
actions: 'notify user',
parameters: {
user:1234,
type:'email'
}
}
So basically, I need the following:
Monitor CRUD events - It would be nice if the tool had a way to do this, but Ican use Rails' ModelObservers here if the tool doesn't natively provide it
Find all matching "rules" - This is my major unknown...
Execute the requested method/parameters - Ideally, this would be defined in my Ruby code as classes/methods
Are there any existing tools that I should investigate?
Edit:
Thanks for the responses so far guys! I really appreciate you pointing me down the right paths.
The use case here is that we have many different clients, with many different business rules. For the rules that apply to all clients, I can easily create those in code (using something like Ruleby), but for all of the client-specific ones, I'd like to store them in the database. Ideally, the rule could be written once, stored either in the code, or in the DB, and then run (using something Resque for performance).
At this point, it looks like I'm going to have to roll my own, so any thoughts as to the best way to do that, or any tools I should investigate, would be greatly appreciated.
Thanks again!
I don't think it would be a major thing to write something yourself to do this, I don't know of any gems which would do this (but it would be good if someone wrote one!)
I would tackle the project in the following way, the way I am thinking is that you don't want to do the rule matching at the point the user saves as it may take a while and could interrupt the user experience and/or slow up the server, so...
Use observers to store a record each time a CRUD event happens, or to make things simpler use the Acts as Audited gem which does this for you.
1.5. Use a rake task, running from your crontab to run through the latest changes, perhaps every minute, or you could use Resque which does a good job of handling lots of jobs
Create a set of tables which define the possible rules a user could select from, perhaps something like
Table: Rule
Name
ForEvent (eg. CRUD)
TableInQuestion
FieldOneName
FieldOneCondition etc.
MethodToExecute
You can use a bit of metaprogramming to execute your method and since your method knows your table name and record id then this can be picked up.
Additional Notes
The best way to get going with this is to start simple then work upwards. To get the simple version working first I'd do the following ...
Install acts as audited
Add an additional field to the created audit table, :when_processed
Create yourself a module in your /lib folder called something like processrules which roughly does this
3.1 Grabs all unprocessed audit entries
3.2 Marks them as processed (perhaps make another small audit table at this point to record events happening)
Now create a rules table which simply has a name and condition statement, perhaps add a few sample ones to get going
Name: First | Rule Statement: 'SELECT 1 WHERE table.value = something'
Adapt your new processrules method to execute that sql for each changed entry (perhaps you want to restrict it to just the tables you are working with)
If the rule matched, add it to your log file.
From here you can extrapolate out the additional functionality you need and perhaps ask another question about the metaprogramaming side of dynamically calling methods as this question is quite broad, am more than happy to help further.
I tend to think the best way to go about task processing is to setup the process nicely first so it will work with any server load and situation then plug in the custom bits.
You could make this abstract enough so that you can specify arbitrary conditions and rules, but then you'd be developing a framework/engine as opposed to solving the specific problems of your app.
There's a good chance that using ActiveRecord::Observer will solve your needs, since you can hardcode all the different types of conditions you expect, and then only put the unknowns in the database. For example, say you know that you'll have people watching categories, then create an association like category_watchers, and use the following Observer:
class TicketObserver < ActiveRecord::Observer
# observe :ticket # not needed here, since it's inferred by the class name
def after_create(ticket)
ticket.category.watchers.each{ |user| notify_user(ticket, user) }
end
# def after_update ... (similar)
private
def notify_user(ticket, user)
# lookup the user's stored email preferences
# send an email if appropriate
end
end
If you want to store the email preference along with the fact that the user is watching the category, then use a join model with a flag indicating that.
If you then want to abstract it a step further, I'd suggest using something like treetop to generate the observers themselves, but I'm not convinced that this adds more value than abstracting similar observers in code.
There's a Ruby & Rules Engines SO post that might have some info that you might find useful. There's another Ruby-based rules engine that you may want to explore that as well - Ruleby.
Hope that this helps you start your investigation.
This might be a stupid questions but I wanted to know what happens if two users edit some data at once and then both click submit at the same time, I assumed Rails handled requests one after the other and so someone would get an error message but is this correct?
Thanks
Once one person has edited data I dont want it to be accessible or editable anymore, which is handled by validations
Ive got this validation in my model as well
def account_active
if self.active == true
return true
else
return false
end
end
Where active is a Boolean set within the controller if the validations pass
As has been mentioned in other answers, the latest write wins.
You might not think this is a problem but as there's no pessimistic lock preventing two users from having the same edit form open at once, both users may think the change they're making will be saved.
There is a way around this by using a version number or timestamp on your models that the system can use to tell "the user was editing version 1, but now there's version 2" and prevent the second user from overriding the first user's write.
Ryan Bates' awesome Railscasts series has covered the basics on how to set this up in Railscast 59: Optimistic Locking.
Your web server daemon would handle the requests one after the other; whichever request gets handled last becomes the newest update. Nobody would receive an error message unless you write some logic to handle such cases.
As with all race conditions involving blind writes, last one wins unless you take steps to change that.
Your original question was answered, but I'll add this:
For the validation, you can simply do the following
def account_active
self.active?
end
Ruby implicitly returns the last line of the method.