Populating a database after "Sign Up" - ruby-on-rails

I would like to populate various tables in my database after a new customer signs up to use our web application. I'm wondering what the best way to do this is, the main requirement being that it should not require any intervention by me. Most of the data is static (but can be changed by the customer later (like preferences for example)), but will obviously need the customer's ID as a way of linking the created records to this customer.
I considered putting a few
Object.create(:customer_id => #customer.id, :yummy => "maybe", :etc => true)
statements in the controller that handles signups, but that annoying little alarm bell that tells me there's a better way is going off again!
Thanks in advance for any suggestions!
Gav

The problem with khelll's solution is that if you create a new Account using from outside of the register action (for example, in an admin module), it's database won't be populated.
So I'd prefer something like the following :
class Account < ActiveModel::Base
def after_create
populate
end
private
def populate
# Your logic
end
end
The after_create callback will be called after any account creation.
That's more MVC and DRY compliant ;)

In your controller
class AccountController < ApplicationController
after_filter :populate_db :only=>[:register]
def populate_db
# whatever stuff
Account.populate
end
end
And put the logic inside your model:
class Account < ActiveModel::Base
def populate
# your logic
end
end

two ways
a trigger function in your database that does this
a callback in your user model on creation

Can you not just set defaults in your database?

I'm using a YAML file to populate more than ten tables in database during signup. This approach has some positive side-effects for me:
I can easily change the contents of the seed data for new account
It is very easy to add localized versions of initial content, i can let YAML files translated easily through 99translations.com
I'm planning to add data import feature later on where new clients can import their data during or right after signup so they can just take the import file template and customize it for their own needs.
There have been some downsides too:
Inevitably, when data schema changes, sometimes i have to modify all localized versions of these files.
It is one piece of ugly code i have that maps the foreign keys between all the new records. Maybe i can use some schema descriptors here...
As i get around 300 new signups in a day, it's too early for me to worry about performance.

#Omar Qureshi
You shouldn't use trigger function in your database - there are many features that can be done by them, but ActiveRecord is database-agnostic and it has callbacks that handle such things that triggers can. Using something that links you to a specific DB system is wrong.
#dmathieu's answer seems to be the best. And you should consider using Factory girl - it's a great tool for populating the DB.

Related

Where to place logic for parsing data before saving to database in Rails?

I'm taking open data about all the countries in the world and need to be able to parse through that JSON file in order to pull out relevant content for each country and store that into my database.
I'm a little unclear about Where I should be writing the logic to parse through that data and save each relevant part for each country in my database - in the model or controller? I almost put it in the root index of my controller to begin parsing as soon as the user visits, but I just have a feeling that this goes against the rails convention of fat models, skinny controllers.
Any and all help would be greatly appreciated. Thank you!
You're absolutely right that placing all the logic in your controller will violate skinny controllers.
One way to do it is by methods on your models which parse data from the api:
class Cat < ActiveRecord::Base
def self.create_from_kittypix_api(data, &block)
model = self.new(attrs_from_some_api(data))
yield(model, data) if block_given?
model
end
def self.attrs_from_kittypix_api(data)
{
foo: data[:bar]
}
end
end
The drawback here is that your models end up knowing a bit too much about the external API, and creating several models at once can lead to confusion about responsibility.
A newer approach is using services, which are plain old ruby objects which take an input and accompish a given task.
class KittypixImportService
def self.call(kitty_pix_id)
client = KittyPixClient.new
result = client.get_kitty(id: kitty_pix_id)
Cat.new(
foo: result['bar']
)
end
end
For myself, my Rails work has evolved into more of an MVCS type approach. I keep my controllers as skinny and light as I can (to me, they're nothing more than the "traffic cops" of the framework).
I try to also keep my models similarly light where added methods either represent the model and its data or its behavior. For example, in a User model with first and last name attributes/fields, I typically add a method called name that concatenates first and last.
If the program requirements fall outside of those or similar use-cases, I tend toward using Services that I store in /app/services.
The classic, to-do-esque example, at least for me, is searching a model, which I rarely place in a model. Instead, I receive the search request in the controller (of course) and then send the params and any other relevant details to a service class and let it do the work.
Here's a really basic search example ... really basic :)!
# app/controllers/some_cleverly_named_controller.rb
def index
#search_results = ClientSearch.call(params)
# ...
end
# app/services/client_search.rb
class ClientSearch
def self.call(params)
ModelName.where(related_id: params[:related_model_id], active: params[:status]).order("id #{params[:sort_order]}, model_name.last_name #{params[:sort_order]}").includes(:related_model)
end
end
I have a super-simple, but boringly named gem that generates a service and an rspec template for it, just in case you try the Services approach.
If interest, do a web search on "Rails Services" and there's a wealth of data!
Good luck!

accessing current_user in model; has to be a better way for logging and auth

I know the dogma says to not access current_user in a model but I don't fully agree with it. For example, I want to write a set of logging functions when an action happens via a rails callback. Or simply writing who wrote a change when an object can have multiple people write to it (not like a message which has a single owner). In many ways, I see current_user more as config for an application - in other words make this app respond to this user. I would rather have my logging via the model DSL rather than in the action where it seems REALLY out of place. What am I missing?
This idea seems rather inelegant Access current_user in model
as does this: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
thx
edit #1
So my question isn't if there are gems that can do auditing / logging. I currently use paper_trail (although moving away from it because I can do same functionality in approx 10 lines of ruby code); it is more about whether current_user should never be accessed in the model - I essentially want to REDUCE my controller code and push down logic to models where it should be. Part of this might be due to the history of ActiveRecord which is essentially a wrapper around database tables for which RoR has added a lot of functionality over the years.
You've given several examples that you'd like to accomplish, I'll go through the solution to each one separately:
I want to write a set of logging functions when an action happens via
a rails callback
Depending on how you want to log (DB vs writing to the logger). If you want to log to the DB, you should have a separate logging model which is given the appropriate information from the controller, or simply with a belongs_to :user type setup. If you want to write to the logger, you should create a method in your application controller which you can call from your create and update methods (or whatever other actions you wanted to have a callback on.)
Or simply writing who wrote a change when an object can have multiple people write to it
class Foo < ActiveRecord::Base
belongs_to :user, as: :edited_by
end
class FooController < ApplicationController
def update
#foo = Foo.find(params[:id])
#foo.attributes = params[:foo]
#foo.edited_by = current_user
end
end
I think you're misunderstanding what the model in Rails does. Its scope is the database. The reason it can't access current_user, is because the current user is not stored in the database, it is a session variable. This has absolutely nothing to do with the model, as this is something that can not exist without a browser.
ActiveRecord::Base is not a class that is designed to work with the browser, it is something that works with the database and only the database. You are using the browser as an interface to that model, but that layer is what needs to access browser specific things such as session variables, as your model is extending a class that is literally incapable of doing so.
This is not a dogma or style choice. This is a fact of the limitations of the class your model is extending from. That means your options basically boil down to extending from something else, handling it in your controller layer, or passing it to the model from your controller layer. ActiveRecord will not do what you want in this case.
The two links you show (each showing imho the same approach) is very similar to a approach I still use. I store the current_user somewhere (indeed thread-context is the safest), and in an observer I can then create a kind of audit-log of all changes to the watched models, and still log the user.
This is imho a really clean approach.
An alternative method, which is more explicit, less clean but more MVC, is that you let the controller create the audit-log, effectively logging the actions of the users, and less the effects on different models. This might also be useful, and in one website we did both. In a controller you know the current-user, and you know the action, but it is more verbose.
I believe your concerns are that somehow this proposed solution is not good enough, or not MVC enough, or ... what?
Another related question: How to create a full Audit log in Rails for every table?
Also check out the audited gem, which solves this problem as well very cleanly.
Hope this helps.

Ruby on Rails: How should I seed my database with account specific data?

I am creating a SAAS app using Rails 3. When a user creates a new account, the database needs to populate with a lot of data. Some of the data will be specific to the newly created account. I don't necessarily want to do this all with Models within the controller "sign up" action. What would be the best way to do this?
From the sounds of things you should be using a callback within your User model. Most likely: a before_create or after_create (depending on your exact needs). You can then have the user model handle the creation of the account specific data, rather than your controller and thus adhere to the ideals of 'fat-model, skinny-controller'.
class User < ActiveRecord::Base
after_create :setup_account_data
private
def setup_account_data
# create other data as required
end
end
Pretty simple really, after the user model is created - the setup_account_data method will be called. A list of the other available callbacks is here.
A couple of approaches come to mind.
simple ruby. This is similar to what is done when you run rake db:seed -- execution of a ruby script.
fixtures. If you dump a database to fixtures [http://snippets.dzone.com/posts/show/4468], you can modify the fixtures so that the data that needs to be customized is done in erb blocks. This is the same technique that is used often in test fixtures.

Should I prevent record editing using a filter in my controller or a callback in my model?

To preserve data integrity, I need to prevent some models from being modified after certain events. For example, a product shouldn't be allowed to be written off after it has been sold.
I've always implemented this in the controller, like so (pseudo-ish code):
def ProductsController < ApplicationController
before_filter require_product_not_sold, :only => [ :write_off ]
private
def require_product_not_sold
if #product.sold?
redirect_to #product, :error => "You can't write off a product that has been sold"
end
end
end
It just struck me that I could also do this in the model. Something like this:
def Product < ActiveRecord::Base
before_update :require_product_not_sold
private
def require_product_not_sold
if self.written_off_changed?
# Add an error, fail validation etc. Prevent the model from saving
end
end
end
Also consider that there may be several different events that require that a product has not been sold to take place.
I like the controller approach - you can set meaningful flash messages rather than adding validation errors. But it feels like this code should be in the model (eg if I wanted to use the model outside of my Rails app).
Am I doing it wrong?
What are the advantages of handling this in my model?
What are the disadvantages of handling this in my model?
If I handle it in the model, should I really be using validates rather than a callback? What's the cleanest way to handle it?
Thanks for your ideas :)
It seems like you already have this one covered, based on your question. Ideally a model should know how to guard its state, as data objects are typically designed with portability in mind (even when they'll never be used that way).
But in this case you want to prevent an action before the user even has access to the model. Using a model validation in this case means you're too late and the user has already gone farther than he should by having access to and attempting to write off a product which should never have been accessible based on its sold status.
So I guess the ideal answer is "both." The model "should" know how to protect itself, as a backup and in case it's ever used externally.
However in the real world we have different priorities and constraints, so I'd suggest implementing the change you listed if feasible, or saving it for the next project if not.
As far as using a model callback versus a validation, I think that's a trickier question but I'll go with a validation because you'd likely want to present a message to the user and validation is built for exactly that use (I'd consider this more of a friendly and expected user error than a hostile or security-related one which you might handle differently).
Is that along the lines of what you've been considering?

Triggers/Callbacks in Ruby on Rails

We are creating a system in Ruby on Rails and we want to be able to offer our users a bit of control about notifications and actions that can take place when some pre-defined trigger occurs. In addition, we plan on iterating through imported data and allowing our users to configure some actions and triggers based on that data.
Let me give you a few examples to better clarify:
Trigger - Action
------------------------------------------------------------------------
New Ticket is Created - User receives an e-mail
New Ticket Parsed for Keyword 'evil' - Ticket gets auto-assigned to a
particular group
User Missed 3 Meetings - A ticket is automatically created
Ideally, we would like some of the triggers to be configurable. For instance, the last example would possibly let you configure how many meetings were missed before the action took place.
I was wondering what patterns might help me in doing this event/callback situation in Ruby on Rails. Also, the triggers and actions may be configurable, but they will be predefined; so, should they be hard coded or stored in the database?
Any thoughts would be greatly appreciated. Thanks!
Update 1: After looking at it, I noticed that the badges system on SO is somewhat similar, based on these criteria, I want to do this action. It's slightly different, but I want to be able to easily add new criteria and actions and present them to the users. Any thoughts relating to this?
I think that what you are looking for are the Observers.
In your examples the Observers could handle the first and the third example (but not the second one, since an Observer only observes the object, not interact with it, even though it is technically possible).
Some code to show how I mean:
class TicketObserver < ActiveRecord::Observer
def after_create(ticket)
UserMailer.deliver_new_ticket_notification
end
end
class UserObserver < ActiveRecord::Observer
def after_update(user)
Ticket.new if user.recently_missed_a_meeting and user.missed_meetings > 3
end
end
And then add the observers to environment.rb
config.active_record.observers = :user_observer, :ticket_observer
Of course you will have to fill in the logic for the missed_meetings, but one detail to mention.
Since the after_update will trigger after every time that the user is being updated, the recently_missed_a_meeting attribute is useful. I usually follow the thinking of restful-authentication and have an instance variable that is being set to true everytime I want to trigger that row. That can be done in a callback or in some custom logic depends on how you track the meetings.
And for the second example, I would put it in a before_update callback, perhaps having the keywords in a lookup table to let users update which words that should trigger the move to a specific group.
You should look at the "callback" methods in Rails
For docs see - Callbacks
Your first rule would be implemented via the after_create method.
If you want them to be configurable, I would suggest using a model / table to store the possible actions and doing a lookup within the callback.
If this is high volume, be sure to consider caching the configuration since it would end up doing a db lookup on each callback.
Maybe something like a state-machine can help. Try AASM gem for RoR.

Resources