Looking for a good idea and a good Ruby structure - ruby-on-rails

I am writing a Rake task to populate a database for development purposes to have some data to work with. ( but In test I am using FactoryGirl, so not talking about that. )
So let's say I have two sample Organizations in my demo database, so I have defined them like this:
sample_organizations = [ '37 Signals', 'Fog Creek']
and then a small method to populate the DB like this:
def create_organizations
sample_organization.each { |item|
Organization.first_or_create(
name: item,
time_zone: 'Central'
)
}
end
so the good thing is that two months from now if I wanted to add a third organization I just go to top of my code and hand type another organization name in that array. Method still works and creates it.
Now here starts the question: I have more tables to populate! For example Summaries table which is hanging off of Organization table, so each organization can have many Summaries, so we have an organization_id foreign key in Summary table to fill too.
But still I want to have a method like create_summaries that does something similar to create_organization method above, but this time fills in Summaries table, and the tricky part is to fill in that organization_id field of the table.
How should I approach this?
Let's say Summaries table has these fields: id (auto generate by Rails) , organization_id, name, member_count

Try using Populator gem for tasks like this. It is easy to use, and you can generate complex structures. Screencast.
As you can't use those, use select a random parent object, or specify one by name in a hash.
summaries = [{:name => "foo", :organization => "bar"}]

Related

Rails - How to Perform Fuzzy Search on Multiple Tables using Textacular?

I am using the textacular gem to implement fuzzy search in my Rails application, and I cannot figure out how to perform a search on multiple tables at once. Here's an example query that searches multiple tables WITHOUT textacular:
#results = Owner.includes(:car => :company).where("name LIKE ? OR cars.name LIKE ? OR companies.name LIKE ?", sql_query, sql_query, sql_query).references(:cars, :companies)
As you can see, this query searches the Owner, Car, and Company tables. The farthest I got with textacular is this:
#results = Owner.includes(:car => :company).references(:cars, :companies).fuzzy_search({name: sql_query})
The above query only searches the Owner table, though. I've tried doing the following to get it to search the Companies table as well:
#results = Owner.includes(:car => :company).references(:cars, :companies).fuzzy_search({name: sql_query, "companies.name" => sql_query})
This doesn't work because it tries searching the owners.companies.name field, which of course does not exist. I want it to search the companies.name field in addition to the owners.name field. Does anyone know how to perform a fuzzy search on multiple tables using textacular?
I figured it out! You need to add the joined table name as a nested hash. Let's say I want to search by the name fields of the Owner model AND the Car model:
#results = Owner.includes(:car).references(:cars).fuzzy_search({name: sql_query, cars: { name: sql_query}})
The cars table specified in the fuzzy_search method refers to the name of the table, NOT the name of the association.
I got inspiration from this old Rails commit from 2008, which made me realize that the conditions you can use in any standard Rails query are available in the fuzzy_search method.
Not sure if you're wedded to textacular but from their docs it looks like you can only search by one model.
I've used PgSearch before and and it's pretty robust, you can search across models.

Edit models with complex primary key with Active Admin

I'm having a trouble with opening AA edit-page for a model, which has a lot of associations.
What I had it's like 50 selects, opening at once. And this page turns to be deadly slow.
After reading this ActiveAdmin: How to handle large associations I considered to use select2 instead of usual select, but things get even worse.
That was because most of the time Rails spent in generating views, not in querying database. So with fancy select2 it reasonably spends even more time in views.
With that knowledge in mind, I decided to not have select inputs on that page at all. So I'll edit "main" object on that slow page, but connected with has_and_belongs_to_many objects should be edited separately.
But after that decision I've faced with a trouble: how should I edit tables with a complex primary key: not just id, but :person_id and :organization_id.
AA by default generates urls like that: /admin/person_organizations/:id/edit, but I need something like this: /admin/person_organizations/:person_id/:organization_id/edit
Any ideas?
ActiveAdmin should be able to handle custom primary keys by default. Just be sure that you add the definition to your model like this:
class Person < ActiveRecord::Base
self.primary_key = 'person_id'
end
After a while I've decided that I don't even need to have multiple keys here since Rails generates artificial id field for habtm tables. And as my goal was to edit this table, I've finished with standard ways of doing this.

How inefficient is this templating system?

Users can be one of 6 different 'types' - Each type will have 12 templates total.
Each template has its own model, with a belongs_to relation to the User model (User model has_one of each template, though I'm worried that it'll mean I'll have 72 has one relations on the user model)
On creation of a user I will use a case statement to create the templates depending on which user type they are - The template editor will then simply update the templates current database entry.
It's worth also noting the templates vary in editable fields and dynamic content, which is why I have seperate models for each.
Any recommendations on how I could improve this would be awesome, it's driving me crazy at the moment.
Any solution in which you are writing "has_one ..." 72 times in your model.rb file is obviously wrong.
Basically i think you need to add more tables/classes, to handle the relationship between users, "types" and templates. That's your strategy.
I would make the user "type" (i think "role" is a better word here, and also isn't a reserved word, meaning you will have less problems) into a class. I would also have a join class/table linking roles to templates in a many-to-many relationship.
I would have a class for templates.
I would actually try to go the opposite way with templates: i would try to bring them back into a simple structure as much as possible. Any situation where you are making a whole new class for a variation of something is a sign that you've gone in the wrong direction.
Instead, look at what varies between templates, and abstract THOSE ASPECTS out into their own class. So, if one template has three sections, which are a "foo section", a "bar section" and a "baz section", then make a TemplateSection class, and make a "foo", "bar" and "baz" instance/record. Then have a join table joining Templates to TemplateSections in many-to-many way. The join table can hold any data which is specific to "the particular way section X is used in template Y".
Basically, whenever something varies, as much as possible try to have the variation held IN DATA rather than in code. This will require you to model your application carefully, making new classes/tables to suit.
I see two possible ways, depending on the amount of flexibility needed.
Option 1: build everything through ActiveRecord associations
Create a Template model and 72 model objects, one for each template. Create a FieldType model to represent the different parts a template consists of, such as "title" or "description". Then create a Field join model that represents the actual usage of a field by a template:
class Template
has_many :fields
end
class FieldType
has_many :fields
end
class Field
belongs_to :template
belongs_to :field_type
end
The FieldType model stores all the properties of a field that are identical everywhere - its name, its type (text, numeric, etc). The Field model stores all the usage-related properties that differ between templates. An example:
login_page = Template.create name: 'Login page'
dashboard = Template.create name: 'Dashboard'
title_field = FieldType.create name: 'Title',
kind: 'text' # careful with reserved words such as 'type'
login_page.fields.create field_type: title_field, required: true
dashboard.fields.create field_type: title_field, required: false
We have two pages use the same field called title, but on the login page the field is mandatory, while on the dashboard it is not.
What's nice about this is that it plays well within Rails' abilities; you get to use ActiveRecords finders, validations, form helpers, the works.
Option 2: represent your data schemaless
If you use Postgres as your database, you get powerful data types such as JSON, JSONB and Hstore that allow you to store arbitrary data in a single database column:
class CreateTemplates < ActiveRecord::Migration
def change
create_table(:templates) do |t|
t.hstore :template_data
end
end
Template.create template_data: {
foo: 'foo',
bar: 1
baz: {
quux: 'kittens'
}
}
This provides the most flexibility. The downside is that you will not get the full-service experience of Rails when it comes to validations, form handling and the like, and you will run into some snags, such as booleans becoming strings.

Rails association with tables without models

I'm looking to rewrite my project in Rails. It's currently written in PHP (CodeIgniter), I've come to the point where I'm writing more libraries and core extensions than I'm writing new code. I've been looking at some Rails tutorials and I'm liking what I see so far (even though I feel you have less control of what's been passed around). However it seems there's little information out there (maybe I'm not looking in the right places) on database tables without models.
For example, I need a table called user_verification_token
CREATE TABLE IF NOT EXISTS `user_verification_token` (
`user_id` int(11) NOT NULL,
`token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`is_used` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
It doesn't make sense for me to create a model for this, right?
First question: How do I generate a migration to create a table alone without any model?
Second question: When this table is filled with data, how do I associate with it. e.g. Associate it with the User model (if possible)/a user object so I can find a user by a token (provided is_used = 0) and return the user's data?
I apologize in advance if this is a newb question, I can do all these comfortably with PHP but I don't know how to do this on Rails.
You need a model for this table. It does make sense for you to have a model for the user_verification_token table. In order to get the associations and all the other functionality you'll want from this data, you need to create a model and the associations for it.
To answer the first question: run rails generate migration your_migration_name
For the activity_type table you mention in the comment on the original question, you might be interested in the RailsLookup gem to help automate some of the work required to make good use of lookup tables in Rails. There is a blog post describing it and the source can be found on GitHub
Your current scenario does require a model, although when you need to store data like categories e.g Sports(football,tennis,cricket,swimming,basketball,etc), you can store them as constants in your config->initializers->constant eg. SPORT_CATEGORIES = [["Football","football"],["Tennis","tennis"],etc], alternatively if you have more cols to store you can create a model then create the default rows as you would in a php .sql file but in ruby of course :) For example:
sport_categories = [
{:category => "football", :category_type => "manly" },
{:category => "Tennis", :category_type => "manly" },
etc
]
sport_categories.each do |attributes|
Model_name_here.find_or_initialize_by_category(attributes[:category]).tap do |p|
p.category_type = attributes[:category_type]
p.save!
end
end
Then you run rake db:seed.
I will suggest you to create model for this, as you will have complete access to user_verification_token table via UserVerificationToken model which will be associated to user.
Create a model because all the validations can be done in a model.
eg:validates :user_id. So there is no need to include those constraints while creating the table

Ajax Datatables from Rails

Hi I have just followed Ryan Bates guide to loading data from the server as the client interacts with the datatable.
http://railscasts.com/episodes/340-datatables?view=asciicast
In his ProductsDatatable class he defines a sorting function:
def sort_column
columns = %w[name category released_on price]
columns[params[:iSortCol_0].to_i]
end
Basically the column name is used to query the database, so "name", "category", etc are all attributes to the product model. So an SQL SELECT statement is generated with Name or Category being used for sorting ASC or DESC.. and so on...
If I have data being displayed in a column that does not map explicitly to a attribute.. such as product.reviews.count, how would I then allow sorting of that column?
You would need something like this
def sort_column
columns = ['name', 'category', 'released_on,' 'price', 'number_of_reviews]
columns[params[:iSortCol_0].to_i]
end
But this means that you need to change the way you get your data, for example:
Product.where(…).select('name, category, count(reviews.id) as number_of_reviews').group('name, category').includes('reviews')
I haven't been able to test the definition of columns, you may have to use reviews.number_of reviews instead
I know this is a really old post (by now), but just wanted to let you know that this gem: https://github.com/antillas21/ajax-datatables-rails can help you jumpstart creating datatables classes easily, as described in the Railscast you mention (it's actually based in this same pattern).
What the gem does is to let you focus on writing the query to retrieve your model(s) data from the db and how to expose it in the JSON that jquery.dataTables needs.
Take it for a spin.

Resources