#StackBounty: #ruby-on-rails #forms #validation #activerecord #simple-form Rails custom validation that limits the number of has_many :…

Bounty: 50

I have a Rails project with a “Product Variant” form. The Product variant model is called Variant, and on the Variant form users should be able to select one choice for each option available. For instance a T-Shirt might have an “Option” called “Size” with the “Choices” small, medium, or large, and another “Option” called one “Color” with the “Choices” red, green, blue. Thus, the Variant created is a unique SKU such as a “T-shirt — Size: Small, Color: Green.” Or if it were a product that had 3 options instead of 2, the variant would require 3 choices per option, such as “Guitar Strap – Size: Long, Fabric Color: Red, Leather Color: Brown”.

I can’t figure out how to write a custom validation that only allows the user to save one choice per option. Each option should only have one choice selected for each variant. Here’s an illustration.

need a validation that prevents this

correct outcome

Here are my models with the relevant associations…

models/variant.rb

class Variant < ApplicationRecord    
  has_many :selections
  has_many :choices, through: :selections

  validate :one_choice_per_option

  private
    def one_choice_per_option
      # can't figure out how to do this custom validation here
    end

end

models/choice.rb

class Choice < ApplicationRecord
  has_many :variants, through: :selections

  belongs_to :option
end

models/selection.rb

class Selection < ApplicationRecord
  belongs_to :choice
  belongs_to :variant
end

models/option.rb

class Option < ApplicationRecord
  has_many :choices, dependent: :destroy

  accepts_nested_attributes_for :choices, allow_destroy: true
end

The best I’ve managed to do is get this custom validation working on in models/variant.rb

def one_choice_per_option
  self.product.options.each do |option|
    if option.choices.count > 1
      errors.add(:choice, 'Error: select one choice for each option')
    end
  end
end

But that only allows one Choice total through the variant form. What I want to do is allow one choice for each set of options.

I know that this could be done with Javascript in the UI, but this is essential to keeping the database clean and preventing a user error, so I believe it should be a Rails validation at the model level.

What is a “Railsy” way to do this type of custom validation? Should I be trying to do a custom validation on the Selection model? If so, how?


UPDATE

Based on the discussion in the comments. It seems that I need to do some combination of Active Record querying to make this work. @sevensidedmarble’s “EDIT 2” below is closer but that is giving me this error: Type Error compared with non class/module

If I save the wrong behavior to the database and then call Variant.last.choices in the console it feels like I’m getting closer:

illustrating the form behavior I need to prevent with a validation

showing the console response

So essentially, what I need to do is not allow the Variant form to save if there is more than one Selection with the same option_id. A selection shouldn’t save unless the option_id is unique to the associated Variant.

Something like this is what I’m trying to do:

validate :selections_must_have_unique_option

  private

    def selections_must_have_unique_option
      unless self.choices.distinct(:option_id)
        errors.add(:options, 'can only have one choice per option')
      end
    end

But that code doesn’t work. It just saves the form as if the validation weren’t there.


Get this bounty!!!

#StackBounty: #ruby-on-rails #ruby-on-rails-3 #logging #actioncontroller Rails 3.2.x: how to filter long parameters from getting logged…

Bounty: 100

There are similar questions like this, this, and this.

None help because the goal is to prevent logging of long parameters within a specific action instead of using config.filter_parameters. Also the answer must work for Rails 3.2.x while many answers are based on Rails 5.

One answer suggests calling request.filtered_parameters inside the controller method, but calling request.filtered_parameters.delete :long_param did not stop :long_param from getting logged.


Get this bounty!!!

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