#StackBounty: #ruby-on-rails #rspec Rspec let variable producing weird result

Bounty: 100

I am having a weird issue with RSpec that I don’t quite understand.

This is my port_stock_spec.rb file:

# == Schema Information
#
# Table name: port_stocks
#
#  id                :bigint(8)        not null, primary key
#  portfolio_id      :integer
#  stock_id          :integer
#  volume            :integer
#  transaction_price :float
#  current_price     :float
#  percent_change    :float
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#  current_value     :float
#  dollar_change     :float
#  total_spend       :float
#  transaction_date  :datetime
#  action            :integer
#  position          :integer          default("open")
#  ticker            :string
#  slug              :string
#

require 'rails_helper'

RSpec.describe PortStock, type: :model do
  let(:stock) { create(:stock, price: 10.00) }
  let(:portfolio) { create(:portfolio) }
  let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }

  context "associations" do
    it { should belong_to(:portfolio) }
    it { should belong_to (:stock) }
  end

  context "methods" do
    it "should accurately calculate the positive percent_change of the current PortStock" do
      port_stock_1.current_price = 20.00
      expect(port_stock_1.calculate_percent_change).to eql 100.00
    end

    it "should accurately calculate the negative percent_change of the current PortStock" do
      port_stock_1.current_price = 5.00
      expect(port_stock_1.calculate_percent_change).to eql(-50.00)
    end

    it "should accurately calculate the negative dollar_change of the current PortStock" do
      port_stock_1.current_price = 5.00
      port_stock_1.volume = 1000
      expect(port_stock_1.calculate_dollar_change).to eql (-5000.00)
    end

    # more specs that may or may no interact with the let variables.            

    it "should accurately calculate the portfolio's initial_dollar_value" do
      expect(portfolio.initial_dollar_value).to eql 1000.00
    end

end

Then I have the following method on my portfolio.rb model:

  def calculate_portfolio_initial_dollar_value
    if self.portfolio.initial_dollar_value.nil?
      self.portfolio.initial_dollar_value = 0.0
    end
    self.portfolio.initial_dollar_value += (self.transaction_price * self.volume)
    self.portfolio.save!
  end

When I run my test suite, that last test keeps failing, when it shouldn’t:

Failures:

  1) PortStock methods should accurately calculate the portfolio's initial_dollar_value
     Failure/Error: expect(portfolio.initial_dollar_value).to eql 1000.00

       expected: 1000.0
            got: 798229.0

       (compared using eql?)
     # ./spec/models/port_stock_spec.rb:77:in `block (3 levels) in <top (required)>'

Finished in 5.05 seconds (files took 3.68 seconds to load)
29 examples, 1 failure, 19 pending

So I put a binding.pry within the it blocks of the last few tests and when I check the portfolio.initial_dollar_value it repeatedly changes the value.

[1] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 2864770.0,
 percent_change: 75.02,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 num_winners: 2,
 num_losers: 7,
 initial_dollar_value: 860679.0,
 dollar_change: 92865.0>
[2] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> port_stock_1.portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[3] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[4] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> portfolio
=> #<Portfolio:0x00007fcdc5c5db28
 id: 14,
 user_id: 7,
 current_dollar_value: 150.0,
 percent_change: -85.0,
 created_at: Sat, 13 Apr 2019 00:36:24 UTC +00:00,
 updated_at: Sat, 13 Apr 2019 00:36:42 UTC +00:00,
 num_winners: 0,
 num_losers: 1,
 initial_dollar_value: 1000.0,
 dollar_change: -850.0>
[5] pry(#<RSpec::ExampleGroups::PortStock::Methods>)> 

I don’t understand why.

Thoughts?

Edit 1

This is portfolio.rb Factory:

FactoryBot.define do
  factory :portfolio do
    user
    current_dollar_value { Faker::Number.number(7) }
    percent_change { Faker::Number.decimal(2) }
    num_winners { Faker::Number.number(1) }
    num_losers { Faker::Number.number(1) }
    initial_dollar_value { Faker::Number.number(6) }
    dollar_change { Faker::Number.number(5) }
  end
end

Edit 2

There is a callback on my port_stock.rb model that triggers methods related to portfolio_initial_dollar_value:

after_save :calculate_portfolio_initial_dollar_value

Also other callbacks that impact other aspects of the portfolio:

  after_save :update_portfolio_current_dollar_value
  after_save :update_portfolio_initial_dollar_value, if: (:total_spend_previously_changed? || :volume_previously_changed?)

  def update_portfolio_current_dollar_value
    self.portfolio.current_dollar_value = self.portfolio.port_stocks.open.map(&:current_value).sum
    self.portfolio.save!
  end

  def update_portfolio_initial_dollar_value
    self.portfolio.initial_dollar_value = self.portfolio.port_stocks.open.map { |ps| ps.volume * ps.transaction_price }.sum
    self.portfolio.save!
  end

Edit 3

For the full version both the model (port_stock.rb) & spec (port_stock_spec.rb) files, check out this gist. I didn’t want to pollute SO with that full dump.


Get this bounty!!!

#StackBounty: #nginx #cache #ruby-on-rails #phusion-passenger #http-status-code-404 Nginx + Passenger: Cache 404 urls

Bounty: 50

Context

I have a Rails app with a nginx server and Passenger.

The app is dynamically generating pages based from the request url: if the url exists in the database the app renders the corresponding page. Or if the url does not exist in the database the app renders a 404 page.

Problem

Many crawlers are trying to find vulnerabilities and request lots of urls (.git, admin/config.php, wp-login.php etc…)

Each of those requests are reaching the Rails app, which is generating hits in the database.

Solution

I am looking for a way to do this:

  1. first time a “non existent” url if requested it goes through the Rails app, which responds with a 404
  2. nginx caches and remember this url
  3. next time the same url is requested, nginx directly respond with 404 status without going through the Rails app

Also when the Rails app is restarted (through Passenger) this cache should be purged.

Tries

  • I tried to add fastcgi_cache_valid 404 10m; in the server block, it’s not working.
  • Also tried proxy_cache_valid 404 10m;

As you may guess I’m new to nginx.
Thanks for your help.

Nginx config

server {
  listen ...;

  server_name ...;

  root /path/to/rails/app;

  error_page 404 /404;
  error_page 500 502 503 504 /500;

  # First I tried this, no success so I removed it
  fastcgi_cache_valid 404 10m;

  # Then I tried this, no success so I removed it also
  proxy_cache_valid 404 10m;

  location / {
    gzip_static on;
    etag off;
    charset utf-8;
    add_header Cache-Control "max-age=0, private, must-revalidate";
    add_header Referrer-Policy strict-origin-when-cross-origin;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options deny;
    add_header X-XSS-Protection "1; mode=block";

    location = / {
      try_files /cached/index.html @rails;
    }

    location / {
      try_files /cached$uri.html /cached$uri $uri @rails;
    }
  }

  location @rails {
    passenger_enabled on;
    passenger_ruby /path/to/ruby;
    passenger_app_env production;
  }
}


Get this bounty!!!

#StackBounty: #ruby-on-rails #cancancan #ruby-on-rails-5.2 #trestle-admin #trestle How do I specify an ability for a route created by a…

Bounty: 100

I am using Trestle Admin, this is my route:

trestle_path        /admin         Trestle::Engine

When a user that is not an admin visits the /admin route, I would like CanCanCan to handle it like it handles all of the other unauthorized requests in my app.

The issue though is that I can’t figure out how to specify that ability in my ability.rb or I can’t figure out where to add an authorize statement to.

When I visit /admin in my app, this is what my log looks like:

Started GET "/admin" for ::1 at 2019-03-31 01:10:01 -0500
Processing by Trestle::DashboardController#index as HTML
Redirected to http://localhost:3000/admin/login
Filter chain halted as :require_authenticated_user rendered or redirected
Completed 302 Found in 13ms (ActiveRecord: 0.0ms)

So all that happens is that it redirects to /admin/login which is how the Trestle engine handles it.

But I would like for CanCanCan to hijack that and handle it like it handles all other unauthorized requests throughout my application via the rule in my application_controller.rb:

rescue_from CanCan::AccessDenied do |exception|
  respond_to do |format|
    format.json { head :forbidden, content_type: 'text/html' }
    format.html { redirect_to main_app.root_url, alert: exception.message }
    format.js   { head :forbidden, content_type: 'text/html' }
  end
end

But, given that it isn’t a Model or a controller I defined, I am not sure what to specify in my ability.rb.

I have tried the following, all to no avail:

  if user.has_role? :admin
    can :manage, :all
  else
    cannot :read, :trestle
  end

or:

  if user.has_role? :admin
    can :manage, :all
  else
    cannot :read, :admin
  end

Is it possible for me to do what I am trying to do?


Get this bounty!!!

#StackBounty: #ruby-on-rails #ruby #ruby-on-rails-5 #monkeypatching #rails-activestorage Monkey patching ActiveStorage::Attachment gets…

Bounty: 50

So I decided to add an url attr_accessor to ActiveStorage::Attachment objects.

In development the patch holds for a while until it seems to “have been lost”. Meaning it works for few minutes, then it does not work anymore. Then I need to restart the server in order to get the patch applied again. I believe I am not patching correctly and I would need advises in that mater.


Here is what I tried:

lib/ext/active_storage/attachment.rb

First try :

module ActiveStorageUrl
  extend ActiveSupport::Concern

  included do
    attr_accessor :url
  end
end

ActiveStorage::Attachment.send :include, ActiveStorageUrl

Second try

class ActiveStorage::Attachment < ActiveRecord::Base
  attr_accessor :url
end

And by the way in both case it’s loaded with this:

config/initializers/monkey_patches.rb

require 'ext/active_storage/attachment'

So when it work I have no error message, but after a while the patch “diseapear” (lacking better terms), and I get the following error, telling me my attr_accessor is not there anymore. Rails must have reloaded ActiveStorage classes and my patch is lost.

Module::DelegationError in Products#images
url delegated to blob, but blob is nil


Get this bounty!!!

#StackBounty: #ruby-on-rails #internationalization #ruby-on-rails-3.2 Why don't my locale settings in number_to_currency work?

Bounty: 100

Per the Rails 3.2 API Docs, to use different locales for number_to_currency, I need to do the following:

<%= number_to_currency(1234567890.506, :locale => :fr) %>

I was expecting the following output:

# => 1 234 567 890,51 €

Even though I literally use that exact thing within my app and it keeps outputting the following:

$1,234,567,890.51

When I check for the available_locales within my app I get the following:

> I18n.available_locales
=> [:en, :de, :es, :fr, :ja, :pl, :"pt-BR", :ru, :sv, :"zh-CN"]

So it SHOULD work, but it doesn’t.

What am I missing?

Update 1

Per @s3tjan’s comment, I did some digging in that linked Rails issue and that led me to my application.rb where I discovered I18n.enforce_available_locales = false. I changed that to true and restarted the server.

When I tried the above again, I am now getting this error:

ActionView::Template::Error (:fr is not a valid locale):

Not sure how to fix this.

Update 2

So I just realize that I never had a locale file in my config/locales. What I really want is to use the GBP Pounds for currency, so I added an en-GB.yml file in my config/locales, then I restarted my server and console.

In my application.rb, I have the following:

I18n.enforce_available_locales = true

Then I checked my console and got this:

[1] pry(main)> I18n.available_locales
=> [:en, :de, :es, :fr, :ja, :pl, :"pt-BR", :ru, :sv, :"zh-CN", :"en-GB"]
[2] pry(main)> 

So the :"en-GB" was added successfully to my app’s load path.

But when I do this in my view:

<%= number_to_currency(1234567890.506, :locale => :"en-GB") %>

This is the error I get:

:"en-GB" is not a valid locale excluded from capture due to environment or should_capture callback

ActionView::Template::Error (:"en-GB" is not a valid locale):

So still not working.

Update 3

My en-GB.yml file was taken directly from https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en-GB.yml

So it looks exactly like that.


Get this bounty!!!

#StackBounty: #javascript #ruby-on-rails #vue.js #blob #wkhtmltopdf Using JS to consume a Rails send_data response

Bounty: 50

I have a VueJS frontend that is connected to a Rails API backend.

In one of the endpoints, I am using WickedPDF to generate a PDF. When I open the URL itself in a browser, the PDF downloads fine and works exactly as expected. When I send a request via Vue, the API responds with a weird looking string that looks like this:

%PDF-1.4
1 0 obj
<<
/Title (��)
/Creator (��wkhtmltopdf 0.12.4)
/Producer (��Qt 4.8.7)
/CreationDate (D:20190222102025+02'00')
>>
endobj
3 0 obj
<<
/Type /ExtGState
/SA true
/SM 0.02
/ca 1.0
/CA 1.0
/AIS false
...

I am not really sure what data type this is? I initially thought that it might be a BLOB, but I have no idea. I followed the logic outlined here to parse the response from my rails api, which did download a PDF to chrome. When opening this PDF, it is blank and the file name at the top of the chrome browser is a combination of weird chars. This makes me think that I am not converting the response in the correct way and some encoding issue ends up happening.

Here is my Rails API code:

def pdf
  pdf_html = ActionController::Base.new.render_to_string(
    template: 'api/v1/exporters/pdf',
    layout: 'pdf',
    page_size: 'A4',
    formats: :html,
    encoding: 'utf8',
    margin: {
      top: 20,
      left: 20,
    }
  )
  pdf = WickedPdf.new.pdf_from_string(pdf_html)
  send_data(
    pdf,                                  
    filename: 'download.pdf',                     
    type: 'application/pdf',                      
    disposition: 'attachment'
  )
end

Here is the JS function from the link above. I am passing the Rails response body (which when console logged is the weird looking char set in the first code block) as the only param:

showFile(blob){
  var newBlob = new Blob([blob], {type: "application/pdf"})

  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob);
    return;
  } 

  const data = window.URL.createObjectURL(newBlob);
  var link = document.createElement('a');
  link.href = data;
  link.download="file.pdf";
  link.click();
  setTimeout(function(){
    window.URL.revokeObjectURL(data);
  , 100}
}

Can anyone help point me in the right direction as to how to configure this in the right way or how to do it in a different/better way? Even a confirmation of what kind of data type the response is might help me.


Get this bounty!!!