diff --git a/Gemfile b/Gemfile index 7619f41..3e04279 100644 --- a/Gemfile +++ b/Gemfile @@ -33,9 +33,11 @@ gem 'spring', group: :development # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' - gem 'unicorn' gem 'font-awesome-rails' +gem 'flipper' +gem 'flipper-active_record' +gem 'flipper-ui' gem 'dotenv-rails' gem 'extra_print' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index 9f60f3c..e15cfb1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,6 +167,17 @@ GEM ffi (1.17.2-x86_64-darwin) ffi (1.17.2-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-musl) + flipper (0.26.0) + concurrent-ruby (< 2) + flipper-active_record (0.26.0) + activerecord (>= 4.2, < 8) + flipper (~> 0.26.0) + flipper-ui (0.26.0) + erubi (>= 1.0.0, < 2.0.0) + flipper (~> 0.26.0) + rack (>= 1.4, < 3) + rack-protection (>= 1.5.3, <= 4.0.0) + sanitize (< 7) font-awesome-rails (4.7.0.9) railties (>= 3.2, < 9.0) formatador (1.1.0) @@ -307,14 +318,16 @@ GEM puma (6.5.0) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.3) - rack-session (2.1.1) - base64 (>= 0.1.0) - rack (>= 3.0.0) + rack (2.2.15) + rack-protection (3.0.5) + rack + rack-session (1.0.2) + rack (< 3) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) - rack (>= 3) + rackup (1.0.1) + rack (< 3) + webrick rails (7.1.5.2) actioncable (= 7.1.5.2) actionmailbox (= 7.1.5.2) @@ -392,6 +405,9 @@ GEM marcel (~> 1.0) zeitwerk (~> 2) rubyzip (2.4.1) + sanitize (6.0.2) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) @@ -448,6 +464,7 @@ GEM kgio (~> 2.6) raindrops (~> 0.7) uri (1.0.3) + webrick (1.8.2) websocket (1.2.11) websocket-driver (0.8.0) base64 @@ -477,6 +494,9 @@ DEPENDENCIES extra_print factory_bot_rails faker + flipper + flipper-active_record + flipper-ui font-awesome-rails guard-livereload guard-rspec diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e53f05b..246bcdd 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -27,6 +27,11 @@ def resume # https://github.com/ambethia/recaptcha begin + unless Flipper.enabled?(:resume_download) + Rails.logger.error("Resume_Request Disabled: Email: #{email_param} User: #{@user.errors.full_messages}") + return render(file: "public/412.html", layout: false) + end + if verify_recaptcha(model: @user) && @user.save require 'open-uri' URI.open(I18n.t('resume_link')) do |pdf| diff --git a/app/views/users/resume_recaptcha.html.haml b/app/views/users/resume_recaptcha.html.haml index f1e7a63..ffb8da2 100644 --- a/app/views/users/resume_recaptcha.html.haml +++ b/app/views/users/resume_recaptcha.html.haml @@ -35,7 +35,10 @@ .align-items-center.justify-content-center.d-flex = recaptcha_tags .form-group.mt-3 - = submit_tag 'Download PDF', class: 'btn btn-bootcoder rounded-pill resume-submit' + - if Flipper.enabled?(:resume_download) + = submit_tag 'Download PDF', class: 'btn btn-bootcoder rounded-pill resume-submit' + - else + = button_to('Download Disabled (Flipper)', '412.html', method: :get, class: 'btn btn-warning rounded-pill') - space_rand = rand(1..5) = render partial: "spacer#{space_rand}" diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb new file mode 100644 index 0000000..52c07d6 --- /dev/null +++ b/config/initializers/flipper.rb @@ -0,0 +1,45 @@ +Rails.application.configure do + ## Memoization ensures that only one adapter call is made per feature per request. + ## For more info, see https://www.flippercloud.io/docs/optimization#memoization + # config.flipper.memoize = true + + ## Flipper preloads all features before each request, which is recommended if: + ## * you have a limited number of features (< 100?) + ## * most of your requests depend on most of your features + ## * you have limited gate data combined across all features (< 1k enabled gates, like individual actors, across all features) + ## + ## For more info, see https://www.flippercloud.io/docs/optimization#preloading + # config.flipper.preload = true + + ## Warn or raise an error if an unknown feature is checked + ## Can be set to `:warn`, `:raise`, or `false` + # config.flipper.strict = Rails.env.development? && :warn + + ## Show Flipper checks in logs + # config.flipper.log = true + + ## Reconfigure Flipper to use the Memory adapter and disable Cloud in tests + # config.flipper.test_help = true + + ## The path that Flipper Cloud will use to sync features + # config.flipper.cloud_path = "_flipper" + + ## The instrumenter that Flipper will use. Defaults to ActiveSupport::Notifications. + # config.flipper.instrumenter = ActiveSupport::Notifications +end + +Flipper.configure do |config| + ## Configure other adapters that you want to use here: + ## See http://flippercloud.io/docs/adapters + # config.use Flipper::Adapters::ActiveSupportCacheStore, Rails.cache, expires_in: 5.minutes +end + +## Register a group that can be used for enabling features. +## +## Flipper.enable_group :my_feature, :admins +## +## See https://www.flippercloud.io/docs/features#enablement-group +# +# Flipper.register(:admins) do |actor| +# actor.respond_to?(:admin?) && actor.admin? +# end diff --git a/config/routes.rb b/config/routes.rb index 0bf176e..fccb6b7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,6 +14,11 @@ get 'resume', to: 'users#resume_recaptcha', as: :resume post 'resume', to: 'users#resume' + flipper_constraint = lambda { |request| request.cookies['flippy_flapper'] == 'true' } + constraints flipper_constraint do + mount Flipper::UI.app(Flipper) => '/flippy' + end + # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/db/migrate/20251210133851_create_flipper_tables.rb b/db/migrate/20251210133851_create_flipper_tables.rb new file mode 100644 index 0000000..ceceafa --- /dev/null +++ b/db/migrate/20251210133851_create_flipper_tables.rb @@ -0,0 +1,22 @@ +class CreateFlipperTables < ActiveRecord::Migration[7.1] + def up + create_table :flipper_features do |t| + t.string :key, null: false + t.timestamps null: false + end + add_index :flipper_features, :key, unique: true + + create_table :flipper_gates do |t| + t.string :feature_key, null: false + t.string :key, null: false + t.text :value + t.timestamps null: false + end + add_index :flipper_gates, [:feature_key, :key, :value], unique: true, length: { value: 255 } + end + + def down + drop_table :flipper_gates + drop_table :flipper_features + end +end diff --git a/db/schema.rb b/db/schema.rb index af4d1e0..250b4ba 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2025_10_21_123259) do +ActiveRecord::Schema[7.1].define(version: 2025_12_10_133851) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -22,6 +22,22 @@ t.index ["post_id"], name: "index_comments_on_post_id" end + create_table "flipper_features", force: :cascade do |t| + t.string "key", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_flipper_features_on_key", unique: true + end + + create_table "flipper_gates", force: :cascade do |t| + t.string "feature_key", null: false + t.string "key", null: false + t.text "value" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true + end + create_table "posts", force: :cascade do |t| t.string "name" t.string "email"