#StackBounty: #ruby-on-rails #json #postgresql #jsonb How to update different json (jsonb) attributes asynchronously with rails and pos…

Bounty: 50

I have a large json object stored in a postgres table column, an example of the schema is like this:

create_table “document” do |t|
    t.jsonb “data”, default: []
end

At the moment I’m updating json in the column like so:

# find document in rails then…
doucment.data[‘some_attribute’][2][‘another_attribute’] = 100
doucment.save

However I write this json attribute many times and sometimes data becomes lost because if two calls are writing it at the same time, then the whole object will be saved or over written with the current objects old data.

For example if there’s two different saves going through at the same with the following

Save 1:

doucment.data[‘some_attribute’][2][‘another_attribute’] = 100
doucment.save

Save 2:

doucment.data[‘some_attribute’][2][‘different_attribute’] = 200
doucment.save

then either one of the attributes data will be lost because the other one will save it’s json but with old data that hasn’t been refreshed yet.

What is the best way to go about making both calls save the new data correctly.

Is there any json method that can just go in and update one attribute, like update_attribute but for a jsonb attribute?


Get this bounty!!!

#StackBounty: #ruby #ruby-on-rails #join #active-record Update join table using list of checkboxes in Rails

Bounty: 50

I have Gig and Singer Active Record models (standard–no customization just yet) with a many-to-many relationship through a generic join table which holds nothing but the respective ids of Gig and Singer. My form sends a given gig id and all the singers who will be attending, denoted with check boxes. I need to have the ability to check or uncheck singers. The following code works, but it does so by removing all the singers from a gig and re-adding them. This feels hacky… is there a better way? (I think this is all the code necessary but let me know if you need me to add anything)

class GigSingersController < ApplicationController

    def create
        gig = Gig.find(params[:gig_id])
        singer_ids = params[:singer_ids] # [1, 4, 5,]
        gig.singers = []
        singer_ids.each do |id|
            singer = Singer.find(id)
            gig.singers << singer
        end
        redirect_to gigs_path
    end
end

EDIT:

As requested in the comments, here are the schema and relevant models, although as I said, they are completely generic. Perhaps I didn’t do a good job of making my question clear: Is the best way to create these relationships when using a checkbox to remove all existing ones and recreate them from the boxes currently checked, thereby removing any that the user unchecked on an edit?

ActiveRecord::Schema.define(version: 2019_07_19_195106) do

  create_table "gig_singers", force: :cascade do |t|
    t.integer "gig_id"
    t.integer "singer_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "gigs", force: :cascade do |t|
    t.string "name"
    t.text "notes"
    t.datetime "datetime"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "singers", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "active"
  end

class Gig < ApplicationRecord
    has_many :gig_singers
    has_many :singers, through: :gig_singers
end

class GigSinger < ApplicationRecord
    belongs_to :gig
    belongs_to :singer
end

class Singer < ApplicationRecord
    has_many :gig_singers
    has_many :gigs, through: :gig_singers

end


Get this bounty!!!

#StackBounty: #ruby-on-rails #cors #ruby-on-rails-5 Rails Font CORS policy

Bounty: 50

I can’t load this font for a CORS Policy.

Folder: app/assets/fonts/Inter-UI.var.woff2

<%=preload_link_tag("Inter-UI.var.woff2", as:'font', crossorigin: "anonymous")%>

Error:

Access to font at
http://localhost:3000/assets/Inter-UI.var-e2e323d19d24946c4d481135af27ba00f3266aa9d4abe4262e97088feccb6ca4.woff2
from origin ‘http://0.0.0.0:3000‘ has been blocked by CORS policy: No
‘Access-Control-Allow-Origin’ header is present on the requested
resource.

Response HTTP status code

enter image description here

If I go directly to http://localhost:3000/assets/Inter-UI.var-e2e323d19d24946c4d481135af27ba00f3266aa9d4abe4262e97088feccb6ca4.woff2 I can download the file successfully.

I have already tried with rack-cors gem, but it’s not working

config/environments/developement.rb

Rails.application.configure do

  config.middleware.insert_before 0, Rack::Cors do
    allow do
      origins '*'
      resource '*', :headers => :any, :methods => :any
    end
  end


Get this bounty!!!

#StackBounty: #ruby-on-rails #postgresql #activerecord Is it OK to specify a schema in `table_name_prefix`?

Bounty: 50

TL;DR: Is it OK to specify a schema in table_name_prefix?

We have a large Rails application that is not quite a traditional multi-tenant app. We have a hundred clients, all supported by one app, and that number will never grow more than 1-2 per year. Currently, every client has their own Postgresql database.

We are addressing some infrastructure concerns of having so many distinct databases…most urgently, a high number of simultaneous database connections when processing many clients’ data at the same time.

The app is not visible, even to clients, so a lot of traditional multi-tenant web site philosophies don’t apply here neatly.

  • Each tenant has a distinct Postgres database, managed in
    database.yml.
  • Each database has a schema, named for the tenant.
  • We have a model specific to each tenant with notably different code.
  • Each model uses establish_connection to select a different database and schema.
  • Each model uses a distinct table_name_prefix with the client’s unique name.

The tables vary extensively for each tenant. There is no hope or desire to normalize the clients together. Clients are not provisioned dynamically — it is always a new code release with migrations.

We intend to move each of the client schemas into one database, so fewer distinct connection pools are required. The unique names we currently have at the database, schema, and table names mean there is no possibility of name collisions.

We’ve looked at the Apartment gem, and decided it is not a good fit for what we’re doing.

We could add all hundred schemas to schema_search_path, so all clients could share the same connection pool and still find their schema. We believe this would reduce our db connection count one-hundred-fold. But we’re a bit uneasy about that. I’ve found no discussions of how many are too many. Perhaps that would work, and perhaps there would not have a performance penalty finding tables.

We’ve found a very simple solution that seems promising, by adding the schema in the table_name_prefix. We’re already setting this like:

def self.table_name_prefix
  'client99_'
end

Through experimenting and looking within Rails 4 (our current version) and Rails 5 source code, this works to specify the schema (‘tenant_99’) as well as the traditional table prefix (‘client99’) :

def self.table_name_prefix
  'tenant_99.client99_'
end

Before that change, queries looked like this:

SELECT COUNT(*) FROM "client99_products"

After, they include the schema, as desired:

SELECT COUNT(*) FROM "tenant_99.client99_products"

This seems to answer our needs, with no downsides. I’ve searched the Interwebs for people encouraging or discouraging this practice, and found no mention of it either way.

So through all this, here are the questions I haven’t found definitive answers for:

  • Is there a concern of having too many schemas listed in schema_search_path?
  • Is putting a schema name in table_name_prefix okay?


Get this bounty!!!

#StackBounty: #ruby-on-rails #ruby #amp-html Headers setting for AMP form in Rails

Bounty: 300

My AMP form in Rails is returning this error in the terminal :

Completed 406 Not Acceptable in 338ms (ActiveRecord: 54.1ms)
ActionController::UnknownFormat (InscriptionsController#create is missing a template for this request format and variant.
request.formats: ["application/json"]
request.variant: []):

And in the browser :

POST http://localhost:3000/inscriptions?__amp_source_origin=http%3A%2F%2Flocalhost%3A3000 406 (Not Acceptable)
Response must contain the AMP-Access-Control-Allow-Source-Origin header
Form submission failed: Error: Response must contain the AMP-Access-Control-Allow-Source-Origin header​​​
at bb.f.assert (https://cdn.ampproject.org/v0.js:21:319)
at y (https://cdn.ampproject.org/v0.js:26:213)
at https://cdn.ampproject.org/v0.js:179:339

However I did follow the CORS sample code provided by the official documentation https://amp.dev/documentation/guides-and-tutorials/learn/amp-caches-and-cors/amp-cors-requests?referrer=ampproject.org, and according to the AMP validation, everything is fine,

This is my controller :

def create
  @inscription = Inscription.new(inscription_params)
  @inscription.save
  subscriber_email = inscription_params[:email].downcase
  if Subscriber.where(email: subscriber_email).count == 0
    @subscriber = Subscriber.new
    @subscriber.email = subscriber_email
    @subscriber.save
    @new_subscriber = true
  else
    @subscriber = Subscriber.where(email: subscriber_email).first
    @new_subscriber = false
  end
 [...]
 respond_to do |format|
  format.html { redirect_to root_path, notice: "Merci ! Nous avons bien reçu votre inscription !" }
  format.js
  format.json {
    allowed_origins = ["https://example.com", "https://example.com.cdn.ampproject.org/", "http://example.com.amp.cloudflare.com", "https://cdn.ampproject.org"]
    allowed_source_origin = "https://example.com"
    source_origin = params[:__amp_source_origin]
    origin = request.headers["Origin"]
    response.set_header('Content-type', 'application/json')
    response.set_header('Access-Control-Allow-Credentials', 'true')
    response.set_header('Access-Control-Allow-Origin', origin)
    response.set_header('AMP-Access-Control-Allow-Source-Origin', source_origin)
    p response.headers
  }
  end
end

May you help me with this ? I probably did something wrong with the headers. Thanks a lot !


Get this bounty!!!

#StackBounty: #ruby-on-rails #ruby #web-services #httpclient #httparty Send multiple files using HTTParty

Bounty: 50

Here is the code which is working using Net::HTTP::Post

request = Net::HTTP::Post.new(url)
...
form_data = [
  ['attachments[]', File.open('file1.txt')],
  ['attachments[]', File.open('file2.txt')]
]
request.set_form form_data, 'multipart/form-data'
http.request(request)

Now, I am trying to use httparty like below but it is not working.

body = { attachments: [ File.open('file1.txt'), File.open('file2.txt') ] }

HTTParty.post(url, body: body)

The response I am getting from web service call is below:

#<HTTParty::Response:0x557d7b549f90 parsed_response={"error"=>true, "error_code"=>"invalid_attachment", "error_message"=>"Attachmen
t(s) not found or invalid."}, @response=#<Net::HTTPBadRequest 400 Bad Request readbody=true>, @headers={"server"=>["nginx"], "date"=>[
"Mon, 20 May 2019 07:41:50 GMT"], "content-type"=>["application/json"], "content-length"=>["102"], "connection"=>["close"], "vary"=>["
Authorization"], "set-cookie"=>["c18664e1c22ce71c0c91742fbeaaa863=uv425hihrbdatsql1udrlbs9as; path=/"], "expires"=>["Thu, 19 Nov 1981
08:52:00 GMT", "-1"], "cache-control"=>["no-store, no-cache, must-revalidate", "private, must-revalidate"], "pragma"=>["no-cache", "no
-cache"], "x-ratelimit-limit"=>["60"], "x-ratelimit-remaining"=>["59"], "strict-transport-security"=>["max-age=63072000; includeSubdom
ains;"]}>

It looks like it is not able to read the contents of files. Does HTTParty supports this or I need to use some other gem?


Get this bounty!!!

#StackBounty: #apache-2.2 #ruby-on-rails #ruby-on-rails-3 ERR_SPDY_PROTOCOL_ERROR for Rails app: occurs on Chrome and Safari browsers, …

Bounty: 100

~20% of the time pages like this https://myhotpot.io/design/chrome_screenshot?id=14 fail to load with this error:

Failed to load resource: net::ERR_SPDY_PROTOCOL_ERROR

This problem happens on Chrome and Safari. It never happens in our WEBrick development environment (default Ruby on Rails apps) – only in the production environment, suggesting it’s an issue with our Rails hosting provider.

This similar question focuses on Chrome.

Stack:

  • CentOS release 6.10 (Final)

  • Apache 2.2.15 with this configuration

  • Passenger 5.3.7

  • Rails 3.2.12 (migrating soon!)

  • Ruby 2.1.2p95


Get this bounty!!!

#StackBounty: #apache-2.2 #apache-2.4 #ruby-on-rails #ruby-on-rails-3 ERR_SPDY_PROTOCOL_ERROR for Rails app: occurs on Chrome and Safar…

Bounty: 100

This is a similar question, but the answer didn’t help.

Similar questions on Stack Overflow and the web also did not help because those focused on Chrome solutions.

However, this problem hits us on Chrome and Safari. Moreover, it never happens in our development environment — only in the production environment, suggesting it’s an issue with our Rails hosting provider.

~20% of the time pages like this https://myhotpot.io/design/chrome_screenshot?id=14 never load because of this error:

Failed to load resource: net::ERR_SPDY_PROTOCOL_ERROR

Stack:

  • CentOS release 6.10 (Final)

  • Apache/2.2.15 (Unix)

  • Passenger 5.3.7

  • Rails 3.2.12 (migrating soon!)

  • Ruby 2.1.2p95

Any suggestions?


Get this bounty!!!

#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!!!