Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I'm having a little trouble finding the best way to handle my associations and callbacks.

I have a Parent and a Child class. Parent has a field called master_count, Child has a count field. Each child of the parent needs to be assigned a portion of the master_count.

Ex.
Parent has a master_count of 10
ChildA gets a count of 6
ChildB gets a count of 4

If a child is created/updated, the parent needs to recalculate which children get which slices of the pie.

They're currently all in a nested attribute form.

Ideally, I would like all of the Children to be updated and then Parent calls it's assign_count method. However, it looks like Parent's after_save callbacks run before the Children get saved, so I can't call assign_counts there. I thought about giving Child's association a touch: true, but that will cause an infinite loop. I feel like I'm missing something simple.

Adbot
ADBOT LOVES YOU

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through

Cocoa Crispies posted:

In a situation like this, I'd be really tempted to move the behavior of what you're actually working with out to a new class, even if it isn't something actually persisted into the database.

If you're dealing with how to allocate a pizza, I'd make a PizzaAllocator class that takes a parent, and a count of how many slices of pizza you're splitting up. The PizzaAllocator would update the master_count on the Parent, and the count on the Child instances, and wrap it all in a transaction so it rolls back if it fails partway through:

Ruby code:
snip
If you're encoding on a specific allocation of slices to children, set up the allocator to take them as inputs between construction and allocation.

Callbacks and associations get confusing really quickly, and I've found that moving that logic out to its own class can salvage a bad situation.

This makes sense. The allocation code is cluttering up Parent, and it'll be much easier to test and modify outside on its own.

KoRMaK posted:

What order are these things getting saved as? Why are you saving the parent first instead of the children? I'd put the after_save on the children to call the Parent.

I'm using a form for the parent, which has nested fields for each of the children. Rails processes and saves the parent before it even looks at the children, which makes sense for most cases.

Ideally I'd be able to process all the data on the forms for the Children and the Parent, save them, and then call the PizzaAllocator, which will process the data and save them again. I'm just not sure how to make this happen.

My main problem now is that I don't know where to put the calls to PizzaAllocator. I've learned that I can use update_column instead of save in the Allocator in order to avoid triggering after_save callbacks. I can put my calls to the Allocator in after_saves on the parent and the children, but that's not ideal. A parent with three kids is going to get allocated up to 4 times, and only the last one actually matters. Is there a way to wrap all the update code into a packet and then execute the Allocation code? I suppose I could do something like:

Ruby code:
class Parent
def update
  super
  PizzaAlloccator.allocate
end
That's similar to what I was doing before this refactoring process but it feels a little too hackish.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Makes sense. I've just been read to keep as much out of the controller as possible I'm wary of doing anything there. Makes sense that it's something the controller should do over anything else.

Where's the conventional place to put PizzaAllocator? In with the models?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I do ajax this way:

  • I have an action that responds to format.js
  • I have a link like this on a view
    Ruby code:
    link_to 'Action', action_model_path(model), remote: true
  • I have my javascript that I want executed when that link is pressed in action.js.erb under that model's views

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through

kayakyakr posted:

e: ^^ that's a very old-school way of doing it.

Do you know of a tutorial for a more modern way of doing it? I learned that from the rails docs.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
First thing I did at my new job was look in /test/. Nothing.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Ruby code:
class ChainClass
  def initialize(hello = 'world')
    @hello = hello
    @counter = 0
  end

  def chain
    @counter += 1
    self
  end

  def puts_chain
    puts "#{@hello}, #{@counter}"
    self
  end
end

chain1 = ChainClass.new
chain1.chain.chain.puts_chain # => world, 2
chain2 = ChainClass.new("hi goon")
chain2.chain.chain.chain.chain.puts_chain.chain # => hi goon, 4

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
At the place I'm working all the view partials are put into concern folders, (like app/views/model_name/concerns/_partials.html.erb). Is this common?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I've got a best practices question about Ajax stuff:

I have a model Article, and I want to be able to update particular attributes of that article through an AJAX form on the Article's Show page. These attributes could be Title, Status, and Footnote.

Right now, I am placing the forms for updating these attributes in their own partials, and placing those onto the show page inside hidden div tags. The forms target the Update action of the ArticleController. I'm manually handling the Ajax calls by binding the ajax:whatever events in articles.js, and this feels a little smelly.

Would I be better off having dedicated update_title action, with its own update_title.js.erb file to handle the Ajax call? I'm a little afraid of overloading the controller.

Edit:
Maybe in update.js.erb I could do something like

JavaScript code:
<% if params[:title] %>
  // stuff to update title
<% end %>
<% if params[:status] %>
  // stuff to update status
<% end %>
// and so on
That kinda feels like a horror but it seems more organized for some reason/

MasterSlowPoke fucked around with this message at 16:14 on Jan 16, 2015

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Is there a fast .xls reader for Ruby? I don't want to write anything or look at fonts or whatever, I'm just interested in the data. I've tried out Roo, but it's taking 3-4 minutes to load a very small (33x10) spreadsheet.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I've tried both
code:
sheet = Roo::Spreadsheet.new('public/uploads/test.xls')
sheet = Roo::Excel.new('public/uploads/test.xls')
Both of these lines take approximately forever, and then the result takes about 20 seconds just to puts to the screen.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
The answer is don't set a class variable unless you have a really good reason to.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
!(session[:user_id].nil?)

if session is nil,
is_logged_in? => !(true) => false

if session is set
is_logged_in? => !(false) => true

I'd probably have session[:user_id].present?.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I've got the feeling I'm not writing my tests properly:

Ruby code:
RSpec.describe Activity, :type => :model do
  context "on creation" do
    before do 
      @lead = FactoryGirl.create(:lead).reload
      @activity = FactoryGirl.create(:activity, lead_id: @lead.id).reload
      @lead.reload
    end

    it "updates last_activity_at of the parent lead" do
      expect(@lead.last_activity_at).to eq(@activity.created_at)
    end
  end

  context "on deletion" do 
    before do 
      @lead = FactoryGirl.create(:lead)
      @first_activity = FactoryGirl.create(:activity, lead_id: @lead.id, created_at: 5.minutes.ago).reload
      destroyed_activity = FactoryGirl.create(:activity, lead_id: @lead.id, created_at: 2.minutes.ago).reload
      destroyed_activity.destroy
      @lead.reload
      
    end

    it "resets the last_activity_at to the last activity" do
      expect(@lead.last_activity_at).to eq(@first_activity.created_at)
    end
  end
end
Ruby code:
RSpec.describe Lead, :type => :model do
  it "can point you to the previous and next leads" do
    leads = 5.times.collect { FactoryGirl.create(:lead) }

    expect(leads[0].prev_lead).to be nil

    leads.each.with_index do |lead, i|
      expect(lead.prev_lead).to eq(leads[i-1]) unless i == 0
      expect(lead.next_lead).to eq(leads[i+1]) unless i == 4
    end

    expect(leads[4].next_lead).to be nil
  end
From browsing around online I'm thinking that my before blocks there aren't ideal, and that I have way too many expects in the last example. Is there a resource to help me write better tests? Or am I crazy?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Gotcha, thanks. I've written these tests for my user model using that info, do they look good?

Ruby code:
RSpec.describe User, :type => :model do
  [...]
  context "admin scope" do 
    let(:users) { FactoryGirl.create_list(:user, 2) }
    let(:admin) { FactoryGirl.create(:admin) }

    it "can pick out admin users" do
      expect(User.admin).to eq([admin])
    end

    it "can pick out non-admin users" do
      expect(User.admin(false)).to eq(users)
    end
  end

  context "division scope" do
    let(:division) { FactoryGirl.create(:division) }
    let(:unassigned) { FactoryGirl.create(:user, division: nil) }
    let(:assigned) { FactoryGirl.create_list(:user, 2, division: division) }
    let(:manager) { FactoryGirl.create(:manager, division: division) }

    it "can find people not assigned to a division" do
      expect(User.division(false)).to eq([unassigned])
    end

    it "can find people who belong to a particular division" do
      expect(User.division(division)).to eq([assigned, manager].flatten)
    end
  end
end
I

As an aside, I don't like this pattern on my admin scope:
Ruby code:
  scope :admin, ->(admin = true) { admin ? where(role: 'a') : where.not(role: 'a') }
I can do something like this, but I think it's trading dryness for readability:
Ruby code:
  scope :admin, ->(admin = true) { where("role #{ '!' unless admin}= 'a'") }
There's got to be a better way that I'm not seeing.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
It doesn't say the poop goes in the toilet. The flush is probably just so no one else knows what you're up to in the stall.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Maybe go about it backwards? Something like

Ruby code:
ids = ModelY.select('DISTINCT user_id').pluck(:user_id) 
User.where.not(id: ids) 

Two simple queries instead of a join query. On phone so may be typos.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
IIRC AR will still store UTC in the database, but auto convert it when pulling it out.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I'd probably just do a really simple scope:

Ruby code:
scope :search, ->(query="") { where("name LIKE '%?%' OR location LIKE '%?%'", query, query)
Unless you need something fancier?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I'm writing some tests for our Cancan implementation, and they're a little repetitive:

Ruby code:
require 'rails_helper'

RSpec.describe Ability, type: :model do
  describe "Board Abilities" do
    let(:ability) { Ability.new(FactoryGirl.build(:board_user)) }

    context "allowed actions" do
      it("can view Users") { expect(ability.can?(:read, User.new)).to be_truthy }
      it("can view Partners") { expect(ability.can?(:read, Partner.new)).to be_truthy }
      it("can view Super Partners") { expect(ability.can?(:read, SuperPartner.new)).to be_truthy }
      it("can view Partner Reports") { expect(ability.can?(:read, PartnerReport.new)).to be_truthy }
      
      it("can see User records") { expect(ability.can?(:records, User)).to be_truthy }
      it("can see Partner records") { expect(ability.can?(:records, Partner)).to be_truthy }
      it("can see SuperPartner records") { expect(ability.can?(:records, SuperPartner)).to be_truthy }
      it("can see Partner Report records") { expect(ability.can?(:records, PartnerReport)).to be_truthy }

      it("can read Anayltics pages") { expect(ability.can?(:manage, :analytic)).to be_truthy }
      it("can view Sales pages") { expect(ability.can?(:manage, :sale)).to be_truthy }
      it("can view Report pages") { expect(ability.can?(:manage, :report)).to be_truthy }
      it("can view Customer Report pages") { expect(ability.can?(:manage, :customer_report)).to be_truthy }
      it("can view Manual Review pages") { expect(ability.can?(:manage, :manual_review)).to be_truthy }
      it("can view Data Quality pages") { expect(ability.can?(:manage, :manual_review)).to be_truthy }
    end

    context "disallowed actions" do
      it("cannot see Business records") { expect(ability.can?(:records, Business)).to be_falsy }
    end
  end
end
What would be a good way to dry this up?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
If you want to learn user authentication, follow the relevant chapters (looks like 6-10) in the excellent Rails Tutorial.

If you're not really interested in user auth, use devise. Any solution you throw together without learning the Why's is going to be hopelessly insecure.

Finally, whatever you do, never store plaintext or encrypted passwords.

MasterSlowPoke fucked around with this message at 00:04 on Mar 28, 2015

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Thought there was a CI thread but I can't find one. Does anyone know if theres a way to configure Jenkins to post a generated web page? My testing generates a coverage report using simplecov, and I'd like to be able to access it on the CI server as well. It's not hugely important, but it'd be nice if it's possible.

edit: Found this plugin for posting Rcov files.

MasterSlowPoke fucked around with this message at 16:51 on Mar 31, 2015

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through

EVGA Longoria posted:

Have you tried CodeClimate? How does it compare? We use CC here and it's ok, but if there's better test coverage I could look into, that'd be great.

I've not. I'm still rather new at Rails and testing in particular. Where I'm working we had no tests when I started - the codebase is mostly just 400 line controller methods. I went with Simplecov just because it was recommended in some tutorial.

I don't like that SimpleCov only provides reports for files called in the tests - there are a lot of models and such that I haven't gotten to yet, and I'd just like to see them so I could eventually check them off. If I wasn't working with a legacy code base that wouldn't be such an issue.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through

Buckhead posted:

I need to process a string word by word and see if certain words match a condition, then compile the string back together. Basically, I am pulling social media posts and want #s and @s to show up in blue.

Does anyone have any experience doing something like this? In that past, I would probably use something like PHP's explode() function, but perhaps there are more elegant methods.

Ruby code:
"lets #fart it up @butt".split(' ').collect { |word| ['#','@'].include?(word[0]) ? word.upcase : word }.join(' ')
Obviously use something other than upcase to tag the words.

edit: okay I must have opened this tab a long time ago and never looked at it

MasterSlowPoke fucked around with this message at 18:35 on Apr 6, 2015

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I'm just about to use Resque, is there anything I should know about it or should I jump to something else before I'm in too deep?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
What is the contents of the @config hash there?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
It's too much data, so if it's lost then it's easier to replace than a backup hard drive?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through

Sewer Adventure posted:

Because it was a has_one relation, assignment doesn't need a save call :pseudo:

When I ever have to use the production console like that I make sure to use sandbox mode.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Should be as simple as Board has_many :boards, belongs_to :board.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
https://www.youtube.com/watch?v=mdqLMkPqjOQ

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
It looks like that's going through and updating every cart to follow the new policy of grouping LineItems together. It is triggered once, when you run your migration. It's odd that there's no structural changes that enforce or allow the policy in that migration - I would have added the quantity column there instead of an earlier migration if I was writing that tutorial.

I think it might just be a demonstration of how to write a reversible complex migration, and showing that migrations can do more than just alter tables.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Simplist way would be to do

code:
system('rake whatever:cool_stuff &')
If you need more structure you could look into setting up a job queue.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I bought the book Rails 4 Test Prescriptions and it was pretty good.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I just set up Sentry this week. Haven't spent a lot of time with it but it looks pretty neat.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
It's kind of like how += works:

a += 1
a = a + 1

Are both the same statement. So:

a ||= 1
a = a || 1

Means that a, if it's a falsy value (like nil), will equal 1. &= and |= aren't common, but they'll be

# for a = 9
a = a & 5
a &= 5

Both result in a being set to 1. Same thing for |=. Bitwise AND and OR simply aren't very popular or generally useful, so no one writes about them.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
You're probably just being tricked by the output. Consider:

Ruby code:
2.3.1 :001 > a = 7
 => 7 
2.3.1 :002 > a & 5
 => 5 
2.3.1 :003 > 5 & a
 => 5 
2.3.1 :004 > a && 5
 => 5 
2.3.1 :005 > 5 && a
 => 7 
& is the Bitwise AND operator. It looks at the bits set here comparing 7 (0111) and 5 (0101). 7 and 5 both have the 4's place and the 1's place set, so the answer is 5.

&& is the Logical AND operator. It look at the truthyness of the operands, and returns the last value looked at. 7 and 5 are both truthy, so it returns the last operand. This is useful for short circuiting - if you do:

if nil && 5

It first looks at nil, and as that's falsy, ends looking and returns nil. This causes the if check to fail.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
Right now we have two databases. We need to get data (about 300k rows) from a secondary database, use 3 different primary keys to the primary database to retrieve information, and then write the data to a file.

The current system (all with active record) is this:

Establish connection to the secondary database
Make a request for the 300k rows
Reconnect to primary database
Map the rows using the primary keys, then inject a hash to hold the data from those rows
Then create an array collating the data.

It's slow as gently caress and I'm sure it's far from the standard way to go about it. What's the correct way to get massive amounts of data connected between databases?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
It's using ActiveRecord::Base.establish_connection to swap databases. The really slow part is pulling the associated data from the primary database. It's just a huge find query reduced to a hash: Addresses.where(id: query.map(&:address_id)).pluck(:fields).inject({}) {...}

It's typically done in the job queue.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
After switching back to the database with the Addresses table I can't pluck anymore right? That does another select query and I no longer have access to that table.

Adbot
ADBOT LOVES YOU

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
I'd like to set up the model to use the database, but unfortunately due to the way they set up their databases (there's multiple versions of this database, 1 per year for some reason I'm not sure) I can't set that up in a non-hacky way.

Did some research and decided on doing something kind of like pluck_in_batches:

Ruby code:
    addresses = {}
    num_batches = address_ids.length/1000.0).ceil
    0.upto(num_batches) do |iterator
      Address.where(id: address_ids[iterator * 1000, 1000])
             .pluck(:id, :country, :city, :postal)
             .each do |address|
               addresses[address[0]] = address[1..-1]
             end
    end
I don't think there's a built in batch like .find_in_batches or .find_each that doesn't create ActiveRecord objects, is there?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply