Notifications or user activity log implementation - ruby-on-rails

The application has certain actions that are recorded for each user and recorded in db table notifications. The table has the following structure:
user_id, notification_type, credit, timestamp
notification_type does not store the entire text of the notification but just stores a short type description.
Later when the user wants to view his notifications I use a helper method from my view to fetch the actual text.
def notification_text(type)
case type_id
when 'flagPositive'
return 'A question you flagged has been marked as correct.'
when 'qAccepted'
return 'A question you added has been accepted.'
when 'qModerated'
return 'You moderated a question.'
when 'flagReport'
return 'You moderated a flag.'
end
end
1) Is this an optimum way to do this?
2) Should I replace the type_description with integer values (say 1 -> flagPositive, 2-> qAccepted) for performance benefits?
3) Are there any best practices around the same that I should be following?

1) This highly depends on your application and requirements. What I can say is that I used this approach sometimes and faced no problems so far.
2) If you see a performance problem with the string lookup, you could do so. A general recommendation is to optimize performance only when really needed.
3) Just google for "Ruby", "Rails", "ActiveRecord" and "Enum". You'll find lots of discussions about different solutions for this kind of problem. There are similar questions on this site, e.g., Enums in Ruby or In Rails, how should I implement a Status field for a Tasks app - integer or enum?

Related

Query Rails Wildcard Enum Type

Trying to figure out how to utilize a "wildcard" feature with rails enums, where I don't want to have a dedicated int/symbol mapping for a number of variations of types.
For example, I have a ticketing system, where the majority of tickets are unanswered or answered, but the user can also set a custom status in special cases.
class Ticket
enum status: { unanswered: 0, answered: 1, *wildcard: 2 }
end
I'd like to be able to query for Ticket.where(status: :wildcard) and return all tickets with a status value of 2. Digging into the rails source code, I think I want to override.
I figure I can skip the assert_valid_value validations by having a before_save callback that converts any non-existent strings to the right status code, but how would I query?

rails achievements

Then it came the moment to implement some sort of achievement in our web application. I had an idea more or less like the hierarcy described in this question How to implement an achievement system in RoR .
The application we are working on is a software as a service intended to be managed externally without software developers. The thing is it should be possible to create new kinds of achievements runtime by the software administrator via the web interface. The hierarcy then becomes a wall.
I have read somewhere it is possible to implement this situation through finite state machines, but at this time i don't have enough informations about that topic.
Edit: specific question
I thought about modeling an achievement class with a list of conditions to be met. This basic class Achievement would have a boolean which recursively checks all the conditions to be valid. The conditions then could be hardcoded classes. The system admin then creates new kind of achievements with combinations of the atomic conditions.
My fear is the growing number of classes for the atomic conditions. I dont want to have 30+ condition classes in the project. Any advise is really appreciated.
Edit: more details about the implementation
From SpyrosP response, it seems a good idea to build the described DSL. In some way achievements then must be saved in the database. Keeping the same example:
comments :less_than => 10
check_comments
comments :more_or_equal => 100
award_hundred_comments_badge
In order to dinamically create achievements there should be a table that stores the condition(s) to be checked:
Achievement
| id | name |
| 1 | "Houndred Comments" |
Condition
| achievement_id | expression |
| 1 | some sort of condition |
I've been interested in the same idea and was reading different stuff some time ago. Probably the best way to do such a thing is by using observers. An observer is like a standard filter(before_filter and the likes), but with some differences, like how the return value is handled and so on.
That said, if your system is really really complex, you may want to use a state machine plugin like https://github.com/pluginaweek/state_machine . However, i feel that this is too much for an Achievements functionality.
If i had to face complex Achiement scenarios, i would probably create a simple DSL that defines the behavior of an achievement. Something like :
for_achievement :hundred_comments do
before_achievement :status => Comment, :lower_than => 100
after_achievement :status => Comment, :more_or_equals => 100
end
You get the idea. This would be a way to fully describe an achievement. Your observers would then be able to use your achievement.rb class scenarios, in order to make out whether an achievement was reached. Think of it the way CanCan works. This could also be a nice way for your administrators to write simple achievement requirements through an even simpler DSL than what i presented in my example above.
Hope that helps a bit, or at least gives you some ideas :)
EDIT : Simpler DSL
A DSL can be really simple and expressive, so that people may even like to write scenarios with. Something like :
comments :less_than => 10
check_comments
comments :more_or_equal => 100
award_hundred_comments_badge
This can easily be formed to be a valid scenario of achieving 100 comments. Let's think of a scenario where the user gets a badge if he has invited exactly 10 people who are women in gender.
invites :less_than => 10, gender :female
check_invites
invites :equals => 10, gender :female
award_women_invitations_badge
Now, i think that this is very simple to write even for admins who have no clue about ruby, if you explain to them basic stuff about the DSL. But if you do not want them to go into that, you can create a form like :
Action Dropdown => [Comment, Invite, Post, ....]
Condition => [Equal, Less Than, More Than, ....]
Condition_Value => (TextBox to write value to)
CheckCondition => [Check Invitation Count, Check Messages Count, ....]

Is this a race condition issue in Rails 3?

Basically I have this User model which has certain attributes say 'health' and another Battle model which records all the fight between Users. Users can fight with one another and some probability will determine who wins. Both will lose health after a fight.
So in the Battle controller, 'CREATE' action I did,
#battle = Battle.attempt current_user.id, opponent.id
In the Battle model,
def self.attempt current_user.id, opponent_id
battle = Battle.new({:user_id => current_user.id, :opponent_id => opponent_id})
# all the math calculation here
...
# Update Health
...
battle.User.health = new_health
battle.User.save
battle.save
return battle
end
Back to the Battle controller, I did ...
new_user_health = current_user.health
to get the new health value after the Battle. However the value I got is the old health value (the health value before the Battle).
Has anyone face this kind of problem before ???
UPDATE
I just add
current_user.reload
before the line
new_user_health = current_user.health
and that works. Problem solved. Thanks!
It appears that you are getting current_user, then updating battle.user and then expecting current_user to automatically have the updated values. This type of thing is possible using Rails' Identity Map but there are some caveats that you'll want to read up on first.
The problem is that even though the two objects are backed by the same data in the database, you have two objects in memory. To refresh the information, you can call current_user.reload.
As a side note, this wouldn't be classified a race condition because you aren't using more than one process to modify/read the data. In this example, you are reading the data, then updating the data on a different object in memory. A race condition could happen if you were using two threads to access the same information at the same time.
Also, you should use battle.user, not battle.User like Wayne mentioned in the comments.

Rails - given an array of Users - how to get a output of just emails?

I have the following:
#users = User.all
User has several fields including email.
What I would like to be able to do is get a list of all the #users emails.
I tried:
#users.email.all but that errors w undefined
Ideas? Thanks
(by popular demand, posting as a real answer)
What I don't like about fl00r's solution is that it instantiates a new User object per record in the DB; which just doesn't scale. It's great for a table with just 10 emails in it, but once you start getting into the thousands you're going to run into problems, mostly with the memory consumption of Ruby.
One can get around this little problem by using connection.select_values on a model, and a little bit of ARel goodness:
User.connection.select_values(User.select("email").to_sql)
This will give you the straight strings of the email addresses from the database. No faffing about with user objects and will scale better than a straight User.select("email") query, but I wouldn't say it's the "best scale". There's probably better ways to do this that I am not aware of yet.
The point is: a String object will use way less memory than a User object and so you can have more of them. It's also a quicker query and doesn't go the long way about it (running the query, then mapping the values). Oh, and map would also take longer too.
If you're using Rails 2.3...
Then you'll have to construct the SQL manually, I'm sorry to say.
User.connection.select_values("SELECT email FROM users")
Just provides another example of the helpers that Rails 3 provides.
I still find the connection.select_values to be a valid way to go about this, but I recently found a default AR method that's built into Rails that will do this for you: pluck.
In your example, all that you would need to do is run:
User.pluck(:email)
The select_values approach can be faster on extremely large datasets, but that's because it doesn't typecast the returned values. E.g., boolean values will be returned how they are stored in the database (as 1's and 0's) and not as true | false.
The pluck method works with ARel, so you can daisy chain things:
User.order('created_at desc').limit(5).pluck(:email)
User.select(:email).map(&:email)
Just use:
User.select("email")
While I visit SO frequently, I only registered today. Unfortunately that means that I don't have enough of a reputation to leave comments on other people's answers.
Piggybacking on Ryan's answer above, you can extend ActiveRecord::Base to create a method that will allow you to use this throughout your code in a cleaner way.
Create a file in config/initializers (e.g., config/initializers/active_record.rb):
class ActiveRecord::Base
def self.selected_to_array
connection.select_values(self.scoped)
end
end
You can then chain this method at the end of your ARel declarations:
User.select('email').selected_to_array
User.select('email').where('id > ?', 5).limit(4).selected_to_array
Use this to get an array of all the e-mails:
#users.collect { |user| user.email }
# => ["test#example.com", "test2#example.com", ...]
Or a shorthand version:
#users.collect(&:email)
You should avoid using User.all.map(&:email) as it will create a lot of ActiveRecord objects which consume large amounts of memory, a good chunk of which will not be collected by Ruby's garbage collector. It's also CPU intensive.
If you simply want to collect only a few attributes from your database without sacrificing performance, high memory usage and cpu cycles, consider using Valium.
https://github.com/ernie/valium
Here's an example for getting all the emails from all the users in your database.
User.all[:email]
Or only for users that subscribed or whatever.
User.where(:subscribed => true)[:email].each do |email|
puts "Do something with #{email}"
end
Using User.all.map(&:email) is considered bad practice for the reasons mentioned above.

RESTful nested conventional routing

I have the model:
User -1---n- Transaction(amount,description, date)
User -1---n- TransactionImport -1---n- TransactonImportField(name,value)
(personal expense tracking app).
What I want to achieve is this:
User opens URL and pastes the CSV with the list of transactions.
User submits it.
System extracts data from CSV into TransactionImport (row) + TransactionImportField (cell).
User can choose which column means what (amount, description, date) from the imported data in TransactionImport(Field).
User click save and the system transfers TransactionImport into the Transaction.
What I can't seem to get right is the fact that step 3 creates multiple records of TransactionImport (and related TransactionImportField).
So doing POST /transaction_imports?csv=abcd is expected to produce one record if we would be RESTful. But the code is supposed to be something like this:
# TransactionImportsController
def create
result = TransactionImports.parse(params[:csv])
flash[:notice] = result.message
redirect_to transaction_imports_path
end
I am probably approaching the task from a wrong angle as I feel that implementation doesn't fit in tp the inherited_resources.
Could you please advise what would be the most conventional way of implementing this?
Thanks,
Dmytrii.
REST/HTTP has no expectation that doing POST will only create one record. That maybe the default rails behaviour, but you should not constrain your design because of that.

Resources