Skip to content

Commit

Permalink
Better support for MySQL (and SQLite)
Browse files Browse the repository at this point in the history
This makes a few adjustments to the migrations for better multi-database
support. SQLite still does not support virtual columns in Rails, so user
search is rudimentary.

Also removes the annotate gem to reduce merge conflicts.
  • Loading branch information
excid3 committed Nov 28, 2023
1 parent 5c5ae5f commit e4aa15a
Show file tree
Hide file tree
Showing 45 changed files with 118 additions and 704 deletions.
3 changes: 2 additions & 1 deletion app/controllers/concerns/set_current_request_details.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ def account_from_param
current_user.accounts.includes(:payment_processor, :users).find_by(id: account_id)
end

# Returns an account sorting by personal accounts and oldest account first
def fallback_account
return unless user_signed_in?
current_user.accounts.includes(:payment_processor, :users).order(created_at: :asc).first || current_user.create_default_account
current_user.accounts.includes(:payment_processor, :users).order(personal: :desc, created_at: :asc).first || current_user.create_default_account
end
end
2 changes: 1 addition & 1 deletion app/controllers/subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def require_payments_enabled
end

def set_plan
@plan = Plan.without_free.find_by_prefix_id!(params[:plan])
@plan = Plan.visible.find_by_prefix_id!(params[:plan])
rescue ActiveRecord::RecordNotFound
redirect_to pricing_path
end
Expand Down
25 changes: 0 additions & 25 deletions app/models/account.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,3 @@
# == Schema Information
#
# Table name: accounts
#
# id :bigint not null, primary key
# account_users_count :integer default(0)
# billing_email :string
# domain :string
# extra_billing_info :text
# name :string not null
# personal :boolean default(FALSE)
# subdomain :string
# created_at :datetime not null
# updated_at :datetime not null
# owner_id :bigint
#
# Indexes
#
# index_accounts_on_owner_id (owner_id)
#
# Foreign Keys
#
# fk_rails_... (owner_id => users.id)
#

class Account < ApplicationRecord
RESERVED_DOMAINS = [Jumpstart.config.domain]
RESERVED_SUBDOMAINS = %w[app help support]
Expand Down
25 changes: 0 additions & 25 deletions app/models/account_invitation.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,3 @@
# == Schema Information
#
# Table name: account_invitations
#
# id :bigint not null, primary key
# email :string not null
# name :string not null
# roles :jsonb not null
# token :string not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
# invited_by_id :bigint
#
# Indexes
#
# index_account_invitations_on_account_id (account_id)
# index_account_invitations_on_invited_by_id (invited_by_id)
# index_account_invitations_on_token (token) UNIQUE
#
# Foreign Keys
#
# fk_rails_... (account_id => accounts.id)
# fk_rails_... (invited_by_id => users.id)
#
class AccountInvitation < ApplicationRecord
ROLES = AccountUser::ROLES

Expand Down
22 changes: 0 additions & 22 deletions app/models/account_user.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
# == Schema Information
#
# Table name: account_users
#
# id :bigint not null, primary key
# roles :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint
# user_id :bigint
#
# Indexes
#
# index_account_users_on_account_id (account_id)
# index_account_users_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (account_id => accounts.id)
# fk_rails_... (user_id => users.id)
#

class AccountUser < ApplicationRecord
# Add account roles to this line
# Do NOT to use any reserved words like `user` or `account`
Expand Down
10 changes: 0 additions & 10 deletions app/models/action_text/embed.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
# == Schema Information
#
# Table name: action_text_embeds
#
# id :bigint not null, primary key
# fields :jsonb
# url :string
# created_at :datetime not null
# updated_at :datetime not null
#
class ActionText::Embed < ApplicationRecord
include ActionText::Attachable

Expand Down
21 changes: 0 additions & 21 deletions app/models/address.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
# == Schema Information
#
# Table name: addresses
#
# id :bigint not null, primary key
# address_type :integer
# addressable_type :string not null
# city :string
# country :string
# line1 :string
# line2 :string
# postal_code :string
# state :string
# created_at :datetime not null
# updated_at :datetime not null
# addressable_id :bigint not null
#
# Indexes
#
# index_addresses_on_addressable (addressable_type,addressable_id)
#
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true

Expand Down
12 changes: 0 additions & 12 deletions app/models/announcement.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
# == Schema Information
#
# Table name: announcements
#
# id :bigint not null, primary key
# kind :string
# published_at :datetime
# title :string
# created_at :datetime not null
# updated_at :datetime not null
#

class Announcement < ApplicationRecord
TYPES = %w[new fix improvement update]

Expand Down
25 changes: 0 additions & 25 deletions app/models/api_token.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,3 @@
# == Schema Information
#
# Table name: api_tokens
#
# id :bigint not null, primary key
# expires_at :datetime
# last_used_at :datetime
# metadata :jsonb
# name :string
# token :string
# transient :boolean default(FALSE)
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint not null
#
# Indexes
#
# index_api_tokens_on_token (token) UNIQUE
# index_api_tokens_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#

class ApiToken < ApplicationRecord
DEFAULT_NAME = I18n.t("api_tokens.default")
APP_NAME = I18n.t("api_tokens.app")
Expand Down
22 changes: 0 additions & 22 deletions app/models/connected_account.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
# == Schema Information
#
# Table name: connected_accounts
#
# id :bigint not null, primary key
# access_token :string
# access_token_secret :string
# auth :text
# expires_at :datetime
# owner_type :string
# provider :string
# refresh_token :string
# uid :string
# created_at :datetime not null
# updated_at :datetime not null
# owner_id :bigint
#
# Indexes
#
# index_connected_accounts_on_owner_id_and_owner_type (owner_id,owner_type)
#

class ConnectedAccount < ApplicationRecord
serialize :auth, coder: JSON

Expand Down
10 changes: 0 additions & 10 deletions app/models/inbound_webhook.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
# == Schema Information
#
# Table name: inbound_webhooks
#
# id :bigint not null, primary key
# body :text
# status :integer default("pending"), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class InboundWebhook < ApplicationRecord
cattr_accessor :incinerate_after, default: 7.days
enum status: %i[pending processing processed failed]
Expand Down
20 changes: 0 additions & 20 deletions app/models/notification.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
# == Schema Information
#
# Table name: notifications
#
# id :bigint not null, primary key
# interacted_at :datetime
# params :jsonb
# read_at :datetime
# recipient_type :string not null
# type :string
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
# recipient_id :bigint not null
#
# Indexes
#
# index_notifications_on_account_id (account_id)
# index_notifications_on_recipient_type_and_recipient_id (recipient_type,recipient_id)
#
class Notification < ApplicationRecord
include Noticed::Model

Expand Down
15 changes: 0 additions & 15 deletions app/models/notification_token.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
# == Schema Information
#
# Table name: notification_tokens
#
# id :bigint not null, primary key
# platform :string not null
# token :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint
#
# Indexes
#
# index_notification_tokens_on_user_id (user_id)
#
class NotificationToken < ApplicationRecord
# Tokens for sending push notifications to mobile devices

Expand Down
35 changes: 7 additions & 28 deletions app/models/plan.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
# == Schema Information
#
# Table name: plans
#
# id :bigint not null, primary key
# amount :integer default(0), not null
# charge_per_unit :boolean
# currency :string
# description :string
# details :jsonb not null
# hidden :boolean
# interval :string not null
# interval_count :integer default(1)
# name :string not null
# trial_period_days :integer default(0)
# unit_label :string
# created_at :datetime not null
# updated_at :datetime not null
#

class Plan < ApplicationRecord
# Generates hash IDs with a friendly prefix so users can't guess hidden plan IDs on checkout
# https://github.com/excid3/prefixed_ids
has_prefix_id :plan

store_accessor :details, :features, :stripe_id, :braintree_id, :paddle_billing_id, :paddle_classic_id, :jumpstart_id, :fake_processor_id, :stripe_tax
attribute :features, :string, array: true
store_accessor :details, :features, :stripe_tax
attribute :currency, default: "usd"
normalizes :currency, with: ->(currency) { currency.downcase }

Expand All @@ -34,14 +13,14 @@ class Plan < ApplicationRecord
validates :trial_period_days, numericality: {only_integer: true}
validates :unit_label, presence: {if: :charge_per_unit?}

scope :hidden, -> { unscope(where: :hidden).where(hidden: true) }
scope :monthly, -> { without_free.where(interval: :month) }
scope :sorted, -> { order(amount: :asc) }
scope :hidden, -> { where(hidden: true) }
scope :visible, -> { where(hidden: [nil, false]) }
scope :with_hidden, -> { unscope(where: :hidden) }
scope :without_free, -> { where.not("details @> ?", {fake_processor_id: :free}.to_json) }
scope :yearly, -> { without_free.where(interval: :year) }
scope :without_free, -> { where.not(fake_processor_id: :free) }
scope :monthly, -> { where(interval: :month) }
scope :yearly, -> { where(interval: :year) }
scope :sorted, -> { order(amount: :asc) }

# Returns a free plan for the Fake Processor
def self.free
plan = where(name: "Free").first_or_initialize
plan.update(hidden: true, amount: 0, currency: :usd, interval: :month, trial_period_days: 0, fake_processor_id: :free)
Expand Down
61 changes: 11 additions & 50 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,52 +1,3 @@
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# accepted_privacy_at :datetime
# accepted_terms_at :datetime
# admin :boolean
# announcements_read_at :datetime
# confirmation_sent_at :datetime
# confirmation_token :string
# confirmed_at :datetime
# email :string default(""), not null
# encrypted_password :string default(""), not null
# first_name :string
# invitation_accepted_at :datetime
# invitation_created_at :datetime
# invitation_limit :integer
# invitation_sent_at :datetime
# invitation_token :string
# invitations_count :integer default(0)
# invited_by_type :string
# last_name :string
# last_otp_timestep :integer
# name :string
# otp_backup_codes :text
# otp_required_for_login :boolean
# otp_secret :string
# preferences :jsonb
# preferred_language :string
# remember_created_at :datetime
# reset_password_sent_at :datetime
# reset_password_token :string
# time_zone :string
# unconfirmed_email :string
# created_at :datetime not null
# updated_at :datetime not null
# invited_by_id :bigint
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_invitation_token (invitation_token) UNIQUE
# index_users_on_invitations_count (invitations_count)
# index_users_on_invited_by_id (invited_by_id)
# index_users_on_invited_by_type_and_invited_by_id (invited_by_type,invited_by_id)
# index_users_on_reset_password_token (reset_password_token) UNIQUE
#

class User < ApplicationRecord
include ActionText::Attachable
include TwoFactorAuthentication
Expand Down Expand Up @@ -84,7 +35,17 @@ class User < ApplicationRecord
# Replace with a search engine like Meilisearch, ElasticSearch, or pg_search to provide better results
# Using arel matches allows for database agnostic like queries
def self.search(query)
where(arel_table[:name].matches("%#{sanitize_sql_like(query)}%"))
case connection.adapter_name
when "SQLite"
first_name, last_name = query.split(" ", 2)
if last_name.present?
where(arel_table[:first_name].matches("%#{sanitize_sql_like(first_name)}%")).where(arel_table[:last_name].matches("%#{sanitize_sql_like(last_name)}%"))
else
where(arel_table[:first_name].matches("%#{sanitize_sql_like(query)}%")).or(where(arel_table[:last_name].matches("%#{sanitize_sql_like(query)}%")))
end
else
where(arel_table[:name].matches("%#{sanitize_sql_like(query)}%"))
end
end

# When ActionText rendering mentions in plain text
Expand Down
Loading

0 comments on commit e4aa15a

Please sign in to comment.