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
GeorgieMordor
Jan 23, 2015
I'm having an issue writing a controller test. My app uses Knock for handling JWTs, and my test looks something a little like this:

code:
def authenticated_user(user)
    token = Knock::AuthToken.new(payload: { sub: user.id}).token
    { "Authorization": "Bearer #{token}" }
end

it "responds to an authorized request" do
     post :create, params: {}, headers: authenticated_user(@user)
     expect(response).to be_ok
end
When rspec runs though it fires back with an
code:
unknown keyword: headers
error.

I'm aware controller tests are supposed to be deprecated, so is this why it's happening? I can accomplish these tests as a request test instead, though I'm curious as to what I'm doing wrong / what's going on.

EDIT:

Missed this earlier -- explains a way to force an auth header through pre-request in a controller test:

https://github.com/nsarno/knock/issues/174

GeorgieMordor fucked around with this message at 17:53 on Feb 19, 2019

Adbot
ADBOT LOVES YOU

kayakyakr
Feb 16, 2004

Kayak is true

GeorgieMordor posted:

I'm having an issue writing a controller test. My app uses Knock for handling JWTs, and my test looks something a little like this:

code:
def authenticated_user(user)
    token = Knock::AuthToken.new(payload: { sub: user.id}).token
    { "Authorization": "Bearer #{token}" }
end

it "responds to an authorized request" do
     post :create, params: {}, headers: authenticated_user(@user)
     expect(response).to be_ok
end
When rspec runs though it fires back with an
code:
unknown keyword: headers
error.

I'm aware controller tests are supposed to be deprecated, so is this why it's happening? I can accomplish these tests as a request test instead, though I'm curious as to what I'm doing wrong / what's going on.

EDIT:

Missed this earlier -- explains a way to force an auth header through pre-request in a controller test:

https://github.com/nsarno/knock/issues/174

Yes, it's happening because it's a controller test. Your link even explains why (controller tests don't include the same "get" helper).

You don't seem to be doing anything that requires a controller test? Just use a request test, it'll save you pain in the long run.

GeorgieMordor
Jan 23, 2015

kayakyakr posted:

You don't seem to be doing anything that requires a controller test? Just use a request test, it'll save you pain in the long run.

Totally. Request tests should be more than adequate, especially since this is an API app anyway. I was just running through some RSpec refresh and thought I'd give the controller tests a go just for sharpness' sake.

Out of curiosity with controller tests being soft deprecated what's an example of some functionality that would merit a controller test these days?

kayakyakr
Feb 16, 2004

Kayak is true

GeorgieMordor posted:

Totally. Request tests should be more than adequate, especially since this is an API app anyway. I was just running through some RSpec refresh and thought I'd give the controller tests a go just for sharpness' sake.

Out of curiosity with controller tests being soft deprecated what's an example of some functionality that would merit a controller test these days?

Historically controller tests were used to ensure that certain instance variables were set in the controller before rendering any sort of views and it made it easier to retrieve values that were created/updated. Multi-mode servers have gone out of favor and feature and request specs cover the use cases better most of the time.

Pollyanna
Mar 5, 2005

Milk's on them.


I made a models graph of our monolith with railroady and the resulting image is 1.7ft by 224ft large.

8ender
Sep 24, 2003

clown is watching you sleep

Pollyanna posted:

I made a models graph of our monolith with railroady and the resulting image is 1.7ft by 224ft large.

Hello do you work for Shopify

Pollyanna
Mar 5, 2005

Milk's on them.


Nope, we just do dumb poo poo like autogenerate classes for a variety of types X namespaces. poo poo that shouldn’t be modeled via OOP, or at least inheritance. The codebase is very much Baby’s First OOP Project in certain ways.

Our specs take 15~20 minutes to run when split across 24 Docker containers running in parallel. :gonk: I have since come to the conclusion that our slow, lovely specs are a symptom and not a cause.

That reminds me to ask: what tack should I take in trying to refactor heavy interdependencies between AR-backed models? Our specs are slow because we have a lot of models that depend on long chains of associated models existing in the database in order to be valid, do work, etc. For example, an Ad requires a Campaign to exist in the database, a Campaign requires a Customer, a Customer requires an Advertiser, etc. This is a common pattern throughout our entire codebase, and that means an individual model needs to write its whole line to the DB to be testable. I have no idea how to fix this short of rearchitecting the app from scratch.

Pollyanna fucked around with this message at 18:21 on Feb 25, 2019

necrotic
Aug 2, 2005
I owe my brother big time for this!
That sounds strikingly similar to an ad platform I worked on a couple years ago.

Pollyanna
Mar 5, 2005

Milk's on them.


I wouldn’t be surprised if it’s pretty common among big companies.

xtal
Jan 9, 2011

by Fluffdaddy

Pollyanna posted:

That reminds me to ask: what tack should I take in trying to refactor heavy interdependencies between AR-backed models? Our specs are slow because we have a lot of models that depend on long chains of associated models existing in the database in order to be valid, do work, etc. For example, an Ad requires a Campaign to exist in the database, a Campaign requires a Customer, a Customer requires an Advertiser, etc. This is a common pattern throughout our entire codebase, and that means an individual model needs to write its whole line to the DB to be testable. I have no idea how to fix this short of rearchitecting the app from scratch.

0. Ensure you have the proper indices set up on the foreign keys (SQL EXPLAIN)
1. Eager load that long chain of dependencies with one query with something like `Ad.includes(campaign: { customer: :advertiser }).find(id)`
2. Use an in-memory database or disable fsync with eatmydata

I don't think that having a lot of foreign keys and stuff is inherently a design problem; in fact, it's often either a choice between having a long chain of dependencies, or having one table with a long list of messy, unrelated columns. When you use joins and eager loading properly, the performance is nearly the same, but the design is far cleaner.

xtal fucked around with this message at 00:22 on Feb 26, 2019

Pollyanna
Mar 5, 2005

Milk's on them.


It’s actually more that it can take up to 1.25 seconds to instantiate and persist a single valid model from a factory in some cases, most notably the models used in our 10-minute feature test. Retrieving associated records is a whole different ballgame that is itself a loving trash fire, but for different reasons than a simple it { is_expected.to validate_presence_of :advertiser } taking .5~1 seconds.

xtal
Jan 9, 2011

by Fluffdaddy
I would try and measure how much time is spent in CPU versus IO. If you're writing your validations and callbacks using fast algorithms and no IO (which is recommended) then you would need hundreds and hundreds of validations to get that slow. So, I think you either have a quadratic algorithm in there somewhere, which you can find using profiling tools; or, you're doing IO such as DB or HTTP, which you should remove from the model and move to a higher level of abstraction.

Installing the Bullet gem might help too. If you have a lot of one-to-many relationships, and aren't careful with your preloading, you will get n+1 queries, which are a huge performance killer for Rails apps.

GeorgieMordor
Jan 23, 2015
What do folks use for serialization these days?

Chilled Milk
Jun 22, 2003

No one here is alone,
satellites in every home

GeorgieMordor posted:

What do folks use for serialization these days?

If you're following json:api, https://github.com/Netflix/fast_jsonapi

kayakyakr
Feb 16, 2004

Kayak is true
Fastest freeform I've worked with was Grape::Entity. Was using it in a normal rails API app, not with Grape.

Partycat
Oct 25, 2004

I have been working on developing my internal toolset in ruby and rails, and have been slowly improving with my techniques, built my own internal gem for some modules, etc

Is the general opinion on this that I should be doing this in python instead at this point? Is Ruby falling apart into decay?

Python immediately turned me off by being column/space dependent giving me COBOL flashbacks, was really hoping not to have to deal with that poo poo or line terminators ever again.

good jovi
Dec 11, 2000

'm pro-dickgirl, and I VOTE!

Partycat posted:

I have been working on developing my internal toolset in ruby and rails, and have been slowly improving with my techniques, built my own internal gem for some modules, etc

Is the general opinion on this that I should be doing this in python instead at this point? Is Ruby falling apart into decay?

Python immediately turned me off by being column/space dependent giving me COBOL flashbacks, was really hoping not to have to deal with that poo poo or line terminators ever again.

No, it’s fine.

Ruby’s not going anywhere. They just previewed some upcoming features in Ruby 3 that sound quite neat, too.

If you want to do a different language, try something with a very different paradigm. Expand your mind a bit. Like, say, clojure, or perhaps an Elm frontend for your rails app. Vastly different languages like that actually complement each other better in your brain, in my experience. Seeing other ways of doing things is going to make you a better programmer. Going from Ruby to Python is just going to make you focus on piddly poo poo like indentation.

The fact that you’re even asking the question suggests that you’re not going to be applying for any jobs where they’d expect you to have really deep, specialized knowledge about a particular language. So get some breadth.

GeorgieMordor
Jan 23, 2015
So, I'm confused.

In Rails 5, where do best practices dictate I should be testing for a cookie being set?

I have a method in my ApplicationController that sets a cookie, and I want to make sure this is working correctly in my spec.

Pardot
Jul 25, 2001




GeorgieMordor posted:

So, I'm confused.

In Rails 5, where do best practices dictate I should be testing for a cookie being set?

I have a method in my ApplicationController that sets a cookie, and I want to make sure this is working correctly in my spec.

Just to make sure, you're talking about an extra cookie, separate from the session cookie?

You could do a features spec, and do the full stack request and then look at the `response_headers` to see the Set-Cookie header. Probably, I haven't done that specifically, but I have that to ensure some CSP headers are set, so it's probably similar.

Chilled Milk
Jun 22, 2003

No one here is alone,
satellites in every home

GeorgieMordor posted:

So, I'm confused.

In Rails 5, where do best practices dictate I should be testing for a cookie being set?

I have a method in my ApplicationController that sets a cookie, and I want to make sure this is working correctly in my spec.

This has always kinda felt like a code smell/antipattern type thing, but I have used this gem in the past and it's worked out.

https://github.com/nruth/show_me_the_cookies

GeorgieMordor
Jan 23, 2015

Pardot posted:

Just to make sure, you're talking about an extra cookie, separate from the session cookie?

You could do a features spec, and do the full stack request and then look at the `response_headers` to see the Set-Cookie header. Probably, I haven't done that specifically, but I have that to ensure some CSP headers are set, so it's probably similar.

Yep, this is a separate cookie from the session one.

Good call sniffing the response headers. I did it that way, and then ran an include? method on the Set-Cookie string that returns and find the expected value in there.


The Milkman posted:

This has always kinda felt like a code smell/antipattern type thing, but I have used this gem in the past and it's worked out.

https://github.com/nruth/show_me_the_cookies

I saw this too, and share the smell sentiment but this honestly didn't seem like a bad route either.

GeorgieMordor
Jan 23, 2015
So, how do you access a global app variable in a feature test? For instance I'm setting something myself in my ApplicationController, and want to make sure it's being set correctly.

Previously I was using the assigns() method , but that's for the apparently dare-not-speak-their-name controller tests.

In general and at least for me, with the deprecation of controller tests, I'm finding it tricky to find best patterns / practices recommendations for replacing stuff.

Peristalsis
Apr 5, 2004
Move along.
I hope this is a simple question to answer, but I can't quite find it elsewhere.

I have an index page with some filter params. There's a link on the page to download a csv version of the page, so the controller looks roughly like this:

code:
def index
  # Set up @items variable with filtered/selected Item rows

  respond_to do |format|
    format.html
    format.csv { send_data(Item.csv_export(@items)) }
  end
end
The index page looks sort of like this:

code:
<%= link_to(items_path(format: 'csv') %>

# Show table of item objects
The problem is that I need to pass params[:filters] through the link to the csv option, so that it will print what's on the view, but I don't see an obvious way to do it.

Changing the link like so, <%= link_to(items_path(format: 'csv', filters: params[:filters]) %> leads to unable to convert unpermitted parameters to hash.

necrotic
Aug 2, 2005
I owe my brother big time for this!
Sounds like an issue with Strong Parameters: you need to permit(:filters) before you can use it (and probably a permit! on that to allow everything under that key).

Tea Bone
Feb 18, 2011

I'm going for gasps.
I’m trying to set up a relationship from a record to records it isn’t associated with. Imagine the following:

code:
class Reader<ActiveRecord::Base
	belongs_to :collections
	has_many :books, through: :collection
end

class Books<ActiveRecord::Base
	belongs_to :collections
	has_many :readers, through: :collections
end

class Collection<ActiveRecord::Base
	has_many :books
	has_many :readers
end
I want to define an association in reader which will return all the books not in their collection. I could use the following method:

code:
def books_not_in_collection
	Books.where(“id NOT IN (?)”, book_ids)
end
But that causes N+1 queries if it’s run on more than one reader, ideally I want to set it up as an association in reader. Is this possible?

Tea Bone fucked around with this message at 13:20 on May 15, 2019

kayakyakr
Feb 16, 2004

Kayak is true

Tea Bone posted:

I’m trying to set up a relationship from a record to records it isn’t associated with. Imagine the following:

code:
class Reader<ActiveRecord::Base
	belongs_to :collections
	has_many :books, through: :collection
end

class Books<ActiveRecord::Base
	belongs_to :collections
	has_many :readers, through: :collections
end

class Collection<ActiveRecord::Base
	has_many :books
	has_many :readers
end
I want to define an association in reader which will return all the books not in their collection. I could use the following method:

code:
def books_not_in_collection
	Books.where(“id NOT IN (?)”, book_ids)
end
But that causes N+1 queries if it’s run on more than one reader, ideally I want to set it up as an association in reader. Is this possible?

code:
has_many :non_collection_books, class_name: 'Books', -> {unscoped.where(“id NOT IN (?)”, book_ids)}
but that's still gonna give you N+1 queries, probably. It really depends on what you're looking for when you query multiple readers. Are you looking for books that are in none of the reader's collections? If so do something like:

code:
class Books
  def self.not_in_collection(reader_ids)
    Books.joins(:collections)
         .joins("LEFT JOIN collections_readers ON collections_readers.collection_id = collections.id")
         .where("collections_readers.reader_id NOT IN (?)", reader_ids)
  end
(note: not tested, may not be even close to correct)

If you're looking to fill out a list of books on each reader's non-books array, well, I think that the N+1 may be the best you can get because rails has to break that down to apply it to the object anyway.

Pardot
Jul 25, 2001




If you're using postgres, you probably don’t want `not in` due to some caveats about nulls. Instead do the antijoin with `left join` and an `is null` or (and I prefer) `not exists` https://explainextended.com/2009/09/16/not-in-vs-not-exists-vs-left-join-is-null-postgresql/

Aquarium of Lies
Feb 5, 2005

sad cutie
:justtrans:

she/her
Taco Defender
My company has a large Ruby on Rails app that's 6+ years old, but thankfully is relatively up-to-date version wise (currently running on Rails 5). During the initial production, we chose to use JRuby + puma for performance reasons. This works fine in production, but one of the biggest issues our devs have with the codebase is how slow development is. Between JRuby (especially startup time) and poorly written specs, it takes 35-45 minutes to run all our specs. This makes it really annoying to wait for our CI to go green even for very small changes.

Despite having no prior experience with Ruby or Rails, I'm working on digging into options to speed up spec time. One of the easiest wins is to switch from JRuby to Ruby MRI. Currently very little of our code actually requires JRuby and the parts that do can easily be updated to not. The benefits seem to be a better supporting Ruby implementation, faster turnaround on bug fixes and new features, and an improved dev process. Is there a reason in 2019 to continue to stick with JRuby?

Obviously if I go forward with this, I'd do a gradual rollout (we have our app distributed on multiple servers and scaling up/down is practically trivial) to get performance metrics of our actual app/loads for comparison. I'm just looking for any high-level opinions between the two to decide whether this is worth pursuing further.

aunt jenkins
Jan 12, 2001

As you said, you'll want to check your specific app's performance. The other reason it's used is because it can easily interop with Java libraries and SDKs. If you're not doing that or using JRuby-only gems that you can't replace, it's definitely worth an experiment. MRI 2.6 is definitely quite a bit faster than 1.9.3 was. :)

Hadlock
Nov 9, 2004

Aquarium of Lies posted:

My company has a large Ruby on Rails app (currently running on Rails 5)poorly written specs, it takes 35-45 minutes to run all our specs. This makes it really annoying to wait for our CI to go green even for very small changes.

This is normal, not great, but normal. We had something like 4 team city agents running around the clock to service 12 engineers on ~200k loc

Once your specs get > 4 hours is time to worry or consider breaking up the monolith

Peristalsis
Apr 5, 2004
Move along.

Aquarium of Lies posted:

My company has a large Ruby on Rails app that's 6+ years old, but thankfully is relatively up-to-date version wise (currently running on Rails 5). During the initial production, we chose to use JRuby + puma for performance reasons. This works fine in production, but one of the biggest issues our devs have with the codebase is how slow development is. Between JRuby (especially startup time) and poorly written specs, it takes 35-45 minutes to run all our specs. This makes it really annoying to wait for our CI to go green even for very small changes.

Despite having no prior experience with Ruby or Rails, I'm working on digging into options to speed up spec time. One of the easiest wins is to switch from JRuby to Ruby MRI. Currently very little of our code actually requires JRuby and the parts that do can easily be updated to not. The benefits seem to be a better supporting Ruby implementation, faster turnaround on bug fixes and new features, and an improved dev process. Is there a reason in 2019 to continue to stick with JRuby?

Obviously if I go forward with this, I'd do a gradual rollout (we have our app distributed on multiple servers and scaling up/down is practically trivial) to get performance metrics of our actual app/loads for comparison. I'm just looking for any high-level opinions between the two to decide whether this is worth pursuing further.

I don't know anything about JRuby, but if you're not already doing it, this gem can parallelize your tests. It's not especially smart about how it divides up the tests between threads/processes, but it does shorten our test runs by quite a bit. I believe Rails or RSpec is supposed to be parallelized by default in a not-too-distant release, too.

Aquarium of Lies
Feb 5, 2005

sad cutie
:justtrans:

she/her
Taco Defender

Peristalsis posted:

I don't know anything about JRuby, but if you're not already doing it, this gem can parallelize your tests. It's not especially smart about how it divides up the tests between threads/processes, but it does shorten our test runs by quite a bit. I believe Rails or RSpec is supposed to be parallelized by default in a not-too-distant release, too.

Rails 6 has parallel testing built in which we’re looking forward too :) Other devs have tried parallelizing in the last without much luck so I’m curious to see if it’ll work better with 6.

Hadlock posted:

This is normal, not great, but normal. We had something like 4 team city agents running around the clock to service 12 engineers on ~200k loc

Once your specs get > 4 hours is time to worry or consider breaking up the monolith

I’ve heard other people say this but it’s still really annoying to go from my other work projects where I can run a specific test in seconds to Rails, where it’s at least a minute if I want to run a single spec. My workflow isn’t quite TDD but I rely heavily on test feedback as I’m developing features and it is affecting my productivity.

Sivart13
May 18, 2003
I have neglected to come up with a clever title

Aquarium of Lies posted:

I’ve heard other people say this but it’s still really annoying to go from my other work projects where I can run a specific test in seconds to Rails, where it’s at least a minute if I want to run a single spec. My workflow isn’t quite TDD but I rely heavily on test feedback as I’m developing features and it is affecting my productivity.
Taking a minute to run a single spec is definitely a JRuby-specific issue (or some other global test setup that ought to be worked through) and the main thing that made me want to stop using JRuby on a previous project.

If you're ambitious you could try hacking up the test suite so it could run in either JRuby or MRI, maybe using an environment variable to work around any interpreter-specific behavior. Could give you that MRI feeling without having to cut hard away from JRuby.

Pardot
Jul 25, 2001




Sivart13 posted:

If you're ambitious

If you're even more ambitious you could ditch bundler for gel https://github.com/gel-rb/gel

8ender
Sep 24, 2003

clown is watching you sleep

Aquarium of Lies posted:

Rails 6 has parallel testing built in which we’re looking forward too :) Other devs have tried parallelizing in the last without much luck so I’m curious to see if it’ll work better with 6.

We've been using this: https://github.com/grosser/parallel_tests with Rspec with great results. We run a Drone instance locally with 4 workers to chew through the test suite.

Piano Maniac
Oct 10, 2011
Rails gurus, I am in a dire need of your help.
Could somebody break it down Mickey Mouse style.

How the heck can I create a link, something like

code:
<a data-toggle="collapse" href="#XXXX"> Some text </a>
Which once you press on it, it will target a div with id #XXXX (that conveniently matches my file_id)

code:
  <div id="#XXXX">
  </div> 
Which would conveniently insert some sweet ruby-enhanced HTML partial into the div thing,
insert the file_id parameter into the right places (so I can display file_id.pdf file and make it bootstrap collapsible), render it and show it to the user.

I have asked my co-workers but they all say わかんないAJAX使ったら? and I am like poo poo, I have been writing ruby for like 2 weeks only. What the hell am I supposed to do? I don't get what AJAX and controllers do either...

I have searched for hours now and now I am dying. Please don't ask me to post my controllers or something, I am a mess right now and it's my twelfth hour in the office. :bang:

kayakyakr
Feb 16, 2004

Kayak is true

Piano Maniac posted:

Rails gurus, I am in a dire need of your help.
Could somebody break it down Mickey Mouse style.

How the heck can I create a link, something like

code:
<a data-toggle="collapse" href="#XXXX"> Some text </a>
Which once you press on it, it will target a div with id #XXXX (that conveniently matches my file_id)

code:
  <div id="#XXXX">
  </div> 
Which would conveniently insert some sweet ruby-enhanced HTML partial into the div thing,
insert the file_id parameter into the right places (so I can display file_id.pdf file and make it bootstrap collapsible), render it and show it to the user.

I have asked my co-workers but they all say わかんないAJAX使ったら? and I am like poo poo, I have been writing ruby for like 2 weeks only. What the hell am I supposed to do? I don't get what AJAX and controllers do either...

I have searched for hours now and now I am dying. Please don't ask me to post my controllers or something, I am a mess right now and it's my twelfth hour in the office. :bang:

https://www.rapidtables.com/web/html/link/html-anchor-link.html

basically, if you're putting the # in front of the ID on the target, then you're doing it wrong.

Pollyanna
Mar 5, 2005

Milk's on them.


That might be part of it, but it sounds like there’s even more logic than core Rails handles.

Piano Maniac
Oct 10, 2011
Thanks for the thoughts & prayers, I managed to solve it with the help of my genius CTO. It was as easy as AJAX!

Adbot
ADBOT LOVES YOU

Pollyanna
Mar 5, 2005

Milk's on them.


One of the senior engineers on the team says that some of our service code shouldn’t actually set any default values for a given model (which needs it to build the form), and that the default values for the new model should instead be determined by whatever the form partial defines as the defaults. I really dislike the idea of our views being the ones in charge of default values, but I don’t know if I’m being too dogmatic about it. Am I overly concerned about this?

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