Thomas Bush

Joined

4,680 Experience
22 Lessons Completed
2 Questions Solved

Activity

Posted in Deploy Ubuntu 20.04 Focal Fossa Discussion

You are attempting to install the version of Passenger for bionic beaver, 18.04. In there "Installing NGINX & Passenger" you need to use a different repository. Use: sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger-testing focal main > /etc/apt/sources.list.d/passenger.list' instead of sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list'. Also note this is still beta / testing for passenger, while it should work, the final version is expect to release in a couple more weeks.

Switch out this repository as mentioned above and rerun the commands form the installing passenger section -- this should allow you to install successfully.

Thanks for all the help Chris, I ended up figuring this out after you pointed me in the right direction.

In my specific case I needed to set in app/config/environments/production.rb:

config.assets.digest = true

I am still a bit confused as to why this was required as the rails docs state config.assets.digest defaults to true, as a result I have never explicitly set this value in any past rails apps.

from the current rails docs

Fingerprinting is enabled by default for both the development and production environments. You can enable or disable it in your configuration through the config.assets.digest option.

In any case, I really appreciate your help as always!

Well that's a great point.

The missing 404 error is for is for applicaiton.css, and the generated file on server is the fingerprinted file. I guess I don't quite understand why the requested file would be anything other than the fingerprinted file as I am just using the standard rails include:

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

My css works perfect locally, but I get a 404 error for applicaiton.css once deployed. I generated my base app using Jumpstart free version. I confirmed app css is generated on the server in the public assests folder
/example/current/public/assets/application-af01d6a4cd32bfcf9b8c41fa5fd1f1501cba6e3953ea8ff518309d0b7a8614c9.css

My nginx config and capistrano deploy both follow the webpacker docs: nginx serves static assets and packs, cap deploy ensures yarn is installed (each included below). My config/webpacker.yml is unchanged. I have seen dozens of issues where people are facing similar situations, but the solutions never seem to apply in my case, I have been stuck on this issue for 2 days now, hoping someone notices something obvious that I missed as I have been staring at it for too long. I appreciate any help or insight you can provide!

/etc/nginx/sites-enabled/example.com

server {
  listen 80;
  listen [::]:80;

  server_name example.com;
  root /home/deploy/example/current/public;

  passenger_enabled on;
  passenger_app_env production;

  location /cable {
    passenger_app_group_name example_websocket;
    passenger_force_max_concurrent_requests_per_process 0;
  }

  location = /favicon.ico { access_log off; log_not_found off; }
  location = /robots.txt  { access_log off; log_not_found off; }

  # Allow uploads up to 100MB in size
  client_max_body_size 100m;

  location ~ ^/(assets|packs) {
    expires max;
    gzip_static on;
    add_header Cache-Control public;
  }

  location /healthcheck {
    access_log off;
    return 200;
    add_header Content-Type text/plain;
  }
}

config/deploy.rb

lock "~> 3.13.0"
require 'capistrano-db-tasks'

set :application, 'example'

set :deploy_via, :remote_cache
set :deploy_to, '/home/deploy/example'
set :branch, ENV['BRANCH'] if ENV['BRANCH']

set :linked_files, %w{config/database.yml config/master.key}
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/packs', '.bundle', 'node_modules'

set :db_local_clean, true
set :db_remote_clean, true

set :keep_releases, 3
set :keep_assets, 3

before "deploy:assets:precompile", "deploy:yarn_install"
namespace :deploy do
  desc "Run rake yarn install"
  task :yarn_install do
    on roles(:web) do
      within release_path do
        execute("cd #{release_path} && yarn install --silent --no-progress --no-audit --no-optional")
      end
    end
  end

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      execute :touch, release_path.join('tmp/restart.txt')
    end
  end

  after :publishing, 'deploy:restart'
  after :finishing, 'deploy:cleanup'
end

Posted in Setup MacOS 10.14 Mojave Discussion

Joel, I know its been a few months, but this just happened to me and I was able to figure it out. To resolve, open xcode in my case as soon as I opened it updated, even though no updates were cited in app store ...? Next, I reinstalled the command line tools in terminal with: xcode-select --install

Once completed I could run the original command without error. Hope this helps

Posted in Custom Validations Issue

Even better, thanks again!

Posted in Custom Validations Issue

Chris,

Thanks for your help!!! I have included my final solution below on the off chance it could help someone else. Ended up discovering one more edge case, the user has two sites marked as default and deletes one of them -- the select call would still trigger a validation error.

After a bit of searching I found .marked_for_destruction? method which is really useful for nested attribute scenarios to validate against only the in-memory data a user intends to keep, instead of all in-memory data which would obviously include those records marked for deletion with "_destroy"=>"true" params

class UserValidator < ActiveModel::Validator
  def validate(record)
    if record.sites_users.select { |su| su.marked_for_destruction? == false }.count < 1
      record.errors.add(:site, "must have at least one site available")
    end

    if record.sites_users.select{ |su| su.marked_for_destruction? == false && su.is_default }.count != 1
      record.errors.add(:site, "must have exactly one site selected as default")
    end
  end
end

Thank you!

Posted in Custom Validations Issue

Chris,

That solves the single default for each user, totally clears up my confusion, too thanks.

Oddly though, the record.sites.count < 1 allows me to delete all sites for a users and save without validation error. This feels like it should contain the in-memory instance, what am I missing?

Posted in Bundle Package and Deployment

Yes, any rake / rails command you would run locally can be run in that fashion on prod.

Typically you wouldn't want to seed the database on every single deploy, unless you have some special circumstance. In my experience db:seed is a one-off-task, typically reserved for initial setup. I run the command, just as you have above, a single time to get my database inline.

After initial setup, the database will still contain this data no matter how many future deploys you have. Does that make sense? I could of course be missing something if you have a unique set up.

Posted in Bundle Package and Deployment

Philip,

edit

Looks like while I was typing Chris responded, defer to that answer.

First of all, welcome, I have been a pro memeber for over 4 years now, you will love the community and gain TONS of value from the pro episodes, highly recommend the upgrade.

I think I can help with some of these questions.

Are you using the 'capistrano-bundler' gem? Is this included in your Capfile? Note that '.bundle' should be added to the linked_dirs array in the deploy.rb file per the capistrano-bundler gem's readme. If this isn't your issue, it may be beneficial to paste your capfile and deploy.rb files.

Next, I believe your issue with running rails console on the prod server is that you need to change directories to the current folder of your app, where rails would be installed and also use the production environment variable. If you follow the GoRails guides for deploment, that would look llike this:

  • ssh deploy@1.2.3.4
  • cd my-app-name/current/
  • RAILS_ENV=production bundle exec rails c

Once in the proper directory, and you set the env for the command, you should be able to access the console as you expect. Hopefully this helps!

Posted in Custom Validations Issue

I have a custom validator that I would like to protect some data on my Users to Sites has_many through relaitonship. There is one extra piece of data on the sites_users join table is_default which is type boolean with the intention being to allow each user to have exactly one default site from the list of related sites for that user.

Instead of testing the current form instance, I am getting results in my validator to these two methods based on the prevesiously saved db instance for this same record. So if the user I am editing has one related site, but the current form isntance would remove all related sites the validation will still pass, even thought I am attempting to ensure at least one site present. Also, if the user I am editing has only one default_site, but the current form instance would set three more default sites, this passes as well, even though I am trying to validate that exactly one site is listed as default.

I have included my user validator and model code below. I can't quite figure out how to validate against the form instance isntead of the previously saved instance. I would really appreciate any help anyone could provide. Thanks you!

Here is my custom validator:

** user_validator.rb**

class UserValidator < ActiveModel::Validator
  def validate(record)
    if record.sites.count < 1
      record.errors.add(:site, "must have at least one site available")
    end

    if default_site.count != 1
      record.errors.add(:site, "must have exactly one site selected as default")
    end
  end
end

and this is my user model
** user.rb**

class User < ApplicationRecord
    has_many :sites_users
    has_many :sites, through: :sites_users
    accepts_nested_attributes_for :sites_users, allow_destroy: true
    ...
    def default_site
      sites.joins(:users).where(sites_users: { is_default: true } )
    end
    ...
end

Posted in Carrierwave file extension issue

It sounds like I misunderstood the extension_whitelist method, I thought this was similar to a frontend form validation -- my intention is to only allow the user to send a png, as I need to use transparent backgrounded images in certain places, than generate the jpgs as these are used most places.

Regardless, removing the whitelist entirely yeilds the same result -- correct images in s3, but incorrect images in db.

instance.file_name.large.url 
=> "../large_ac25c7fe-b835-4e7d-adc3-98c92d9f15d8.png"

The actual file for the above renders a 404 as the file is a .jpg in S3

instance.file_name.large_png.url
=> "../large_png_ac25c7fe-b835-4e7d-adc3-98c92d9f15d8.png"

Posted in Carrierwave file extension issue

Carrierwave is converting and uploading image files with proper extensions to S3, but storing all in db as .png extension when accessing the .url method for versions.

The file name and secure token methods are taken straight from the carrierwave wiki

Any help would be greatly appreciated, thanks!

Uploader

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  storage :fog

    def extension_whitelist
      %w(png)
  end

  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  version :large do
    process resize_and_pad: [650, 650, '#FFFFFF', 'Center'], convert: :jpg
  end

  version :large_png do
    process resize_to_fit: [650, 650]
  end

  protected

  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) || model.instance_variable_set(var, SecureRandom.uuid)
  end
end

So I may be making inacurate assumptions, but I think you want to receive a cloudfront url instead of an s3 url. Cloudfront is a CDN distribution that you attach to an s3 bucket. If so, I layout the process below. Once done, restart your app and file_url should return a cloudfront url that is the image in the s3 bucket.

Reason you would want cloudfront url instead of s3 url
s3 is intentionally not overly quick retrieving/serving assets -- you will want to create a cloudfront distribution and attach it to the s3. I believe its a pretty straight forward process, just essentially choose your s3 bucket in the cloudfront creation process -- cloudfront will keep itself in sync with your bucket so no worries there. In that scenario, s3 accepts and stores images from your rails app, while cloudfront is the CDN for your s3 bucket -- so all images will have a cloudfront base url.

Than add one more line to your config/initializers/carrierwave.rb, it will look like this:
config.asset_host = 'https://your-cloudfront-base.cloudfront.net'

Example carrierwave.rb

CarrierWave.configure do |config|
  config.fog_credentials = {
    ...your credentials
  }
  config.fog_directory  = 'YOUR-BUCKET'
  config.asset_host = 'https://your-cloudfront-base.cloudfront.net'
  config.fog_attributes = {'Cache-Control'=>'max-age=315576000'}  # optional, defaults to {}
end

What is the name of the carrierwave string column you use for this file?

If it file for example than the url is simply file_url as a result of the fact that you are using carrierwave. Or if you wanted a specific version, you could use file.thumb.url.

I am buidling an application for real estate centered around comparing properties. I have a self referential relationship, Comps, which joins two properties and stores some additional data.

Given:
PropertyA.comps => PropertyB
The following must also be true:
PropertyB.comps => PropertyA

I can only think of two routes to accomplish included below

  1. Add create and destroy hooks to automatically add and removed the inverse relationship. When researching, I found a great article on this method. My only reservation is that creating duplicate records and using hooks feels dirty, but my hunch is that this is an exception.

  2. Handle this case with ruby. Load up all the normal and inverse Comp records, normalize and merge. This approach feels like it would be very slow in comparion.

I was hoping that someone with more experience could validate my assumption that option 1 is the optimal direction to move. It feels like an exception to the rule: that using hooks and creating duplicate records makes sense in this case. I appreciate any help or advice you can provide, thanks!

What do you see when running tail -f your_app_folder/shared/log/production.log on the server before trying to updload as Jacob sugguested?

Got sidetracked with other projects, and finally got back to this with help. Thank you Chris -- processing locally and uploading with s3_uploader solved, no need for Carrierwave in the end.

For now, my solution is to turn off the default scope, even though this is something I actually want. I than manually re-enadable it in the index action. Included below incase anyone else experiences this issue. Still very confused, but I guess this works for now...

product.rb
acts_as_paranoid without_default_scope: true

product_controller.rb index action
@products = Product.without_deleted.order("position")

Any idea how to find the query? I was coming to the same conclusion myself, so I commented out the before_action set_product entirely in an attempt to break the app. The application still loads product show pages -- which I guess proves that there's something else loading products somewhere. Problem being its not in the controller and its not in the show view, so I don't really know where it could be.

  • ensured dev:cache was not enabled
  • searched for 'product =', 'product=', etc in sublime
    • products controller and products api controller commented both instances out
  • ran rails tmp:cache:clear to ensure not somehow cached
  • restarted server numerous times
  • ran spring stop

I am out of ideas. I just upgraded to Rails 5, is there some new magic I am not aware of?