Rails - ActiveRecord Issue - ruby-on-rails

I am creating a political Rails app that will allow users to compare candidates' positions on issues. At the heart of it is a table, representing the Active Record table, where the rows represent five presidential candidates and columns represent issues. Each <td> will represent whether or not the specific candidate has stated a position on the issue. If not, of course, it will be nil.
The problem:
I have code for candidate.issues.each and issue.position, but I can’t seem to create new positions for candidates without overriding other ones.
The goal is to include both candidates and both positions, so that the user can get a closer look at where they stand on the issue.
ActiveRecord::Schema.define(version: 20160827005554) do
create_table "candidates", force: :cascade do |t|
t.string "fname"
t.string "lname"
t.string "title"
t.string "minitial"
t.string "party"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "issues", force: :cascade do |t|
t.string "issname"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "candidate_id"
t.string "position"
end
end
Models: Issue belongs to candidate
Candidate: has_many issues (or should it be positions for each issue?)
Examples, in the Rails console:
i = Issue.find(1) *Agriculture
c = Candidate.find(2) *Trump
c.i.position = “(a couple paragraphs of text)” => error.
Example 2, also in the console:
i = Issue.find(1) *Agriculture
i.candidate_id = 1 *Clinton
i.position = “(some paragraphs of text)"
i.save
i.candidate_id = 2 *Trump
i.position = “(some other paragraphs of text)"
i.save *overrides candidate_id(1) and, with it, Clinton’s position.
By the way, I intend to save paragraphs in the console so that I could give detailed positions on the issues. Is it feasible to include <b> tags, <i> tags, etc.?
Ex. Clinton on agriculture:
ENERGY
Clinton has touted the benefits of cleaner energy sources and posited them as a boon for agricultural economies. Most notably, her plan calls for the expansion of solar panels by half a billion by 2021. She supports expanded biofuel research and use.
FOOD POLICY
Clinton favors keeping federal food programs, like the Supplemental Nutrition Assistance Program (SNAP) program, funded. She also wants to increase local food options through different channels - grants that aim to connect consumers directly to farmers, increased funds for USDA marketing programs, and other non-specified avenues.
She has indicated support for the use of genetically modified organisms (GMOs).

It seems that you want to keep issues unique and at the same time save each candidate's position for the issue.
That will only work if you duplicate issues in the table. Like this:
trump = Candidate.find 1
clinton = Candidate.find 2
Issue.create(issname: 'Agriculture', candidate: trump, popsition: 'some text')
Issue.create(issname: 'Agriculture', candidate: clinton, position: 'text')
But for me it's not ideal. You'd better add another model - Position
class Position
belongs_to :candidate
belongs_to :issue
end
class Candidate
has_many :positions
end
class Issue
has_many :positions
end
create_table "positions", force: :cascade do |t|
t.integer "issue_id"
t.integer "candidate_id"
t.string "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Than you can write like this:
agriculture = Issue.find_by(issname: 'Agriculture')
trump = Candidate.find_by(lname: 'Trump')
trump.positions.where(issue: agriculture).create!(text: 'text')

Related

ActiveRecord search for similar entries to show page selection (Rails 5)

I have a show page generated from a very large database, queried by #listing = Listing.find(params[:listing_id]) in the controller. At the bottom of the page I also have links to three other listings; for now they are queried by #listings = Listing.last(3), but I am trying to pick three similar listings - by community, or by a price range.
example - Listing.where(:price => 500000..600000).limit(2)
However...I am at a loss as to how to do this in comparison to the active page listing. How would I pick listings from the same community, or within a certain price range of the same record, for instance? If someone could direct me as to how to make this connection I would appreciate it (I'm fairly junior, but also am really learning to enjoy ActiveRecord, and want to expand what I can do with it).
Before anyone asks, here's my schema:
create_table "listings", force: :cascade do |t|
t.string "mls"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "community"
t.string "description"
t.integer "price"
t.string "status"
t.string "address"
end
It looks like you need something like this:
price_range = (#listing.price - 100)..(#listing.price + 100)
Listing.where(price: price_range, community: #listing.community).limit(3)
You were on the right track!

Ransack: Conditions on both a child, and its respective grandchild record

Don't be intimidated by the length. It's probably actually quite simple.I couldn't find an answer, though I tried looking everywhere. I hope this is a good enough challenge for you.
Here goes:
Panel.rb
has_many :status_dates
has_many :statuses, through: :status_dates
StatusDate.rb
belongs_to :status
belongs_to :panel
def self.ransackable_attributes(auth_object = nil)
%w( current ) + _ransackers.keys
end
Status.rb
has_many :status_dates
Here is the schema.
create_table "panels", force: :cascade do |t|
t.string "no"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "status_dates", force: :cascade do |t|
t.integer "status_id"
t.integer "panel_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.date "date"
t.boolean "current"
end
add_index "status_dates", ["panel_id"], name: "index_status_dates_on_panel_id"
add_index "status_dates", ["status_id"], name: "index_status_dates_on_status_id"
create_table "statuses", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
What do I want to do?
I want to identify a parent record based on two conditions which must both simultaneously exist exist on: (i) a child record and (ii) in turn, that child's, child record too (i.e. a grand child record).
What does that mean?
Suppose we have:
I want to find all panels, where there is a particular StatusDate record such that StatusDate.current = y, and its respective child, Status.name = x.
(“Current” is actually a boolean value on the StatusDate record.)
What is happening at the moment?
Here is my _condition_fields.html.erb partial:
What is the problem?
Right now, ransack applies those conditions across different records. But I want them applied directly to: (I) a child record and (ii) that particular child's child record as well.
Any idea how I can do this?
Assistance very much appreciated.
Did you try query following way:
Panel.ransack({StatusDate_current:0, StatusDate_Status_name: "Installed"})

How to speed up score board calculation in rails from large database

What ways can I speed up total score calculation from about 100k rows users table and 15000k rows scores table database? I already added index to user_id in scores table.
Now, the speed is:
Rendered users/_user_row.html.erb (3.0ms)
Rendered users/index.html.erb within layouts/application (1970.1ms)
Completed 200 OK in 2113ms (Views: 343.8ms | ActiveRecord: 1768.3ms)
What I have is
User_controller:
def index
#users = User.by_total_points.limit(50)
end
User.rb model:
has_many :points
def self.by_total_points
joins(:points).group('users.id').order('SUM(points.value) DESC')
end
def total_points
self.points.sum(:value)
end
point.rb model:
belongs_to :user
Schema.rb
create_table "points", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "value", null: false
t.string "label", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "points", ["user_id"], name: "index_points_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "first_name", null: false
t.string "last_name", null: false
t.string "username", limit: 32, null: false
t.string "email", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "sumscore"
end
A lot of the answer is going to be contextual to how often it is hit, how often the points update and how ok it is for the scores to be outdated.
You will probably end up with either caching or a counter. Just a random stab at it without any context
Setup cache in memory and have a cache key for each user for total points
Setup cache in memory for the top 50 users and their points
When someone updates points update the first cache for that user AND also update the cache if they are now in the top 50
Now instead of hitting the database you can check the cache, your request will now be lighting fast at the expense of doing extra work updating caches.
http://guides.rubyonrails.org/caching_with_rails.html
Extra Note: Also write a rake task to rebuild these for the case when you lose the cache.
So this is less of a Rails problem, more of a system design problem. If you are trying to just always only get the top 50, you can
use priority queue and keep a heap of the top 50 players
cache that (either in redis, or just a separate table).
Everytime when you need to render the score board, you just need to pull that and sort 50 players.
This way, you have insertion time of nlog(n) on 50 records, which is constant asymptotically.
In this system, you will have two scroing tables, or one table, one array in redis. The main table keep all the scores, the cache table or array just keep the top 50.
If you are trying to get all the rankings, you can structure an sorted tree (n-nary) and bucket users into score ranges so that you do do log(n) insertion and read. This is a bit complicated to setup, and I suggest avoiding it unless you have hundreds of write per second updating scores and your app require real time ranking, and you have over 100m users.

Migrating legacy data to new model in rails

I'm getting started with Rails again and am running into a conundrum I find intimidating at the moment. I'm somewhat of a noob when it comes to working with databases, so please forgive me if this is fairly basic.
I have an older Rails app with a data model I no longer wish to conform to. The model should be deprecated in favor of a lighter, less complex one.
The older app is also very monolithic, so I'm trying to break it up into smaller service components.
So this leads me to my question, since it's generally frowned upon to use multiple databases from a single model… what would be the best method for translating data stored in the old model to my new model, one service at a time?
For example, let us suppose I have a user model in both the old and the new. In the old model, the user has many columns, not all of which should make it to the new model.
An example of this could be a change from a user being limited to a single address in the old model to being able to assign a one to many relationship where addresses are split off in their own model and simply referenced using a foreign key or something.
EDIT 1:
The goal ultimately is to siphon the data from the legacy model's database into the new model's database as easily as possible, one dataset at a time.
EDIT 2:
Originally posted from my mobile. Here are a couple examples which may help with suggestions.
OLD MODEL
create_table "brands", force: :cascade do |t|
t.string "name"
t.string "url"
t.string "logo"
t.boolean "verified"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "hidden", default: false
t.string "facebook_url"
t.string "twitter_handle"
t.string "pinterest_handle"
t.string "google_plus_url"
t.string "address_street1"
t.string "address_street2"
t.string "address_street3"
t.string "address_city"
t.string "address_state"
t.string "address_zip"
t.string "address_country"
t.string "email"
t.string "phone"
t.string "story_title"
t.text "story_text"
t.string "story_photo"
end
NEW MODEL
create_table "companies", force: :cascade do |t|
t.string "companyName", null: false
t.string "companyURL", null: false
t.boolean "companyIsActive", default: true, null: false
t.boolean "companyDataIsVerified", default: false, null: false
t.string "companyLogoFileURL"
t.datetime "companyFoundedOnDate"
t.integer "companyHQLocationID"
t.integer "companyParentCompanyID"
t.integer "companyFirstSuggestedByID"
t.string "companyFacebookURL"
t.string "companyGooglePlusURL"
t.string "companyInstagramURL"
t.string "companyPinterestURL"
t.string "companyTwitterURL"
t.string "companyStoryTitle"
t.text "companyStoryContent"
t.string "companyStoryImageFileURL"
t.boolean "companyIsHiddenFromIndex", default: false, null: false
t.integer "companyDataScraperID"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
So, basically... I want to be able to take data from the old model, say a brands "name" column and siphon its related values to the new model so the value ends up in the companies "companyName" column of a totally different postgresql instance.
Having done this multiple times, I can tell you that the easiest thing to do is to create a simple rake task that iterates the first collection and creates items in the new collection.
There is no need to use anything like DataMapper. You already have ActiveRecord and can simply define which DB connection to use for each model.
In your config/database.yml:
brand_database:
adapter: postgresql
host: brand_host
username: brand_user
password: brand_pass
database: brand_db
company_database:
adapter: postgresql
host: company_host
username: company_user
password: company_pass
database: company_db
In your models:
class Brand < ActiveRecord::Base
establish_connection :brand_database
end
class Company < ActiveRecord::Base
establish_connection :company_database
end
In your new rake task (lib/tasks/db.rake):
# lib/tasks/db.rake
namespace :db do
desc "Migrate brand records to company records"
task :migrate_brands_to_companies, [] => :environment do
Brand.find_each do |brand|
Company.find_or_initialize_by(companyName: brand.name) do |company|
puts "\n\tCreating Company record for #{brand.name}"
company.companyURL = brand.url
company.companyLogoFileURL = brand.logo
company.companyTwitterURL = "https://twitter.com/#{brand.twitter_handle}"
company.companyIsHiddenFromIndex = brand.hidden
company.created_at = brand.created_at
company.updated_at = brand.updated_at
company.save!
end
end
end
end
Finally, run the rake task:
$ rake db:migrate_brands_to_companies
I need to say this: Rails is built using a solid convention. Failing to adhere to that convention will cause problems and additional expense, everytime. I have seen this many many times. Every single time I have seen someone deviate from that convention, they run into far more trouble than they would have ever expected. They break a lot of the "Rails magic".
Taking a TDD approach would certainly help you cover more ground.
Look at DataMapper, which you can use in a Rake task or completely separate Ruby-script. This way, you can iterate over the app data (from Active Record) and pass it to the new Postgres DB via DataMapper.
You can connect to the new DB like this,
DataMapper.setup(:default, 'postgres://user:password#hostname/database')

Can I add multiple entries to one column attribute in my rails database?

I'm building a job board in the rails framework. I'm setting up my job scaffold and running into an issue.
The employer will be able to set the job_title, job_location, salary, description, required_education, required_major required_experience, and so forth. In some job postings, you'll see employers put in their description, "Minimum Education Requirement is a BS in Computer Science or related field." I'm trying to add numerous fields in my db under the required_major column so the employer can add the related degrees. For example, if the employer will accept a Bachelors degree in Computer Science, Software Engineering, Computer Engineering, or Information Technology; I want to be able to add multiple majors for the required_major field. Would I be able to create a subfield of this column so it would have all the degrees listed or should I have numerous columns (ex. required_major1 required_major2, etc.).
Here is my current schema for the jobs table:
ActiveRecord::Schema.define(version: 20160103030240) do
create_table "jobs", force: :cascade do |t|
t.integer "company_id"
t.string "job_title"
t.string "job_location"
t.decimal "salary", precision: 8, scale: 2
t.text "description"
t.string "required_education"
t.string "required_major"
t.string "required_experience"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Right now, I'm just going to start with a single degree field and expand on it as I build. Any input on this would be appreciated.

Resources