diff --git a/.env b/.env deleted file mode 100644 index 5d0bfdf..0000000 --- a/.env +++ /dev/null @@ -1,5 +0,0 @@ -PORT=3000 - -DATABASE=hensei -DATABASE_USER=justin -DATABASE_PASSWORD= diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000..f3876a4 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +granblue diff --git a/Gemfile b/Gemfile index 3adf1fb..ad667b2 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gem 'bootsnap' gem 'pg' gem 'rack-cors' gem 'rails' +gem 'sprockets-rails' # A Ruby Web Server Built For Concurrency gem 'puma' @@ -43,15 +44,23 @@ gem 'pg_search' # Pagination library gem 'will_paginate', '~> 3.3' + gem 'httparty' +# Migrate and update data alongside your database structure. +gem 'data_migrate' + +# A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model +gem 'amoeba' + + group :doc do gem 'apipie-rails' gem 'sdoc' end group :development, :test do - gem 'awesome_print' + gem 'amazing_print' gem 'dotenv-rails' gem 'factory_bot_rails' gem 'faker' diff --git a/Gemfile.lock b/Gemfile.lock index 5fba3a4..5c87377 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,259 +1,285 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + actioncable (7.0.4.1) + actionpack (= 7.0.4.1) + activesupport (= 7.0.4.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionmailbox (7.0.4.1) + actionpack (= 7.0.4.1) + activejob (= 7.0.4.1) + activerecord (= 7.0.4.1) + activestorage (= 7.0.4.1) + activesupport (= 7.0.4.1) mail (>= 2.7.1) - actionmailer (6.1.4.1) - actionpack (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activesupport (= 6.1.4.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.4.1) + actionpack (= 7.0.4.1) + actionview (= 7.0.4.1) + activejob (= 7.0.4.1) + activesupport (= 7.0.4.1) mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp rails-dom-testing (~> 2.0) - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) - rack (~> 2.0, >= 2.0.9) + actionpack (7.0.4.1) + actionview (= 7.0.4.1) + activesupport (= 7.0.4.1) + rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.1) - actionpack (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actiontext (7.0.4.1) + actionpack (= 7.0.4.1) + activerecord (= 7.0.4.1) + activestorage (= 7.0.4.1) + activesupport (= 7.0.4.1) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + actionview (7.0.4.1) + activesupport (= 7.0.4.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.4.1) - activesupport (= 6.1.4.1) + activejob (7.0.4.1) + activesupport (= 7.0.4.1) globalid (>= 0.3.6) - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) - activestorage (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activesupport (= 6.1.4.1) - marcel (~> 1.0.0) + activemodel (7.0.4.1) + activesupport (= 7.0.4.1) + activerecord (7.0.4.1) + activemodel (= 7.0.4.1) + activesupport (= 7.0.4.1) + activestorage (7.0.4.1) + actionpack (= 7.0.4.1) + activejob (= 7.0.4.1) + activerecord (= 7.0.4.1) + activesupport (= 7.0.4.1) + marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (6.1.4.1) + activesupport (7.0.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - zeitwerk (~> 2.3) + amazing_print (1.4.0) + amoeba (3.2.0) + activerecord (>= 4.2.0) api_matchers (0.6.2) activesupport (>= 3.2.5) nokogiri (>= 1.5.2) rspec (>= 3.1) - apipie-rails (0.5.19) - rails (>= 4.1) + apipie-rails (0.9.1) + actionpack (>= 5.0) + activesupport (>= 5.0) ast (2.4.2) - awesome_nested_set (3.4.0) - activerecord (>= 4.0.0, < 7.0) - awesome_print (1.9.2) + awesome_nested_set (3.5.0) + activerecord (>= 4.0.0, < 7.1) backport (1.2.0) - bcrypt (3.1.16) - benchmark (0.2.0) + bcrypt (3.1.18) + benchmark (0.2.1) blueprinter (0.25.3) - bootsnap (1.9.1) - msgpack (~> 1.0) + bootsnap (1.15.0) + msgpack (~> 1.2) builder (3.2.4) byebug (11.1.3) coderay (1.1.3) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) crass (1.0.6) + data_migrate (8.5.0) + activerecord (>= 5.0) + railties (>= 5.0) database_cleaner (2.0.1) database_cleaner-active_record (~> 2.0.0) database_cleaner-active_record (2.0.1) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - diff-lcs (1.4.4) + date (3.3.3) + diff-lcs (1.5.0) docile (1.4.0) - doorkeeper (5.5.4) + doorkeeper (5.6.2) railties (>= 5) - dotenv (2.7.6) - dotenv-rails (2.7.6) - dotenv (= 2.7.6) + dotenv (2.8.1) + dotenv-rails (2.8.1) + dotenv (= 2.8.1) railties (>= 3.2) e2mmap (0.1.0) - email_validator (2.2.3) + email_validator (2.2.4) activemodel - erubi (1.10.0) - factory_bot (6.2.0) + erubi (1.12.0) + factory_bot (6.2.1) activesupport (>= 5.0.0) factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faker (2.19.0) - i18n (>= 1.6, < 2) - ffi (1.15.4) + faker (3.1.0) + i18n (>= 1.8.11, < 2) + ffi (1.15.5) figaro (1.2.0) thor (>= 0.14.0, < 2) - gemoji (3.0.1) + gemoji (4.0.1) gemoji-parser (1.3.1) gemoji (>= 2.1.0) - globalid (0.5.2) + globalid (1.0.1) activesupport (>= 5.0) httparty (0.20.0) mime-types (~> 3.0) multi_xml (>= 0.5.2) - i18n (1.8.10) + i18n (1.12.0) concurrent-ruby (~> 1.0) jaro_winkler (1.5.4) - kramdown (2.3.1) + json (2.6.3) + kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - listen (3.7.0) + listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.12.0) + loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.1) + mail (2.8.0.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp marcel (1.0.2) method_source (1.0.0) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) mini_mime (1.1.2) - mini_portile2 (2.6.1) - minitest (5.14.4) - msgpack (1.4.2) + mini_portile2 (2.8.1) + minitest (5.17.0) + msgpack (1.6.0) multi_xml (0.6.0) + net-imap (0.3.4) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.1) + timeout + net-smtp (0.3.3) + net-protocol nio4r (2.5.8) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) + nokogiri (1.14.0) + mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogiri (1.12.5-arm64-darwin) - racc (~> 1.4) - nokogiri (1.12.5-x86_64-linux) - racc (~> 1.4) - oj (3.13.9) - parallel (1.21.0) - parser (3.0.2.0) + oj (3.13.23) + parallel (1.22.1) + parser (3.2.0.0) ast (~> 2.4.1) - pg (1.2.3) - pg_search (2.3.5) + pg (1.4.5) + pg_search (2.3.6) activerecord (>= 5.2) activesupport (>= 5.2) pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) - puma (5.5.2) + psych (5.0.2) + stringio + puma (6.0.2) nio4r (~> 2.0) - racc (1.6.0) - rack (2.2.3) + racc (1.6.2) + rack (2.2.6.2) rack-cors (1.1.1) rack (>= 2.0.0) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.1.4.1) - actioncable (= 6.1.4.1) - actionmailbox (= 6.1.4.1) - actionmailer (= 6.1.4.1) - actionpack (= 6.1.4.1) - actiontext (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activemodel (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + rack-test (2.0.2) + rack (>= 1.3) + rails (7.0.4.1) + actioncable (= 7.0.4.1) + actionmailbox (= 7.0.4.1) + actionmailer (= 7.0.4.1) + actionpack (= 7.0.4.1) + actiontext (= 7.0.4.1) + actionview (= 7.0.4.1) + activejob (= 7.0.4.1) + activemodel (= 7.0.4.1) + activerecord (= 7.0.4.1) + activestorage (= 7.0.4.1) + activesupport (= 7.0.4.1) bundler (>= 1.15.0) - railties (= 6.1.4.1) - sprockets-rails (>= 2.0.0) + railties (= 7.0.4.1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) - loofah (~> 2.3) - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + rails-html-sanitizer (1.5.0) + loofah (~> 2.19, >= 2.19.1) + railties (7.0.4.1) + actionpack (= 7.0.4.1) + activesupport (= 7.0.4.1) method_source - rake (>= 0.13) + rake (>= 12.2) thor (~> 1.0) - rainbow (3.0.0) + zeitwerk (~> 2.5) + rainbow (3.1.1) rake (13.0.6) - rb-fsevent (0.11.0) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rdoc (6.3.2) - regexp_parser (2.1.1) + rdoc (6.5.0) + psych (>= 4.0.0) + regexp_parser (2.6.2) responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) - reverse_markdown (2.0.0) + reverse_markdown (2.1.1) nokogiri rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.0) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-rails (5.0.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - railties (>= 5.2) - rspec-core (~> 3.10) - rspec-expectations (~> 3.10) - rspec-mocks (~> 3.10) - rspec-support (~> 3.10) - rspec-support (3.10.2) - rspec_junit_formatter (0.4.1) + rspec-support (~> 3.12.0) + rspec-rails (6.0.1) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.11) + rspec-expectations (~> 3.11) + rspec-mocks (~> 3.11) + rspec-support (~> 3.11) + rspec-support (3.12.0) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.22.3) + rubocop (1.43.0) + json (~> 2.3) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.12.0, < 2.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.24.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.12.0) - parser (>= 3.0.1.1) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.24.1) + parser (>= 3.1.1.0) ruby-progressbar (1.11.0) - sdoc (2.2.0) + sdoc (2.6.0) rdoc (>= 5.0) - shoulda-matchers (5.0.0) + shoulda-matchers (5.3.0) activesupport (>= 5.2.0) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - solargraph (0.44.0) + simplecov_json_formatter (0.1.4) + solargraph (0.48.0) backport (~> 1.2) benchmark bundler (>= 1.17.2) @@ -268,42 +294,46 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) - spring (3.0.0) + spring (4.1.1) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (4.0.2) + sprockets (4.2.0) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) - thor (1.1.0) - tilt (2.0.10) - tzinfo (2.0.4) + stringio (3.0.4) + thor (1.2.1) + tilt (2.0.11) + timeout (0.3.1) + tzinfo (2.0.5) concurrent-ruby (~> 1.0) - unicode-display_width (2.1.0) + unicode-display_width (2.4.2) + webrick (1.7.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) will_paginate (3.3.1) - yard (0.9.26) - zeitwerk (2.5.1) + yard (0.9.28) + webrick (~> 1.7.0) + zeitwerk (2.6.6) PLATFORMS - arm64-darwin-21 ruby - x86_64-linux DEPENDENCIES + amazing_print + amoeba api_matchers apipie-rails awesome_nested_set - awesome_print bcrypt blueprinter bootsnap byebug + data_migrate database_cleaner doorkeeper dotenv-rails @@ -331,10 +361,11 @@ DEPENDENCIES solargraph spring spring-commands-rspec + sprockets-rails will_paginate (~> 3.3) RUBY VERSION ruby 3.0.0p0 BUNDLED WITH - 2.4.2 + 2.3.26 diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..e69de29 diff --git a/app/blueprints/api/v1/conflict_blueprint.rb b/app/blueprints/api/v1/conflict_blueprint.rb index eccfab1..fa036e5 100644 --- a/app/blueprints/api/v1/conflict_blueprint.rb +++ b/app/blueprints/api/v1/conflict_blueprint.rb @@ -18,8 +18,8 @@ module Api end view :weapons do - field :conflicts, if: ->(_fn, _obj, options) { options.key?(:conflict_weapons) } do |_, options| - GridWeaponBlueprint.render_as_hash(options[:conflict_weapons], view: :nested) + field :conflicts, if: ->(_fn, _obj, options) { options.key?(:conflict_weapon) } do |_, options| + GridWeaponBlueprint.render_as_hash(options[:conflict_weapon], view: :nested) end field :incoming, if: ->(_fn, _obj, options) { options.key?(:incoming_weapon) } do |_, options| diff --git a/app/blueprints/api/v1/error_blueprint.rb b/app/blueprints/api/v1/error_blueprint.rb index 78a431a..29796c0 100644 --- a/app/blueprints/api/v1/error_blueprint.rb +++ b/app/blueprints/api/v1/error_blueprint.rb @@ -14,6 +14,10 @@ module Api field :errors, if: ->(_field_name, _error, options) { options.key?(:exception) } do |_, options| options[:exception] end + + field :errors, if: ->(_field_name, object, options) { options.key?(:errors) } do |_, options| + options[:errors] + end end end end diff --git a/app/blueprints/api/v1/grid_character_blueprint.rb b/app/blueprints/api/v1/grid_character_blueprint.rb index c753940..4ecf072 100644 --- a/app/blueprints/api/v1/grid_character_blueprint.rb +++ b/app/blueprints/api/v1/grid_character_blueprint.rb @@ -11,11 +11,33 @@ module Api view :nested do fields :position, :uncap_level, :perpetuity + field :transcendence_step, if: lambda { |_fn, obj, _opt| + obj.character.ulb + } do |c| + c.transcendence_step + end + field :awakening do |c| - { - type: c.awakening_type, - level: c.awakening_level - } + c.awakening + end + + field :over_mastery, if: lambda { |_fn, obj, _opt| + !obj.ring1['modifier'].nil? && !obj.ring2['modifier'].nil? + } do |c| + rings = [] + + rings.push(c.ring1) unless c.ring1['modifier'].nil? + rings.push(c.ring2) unless c.ring2['modifier'].nil? + rings.push(c.ring3) unless c.ring3['modifier'].nil? + rings.push(c.ring4) unless c.ring4['modifier'].nil? + + rings + end + + field :aetherial_mastery, if: lambda { |_fn, obj, _opt| + !obj.earring['modifier'].nil? + } do |c| + c.earring end association :character, name: :object, blueprint: CharacterBlueprint @@ -25,6 +47,10 @@ module Api include_view :nested association :party, blueprint: PartyBlueprint, view: :minimal end + + view :destroyed do + fields :position, :created_at, :updated_at + end end end end diff --git a/app/blueprints/api/v1/grid_summon_blueprint.rb b/app/blueprints/api/v1/grid_summon_blueprint.rb index af2cec6..116b7ab 100644 --- a/app/blueprints/api/v1/grid_summon_blueprint.rb +++ b/app/blueprints/api/v1/grid_summon_blueprint.rb @@ -5,11 +5,11 @@ module Api class GridSummonBlueprint < ApiBlueprint view :uncap do association :party, blueprint: PartyBlueprint, view: :minimal - fields :position, :uncap_level + fields :position, :uncap_level, :transcendence_step end view :nested do - fields :main, :friend, :position, :uncap_level + fields :main, :friend, :position, :uncap_level, :transcendence_step association :summon, name: :object, blueprint: SummonBlueprint end @@ -17,6 +17,10 @@ module Api include_view :nested association :party, blueprint: PartyBlueprint, view: :minimal end + + view :destroyed do + fields :main, :friend, :position, :created_at, :updated_at + end end end end diff --git a/app/blueprints/api/v1/grid_weapon_blueprint.rb b/app/blueprints/api/v1/grid_weapon_blueprint.rb index 26c5f25..3e409e8 100644 --- a/app/blueprints/api/v1/grid_weapon_blueprint.rb +++ b/app/blueprints/api/v1/grid_weapon_blueprint.rb @@ -18,7 +18,7 @@ module Api [2, 3, 17, 24].include?(w.weapon.series) } - field :ax, if: ->(_field_name, w, _options) { w.weapon.ax.positive? } do |w| + field :ax, if: ->(_field_name, w, _options) { w.weapon.ax } do |w| [ { modifier: w.ax_modifier1, @@ -43,6 +43,10 @@ module Api include_view :nested association :party, blueprint: PartyBlueprint, view: :minimal end + + view :destroyed do + fields :mainhand, :position, :created_at, :updated_at + end end end end diff --git a/app/blueprints/api/v1/job_accessory_blueprint.rb b/app/blueprints/api/v1/job_accessory_blueprint.rb new file mode 100644 index 0000000..24648bc --- /dev/null +++ b/app/blueprints/api/v1/job_accessory_blueprint.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Api + module V1 + class JobAccessoryBlueprint < ApiBlueprint + field :name do |skill| + { + en: skill.name_en, + ja: skill.name_jp + } + end + + association :job, + name: :job, + blueprint: JobBlueprint + + fields :granblue_id, :rarity, :release_date + end + end +end diff --git a/app/blueprints/api/v1/job_blueprint.rb b/app/blueprints/api/v1/job_blueprint.rb index 319a62b..43fabec 100644 --- a/app/blueprints/api/v1/job_blueprint.rb +++ b/app/blueprints/api/v1/job_blueprint.rb @@ -17,7 +17,7 @@ module Api ] end - fields :row, :ml, :order + fields :granblue_id, :row, :ml, :order end end end diff --git a/app/blueprints/api/v1/party_blueprint.rb b/app/blueprints/api/v1/party_blueprint.rb index db539fd..7424320 100644 --- a/app/blueprints/api/v1/party_blueprint.rb +++ b/app/blueprints/api/v1/party_blueprint.rb @@ -33,7 +33,12 @@ module Api end view :minimal do - fields :name, :element, :shortcode, :favorited, :extra, :created_at, :updated_at + fields :name, :element, :shortcode, :favorited, :extra, + :full_auto, :clear_time, :auto_guard, :created_at, :updated_at + + field :remix do |p| + p.is_remix + end association :raid, blueprint: RaidBlueprint @@ -63,7 +68,18 @@ module Api include_view :characters include_view :job_skills - fields :description, :extra + association :accessory, + blueprint: JobAccessoryBlueprint + fields :description, :charge_attack, :button_count, :turn_count, :chain_count + + association :source_party, + blueprint: PartyBlueprint, + view: :minimal + + # TODO: This should probably be paginated + association :remixes, + blueprint: PartyBlueprint, + view: :collection end view :collection do diff --git a/app/blueprints/api/v1/summon_blueprint.rb b/app/blueprints/api/v1/summon_blueprint.rb index 3db846d..7c9b84a 100644 --- a/app/blueprints/api/v1/summon_blueprint.rb +++ b/app/blueprints/api/v1/summon_blueprint.rb @@ -15,7 +15,8 @@ module Api field :uncap do |w| { flb: w.flb, - ulb: w.ulb + ulb: w.ulb, + xlb: w.xlb } end @@ -24,7 +25,8 @@ module Api min_hp: w.min_hp, max_hp: w.max_hp, max_hp_flb: w.max_hp_flb, - max_hp_ulb: w.max_hp_ulb + max_hp_ulb: w.max_hp_ulb, + max_hp_xlb: w.max_hp_xlb } end @@ -33,7 +35,8 @@ module Api min_atk: w.min_atk, max_atk: w.max_atk, max_atk_flb: w.max_atk_flb, - max_atk_ulb: w.max_atk_ulb + max_atk_ulb: w.max_atk_ulb, + max_atk_xlb: w.max_atk_xlb } end end diff --git a/app/blueprints/api/v1/update_blueprint.rb b/app/blueprints/api/v1/update_blueprint.rb new file mode 100644 index 0000000..d8c831c --- /dev/null +++ b/app/blueprints/api/v1/update_blueprint.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Api + module V1 + class UpdateBlueprint < Blueprinter::Base + fields :version, :update_type, :updated_at + end + end +end diff --git a/app/blueprints/api/v1/weapon_blueprint.rb b/app/blueprints/api/v1/weapon_blueprint.rb index 87c491c..d7701e1 100644 --- a/app/blueprints/api/v1/weapon_blueprint.rb +++ b/app/blueprints/api/v1/weapon_blueprint.rb @@ -12,7 +12,7 @@ module Api fields :granblue_id, :element, :proficiency, :max_level, :max_skill_level, :limit, :rarity, - :series, :ax, :awakening + :series, :ax, :ax_type, :awakening field :uncap do |w| { diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index 087b455..48aa741 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -36,6 +36,11 @@ module Api respond_to :json ##### Methods + # Returns the latest update + def version + render json: UpdateBlueprint.render_as_json(AppUpdate.last) + end + # Assign the current user if the Doorkeeper token isn't nil, then # update the current user's last seen datetime and last IP address # before returning @@ -85,9 +90,9 @@ module Api def render_not_found_response(object) render json: ErrorBlueprint.render(nil, error: { - message: "#{object.capitalize} could not be found", - code: 'not_found' - }), status: :not_found + message: "#{object.capitalize} could not be found", + code: 'not_found' + }), status: :not_found end def render_unauthorized_response diff --git a/app/controllers/api/v1/grid_characters_controller.rb b/app/controllers/api/v1/grid_characters_controller.rb index 31674cd..376cc46 100644 --- a/app/controllers/api/v1/grid_characters_controller.rb +++ b/app/controllers/api/v1/grid_characters_controller.rb @@ -3,50 +3,55 @@ module Api module V1 class GridCharactersController < Api::V1::ApiController + attr_reader :party, :incoming_character, :current_characters + + before_action :find_party, only: :create + before_action :set, only: %i[update destroy] + before_action :check_authorization, only: %i[update destroy] + before_action :find_incoming_character, only: :create + before_action :find_current_characters, only: :create + def create - party = Party.find(character_params[:party_id]) - incoming_character = Character.find(character_params[:character_id]) - - render_unauthorized_response if current_user && (party.user != current_user) - - current_characters = party.characters.map do |c| - Character.find(c.character.id).character_id - end.flatten - - # Check all character ids on incoming character against current characters - conflict_ids = (current_characters & incoming_character.character_id) - - if conflict_ids.length.positive? - # Find conflicting character ids in party characters - conflict_characters = party.characters.filter do |c| - c if (conflict_ids & c.character.character_id).length.positive? - end.flatten - + if !conflict_characters.nil? && conflict_characters.length.positive? # Render a template with the conflicting and incoming characters, # as well as the selected position, so the user can be presented with # a decision. # Up to 3 characters can be removed at the same time - render json: ConflictBlueprint.render(nil, view: :characters, - conflict_characters: conflict_characters, - incoming_character: incoming_character, - incoming_position: character_params[:position]) + conflict_view = render_conflict_view(conflict_characters, incoming_character, character_params[:position]) + render json: conflict_view else - # Replace the grid character in the position if it is already filled + # Destroy the grid character in the position if it is already filled if GridCharacter.where(party_id: party.id, position: character_params[:position]).exists? character = GridCharacter.where(party_id: party.id, position: character_params[:position]).limit(1)[0] - character.character_id = incoming_character.id - - # Otherwise, create a new grid character - else - character = GridCharacter.create!(character_params.merge(party_id: party.id, - character_id: incoming_character.id)) + character.destroy end - render json: GridCharacterBlueprint.render(character, view: :nested), status: :created if character.save! + # Then, create a new grid character + character = GridCharacter.create!(character_params.merge(party_id: party.id, + character_id: incoming_character.id)) + + if character.save! + grid_character_view = render_grid_character_view(character) + render json: grid_character_view, status: :created + end end end + def update + mastery = {} + %i[ring1 ring2 ring3 ring4 earring awakening].each do |key| + value = character_params.to_h[key] + mastery[key] = value unless value.nil? + end + + @character.attributes = character_params.merge(mastery) + + return render json: GridCharacterBlueprint.render(@character, view: :full) if @character.save + + render_validation_error_response(@character) + end + def resolve incoming = Character.find(resolve_params[:incoming]) conflicting = resolve_params[:conflicting].map { |id| GridCharacter.find(id) } @@ -80,25 +85,84 @@ module Api render_unauthorized_response if current_user && (character.party.user != current_user) character.uncap_level = character_params[:uncap_level] + character.transcendence_step = character_params[:transcendence_step] return unless character.save! render json: GridCharacterBlueprint.render(character, view: :nested, root: :grid_character) end # TODO: Implement removing characters - def destroy; end + def destroy + render_unauthorized_response if @character.party.user != current_user + return render json: GridCharacterBlueprint.render(@character, view: :destroyed) if @character.destroy + end private + def conflict_characters + @conflict_characters ||= find_conflict_characters(incoming_character) + end + + def find_conflict_characters(incoming_character) + # Check all character ids on incoming character against current characters + conflict_ids = (current_characters & incoming_character.character_id) + + return unless conflict_ids.length.positive? + + # Find conflicting character ids in party characters + party.characters.filter do |c| + c if (conflict_ids & c.character.character_id).length.positive? + end.flatten + end + + def find_current_characters + # Make a list of all character IDs + @current_characters = party.characters.map do |c| + Character.find(c.character.id).character_id + end.flatten + end + + def set + @character = GridCharacter.find(params[:id]) + end + + def find_incoming_character + @incoming_character = Character.find(character_params[:character_id]) + end + + def find_party + @party = Party.find(character_params[:party_id]) + render_unauthorized_response if current_user && (party.user != current_user) + end + + def check_authorization + render_unauthorized_response if @character.party.user != current_user + end + # Specify whitelisted properties that can be modified. def character_params - params.require(:character).permit(:id, :party_id, :character_id, :position, :uncap_level, :conflicting, - :incoming) + params.require(:character).permit(:id, :party_id, :character_id, :position, + :uncap_level, :transcendence_step, :perpetuity, + ring1: %i[modifier strength], ring2: %i[modifier strength], + ring3: %i[modifier strength], ring4: %i[modifier strength], + earring: %i[modifier strength], awakening: %i[type level]) end def resolve_params params.require(:resolve).permit(:position, :incoming, conflicting: []) end + + def render_conflict_view(conflict_characters, incoming_character, incoming_position) + ConflictBlueprint.render(nil, + view: :characters, + conflict_characters: conflict_characters, + incoming_character: incoming_character, + incoming_position: incoming_position) + end + + def render_grid_character_view(grid_character) + GridCharacterBlueprint.render(grid_character, view: :nested) + end end end end diff --git a/app/controllers/api/v1/grid_summons_controller.rb b/app/controllers/api/v1/grid_summons_controller.rb index cf9ea7d..9d5f32e 100644 --- a/app/controllers/api/v1/grid_summons_controller.rb +++ b/app/controllers/api/v1/grid_summons_controller.rb @@ -3,12 +3,34 @@ module Api module V1 class GridSummonsController < Api::V1::ApiController + before_action :set, only: %w[update destroy] + + attr_reader :party, :incoming_summon + + before_action :find_party, only: :create + before_action :find_incoming_summon, only: :create + def create - party = Party.find(summon_params[:party_id]) - canonical_summon = Summon.find(summon_params[:summon_id]) + # Create the GridSummon with the desired parameters + summon = GridSummon.new + summon.attributes = summon_params.merge(party_id: party.id, summon_id: incoming_summon.id) - render_unauthorized_response if current_user && (party.user != current_user) + if summon.validate + save_summon(summon) + else + handle_conflict(summon) + end + end + def update + @summon.attributes = summon_params + + return render json: GridSummonBlueprint.render(@summon, view: :nested, root: :grid_summon) if @summon.save + + render_validation_error_response(@character) + end + + def save_summon(summon) if (grid_summon = GridSummon.where( party_id: party.id, position: summon_params[:position] @@ -16,8 +38,24 @@ module Api GridSummon.destroy(grid_summon.id) end - summon = GridSummon.create!(summon_params.merge(party_id: party.id, summon_id: canonical_summon.id)) - render json: GridSummonBlueprint.render(summon, view: :nested), status: :created if summon.save! + return unless summon.save + + output = render_grid_summon_view(summon) + render json: output, status: :created + end + + def handle_conflict(summon) + conflict_summon = summon.conflicts(party) + ap conflict_summon + return unless conflict_summon.summon.id == incoming_summon.id + + old_position = conflict_summon.position + conflict_summon.position = summon_params[:position] + + return unless conflict_summon.save + + output = render_grid_summon_view(conflict_summon, old_position) + render json: output end def update_uncap_level @@ -26,19 +64,44 @@ module Api render_unauthorized_response if current_user && (summon.party.user != current_user) summon.uncap_level = summon_params[:uncap_level] + summon.transcendence_step = 0 + return unless summon.save! render json: GridSummonBlueprint.render(summon, view: :nested, root: :grid_summon) end - # TODO: Implement removing summons - def destroy; end + def destroy + render_unauthorized_response if @summon.party.user != current_user + return render json: GridSummonBlueprint.render(@summon, view: :destroyed) if @summon.destroy + end private + def find_incoming_summon + @incoming_summon = Summon.find_by(id: summon_params[:summon_id]) + end + + def find_party + # BUG: I can create grid weapons even when I'm not logged in on an authenticated party + @party = Party.find(summon_params[:party_id]) + render_unauthorized_response if current_user && (party.user != current_user) + end + + def render_grid_summon_view(grid_summon, conflict_position = nil) + GridSummonBlueprint.render(grid_summon, view: :nested, + root: :grid_summon, + meta: { replaced: conflict_position }) + end + + def set + @summon = GridSummon.where('id = ?', params[:id]).first + end + # Specify whitelisted properties that can be modified. def summon_params - params.require(:summon).permit(:id, :party_id, :summon_id, :position, :main, :friend, :uncap_level) + params.require(:summon).permit(:id, :party_id, :summon_id, :position, :main, :friend, :uncap_level, + :transcendence_step) end end end diff --git a/app/controllers/api/v1/grid_weapons_controller.rb b/app/controllers/api/v1/grid_weapons_controller.rb index 8ecb573..50f3dae 100644 --- a/app/controllers/api/v1/grid_weapons_controller.rb +++ b/app/controllers/api/v1/grid_weapons_controller.rb @@ -3,72 +3,23 @@ module Api module V1 class GridWeaponsController < Api::V1::ApiController - before_action :set, except: %w[create update_uncap_level destroy] + before_action :set, except: %w[create update_uncap_level] + + attr_reader :party, :incoming_weapon + + before_action :find_party, only: :create + before_action :find_incoming_weapon, only: :create def create - # BUG: I can create grid weapons even when I'm not logged in on an authenticated party - party = Party.find(weapon_params[:party_id]) - render_unauthorized_response if current_user && (party.user != current_user) - - incoming_weapon = Weapon.find(weapon_params[:weapon_id]) - incoming_weapon.limit - - # Set up conflict_position in case it is used - conflict_position = nil - - if [9, 10, 11].include?(weapon_params[:position].to_i) && ![11, 16, 17, 28, 29].include?(incoming_weapon.series) - raise Api::V1::IncompatibleWeaponForPositionError.new(weapon: incoming_weapon) - end - - # 1. If the weapon has a limit - # 2. If the weapon does not match a weapon already in grid - # 3. If the incoming weapon has a limit and other weapons of the same series are in grid - if incoming_weapon.limit && incoming_weapon.limit.positive? - conflict_weapon = party.weapons.find do |weapon| - weapon if incoming_weapon.series == weapon.weapon.series || - ([2, 3].include?(incoming_weapon.series) && [2, 3].include?(weapon.weapon.series)) - end - - if conflict_weapon - if conflict_weapon.weapon.id != incoming_weapon.id - return render json: ConflictBlueprint.render(nil, view: :weapons, - conflict_weapons: [conflict_weapon], - incoming_weapon: incoming_weapon, - incoming_position: weapon_params[:position]) - else - # Destroy the original grid weapon - # TODO: Use conflict_position to alert the client that that position has changed - conflict_position = conflict_weapon.position - GridWeapon.destroy(conflict_weapon.id) - end - end - end - - # Destroy the existing item before adding a new one - if (grid_weapon = GridWeapon.where( - party_id: party.id, - position: weapon_params[:position] - ).first) - GridWeapon.destroy(grid_weapon.id) - end - + # Create the GridWeapon with the desired parameters weapon = GridWeapon.new weapon.attributes = weapon_params.merge(party_id: party.id, weapon_id: incoming_weapon.id) - if weapon.position == -1 - party.element = weapon.weapon.element - party.save! + if weapon.validate + save_weapon(weapon) + else + handle_conflict(weapon) end - - # Render the new weapon and any weapons changed - return unless weapon.save! - - render json: GridWeaponBlueprint.render(weapon, view: :full, - root: :grid_weapon, - meta: { - replaced: conflict_position - }), - status: :created end def resolve @@ -89,7 +40,11 @@ module Api weapon = GridWeapon.create!(party_id: party.id, weapon_id: incoming.id, position: resolve_params[:position], uncap_level: uncap_level) - render json: GridWeaponBlueprint.render(weapon, view: :nested), status: :created if weapon.save! + + if weapon.save + view = render_grid_weapon_view(weapon, resolve_params[:position]) + render json: view, status: :created + end end def update @@ -104,8 +59,10 @@ module Api render json: GridWeaponBlueprint.render(@weapon, view: :nested) if @weapon.update(weapon_params) end - # TODO: Implement removing characters - def destroy; end + def destroy + render_unauthorized_response if @weapon.party.user != current_user + return render json: GridCharacterBlueprint.render(@weapon, view: :destroyed) if @weapon.destroy + end def update_uncap_level weapon = GridWeapon.find(weapon_params[:id]) @@ -121,6 +78,107 @@ module Api private + def check_weapon_compatibility + return if compatible_with_position?(incoming_weapon, weapon_params[:position]) + + raise Api::V1::IncompatibleWeaponForPositionError.new(weapon: incoming_weapon) + end + + # Check if the incoming weapon is compatible with the specified position + def compatible_with_position?(incoming_weapon, position) + false if [9, 10, 11].include?(position.to_i) && ![11, 16, 17, 28, 29].include?(incoming_weapon.series) + true + end + + def conflict_weapon + @conflict_weapon ||= find_conflict_weapon(party, incoming_weapon) + end + + # Find a conflict weapon if one exists + def find_conflict_weapon(party, incoming_weapon) + return unless incoming_weapon.limit + + party.weapons.find do |weapon| + series_match = incoming_weapon.series == weapon.weapon.series + weapon if series_match || opus_or_draconic?(weapon.weapon) && opus_or_draconic?(incoming_weapon) + end + end + + def find_incoming_weapon + @incoming_weapon = Weapon.find_by(id: weapon_params[:weapon_id]) + end + + def find_party + # BUG: I can create grid weapons even when I'm not logged in on an authenticated party + @party = Party.find(weapon_params[:party_id]) + render_unauthorized_response if current_user && (party.user != current_user) + end + + def opus_or_draconic?(weapon) + [2, 3].include?(weapon.series) + end + + # Render the conflict view as a string + def render_conflict_view(conflict_weapon, incoming_weapon, incoming_position) + ConflictBlueprint.render(nil, view: :weapons, + conflict_weapon: conflict_weapon, + incoming_weapon: incoming_weapon, + incoming_position: incoming_position) + end + + def render_grid_weapon_view(grid_weapon, conflict_position) + GridWeaponBlueprint.render(grid_weapon, view: :full, + root: :grid_weapon, + meta: { replaced: conflict_position }) + end + + def save_weapon(weapon) + # Check weapon validation and delete existing grid weapon + # if one already exists at position + if (grid_weapon = GridWeapon.where( + party_id: party.id, + position: weapon_params[:position] + ).first) + GridWeapon.destroy(grid_weapon.id) + end + + # Set the party's element if the grid weapon is being set as mainhand + if weapon.position == -1 + party.element = weapon.weapon.element + party.save! + elsif [9, 10, 11].include?(weapon.position) + party.extra = true + party.save! + end + + # Render the weapon if it can be saved + return unless weapon.save + + output = GridWeaponBlueprint.render(weapon, view: :full, root: :grid_weapon) + render json: output, status: :created + end + + def handle_conflict(weapon) + conflict_weapon = weapon.conflicts(party) + + if conflict_weapon.weapon.id != incoming_weapon.id + # Render conflict view if the underlying canonical weapons + # are not identical + output = render_conflict_view([conflict_weapon], incoming_weapon, weapon_params[:position]) + render json: output + else + # Move the original grid weapon to the new position + # to preserve keys and other modifications + old_position = conflict_weapon.position + conflict_weapon.position = weapon_params[:position] + + if conflict_weapon.save + output = render_grid_weapon_view(conflict_weapon, old_position) + render json: output + end + end + end + def set @weapon = GridWeapon.where('id = ?', params[:id]).first end diff --git a/app/controllers/api/v1/job_accessories_controller.rb b/app/controllers/api/v1/job_accessories_controller.rb new file mode 100644 index 0000000..f38a6c6 --- /dev/null +++ b/app/controllers/api/v1/job_accessories_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Api + module V1 + class JobAccessoriesController < Api::V1::ApiController + def job + accessories = JobAccessory.where('job_id = ?', params[:id]) + render json: JobAccessoryBlueprint.render(accessories) + end + end + end +end diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index db75319..6983d27 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -29,10 +29,10 @@ module Api # Remove extra subskills if necessary if old_job && - %w[1 2 3].include?(old_job.row) && - %w[4 5 ex2].include?(job.row) && - @party.skill1 && @party.skill2 && @party.skill3 && - @party.skill1.sub && @party.skill2.sub && @party.skill3.sub + %w[1 2 3].include?(old_job.row) && + %w[4 5 ex2].include?(job.row) && + @party.skill1 && @party.skill2 && @party.skill3 && + @party.skill1.sub && @party.skill2.sub && @party.skill3.sub @party['skill3_id'] = nil end else @@ -63,8 +63,7 @@ module Api new_skill_ids = new_skill_keys.map { |key| job_params[key] } new_skill_ids.map do |id| skill = JobSkill.find(id) - raise Api::V1::IncompatibleSkillError.new(job: @party.job, skill: skill) if mismatched_skill(@party.job, - skill) + raise Api::V1::IncompatibleSkillError.new(job: @party.job, skill: skill) if mismatched_skill(@party.job, skill) end positions = extract_positions_from_keys(new_skill_keys) @@ -154,7 +153,11 @@ module Api mismatched_base = skill.job.base_job && (job.row != 'ex2' || skill.job.base_job.id != job.base_job.id) && skill.base if %w[4 5 ex2].include?(job.row) - true if mismatched_emp || mismatched_base || mismatched_main + if skill.base && !mismatched_base + false + else + true if mismatched_emp || mismatched_main + end elsif mismatched_emp || mismatched_main true else diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index e6494fb..76c456f 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -8,22 +8,25 @@ module Api before_action :set, only: %w[update destroy] def create - @party = Party.new(shortcode: random_string) - @party.extra = party_params['extra'] + party = Party.new + party.user = current_user if current_user + party.attributes = party_params if party_params - # TODO: Extract this into a different method - job = Job.find(party_params['job_id']) if party_params['job_id'].present? - if job - job_skills = JobSkill.where(job: job.id, main: true) - job_skills.each_with_index do |skill, index| - @party["skill#{index}_id"] = skill.id - end - end + # unless party_params.empty? + # party.attributes = party_params + # + # # TODO: Extract this into a different method + # job = Job.find(party_params['job_id']) if party_params['job_id'].present? + # if job + # job_skills = JobSkill.where(job: job.id, main: true) + # job_skills.each_with_index do |skill, index| + # party["skill#{index}_id"] = skill.id + # end + # end + # end - @party.user = current_user if current_user - - if @party.save! - return render json: PartyBlueprint.render(@party, view: :full, root: :party), + if party.save! + return render json: PartyBlueprint.render(party, view: :full, root: :party), status: :created end @@ -41,6 +44,8 @@ module Api @party.attributes = party_params.except(:skill1_id, :skill2_id, :skill3_id) + # TODO: Validate accessory with job + return render json: PartyBlueprint.render(@party, view: :full, root: :party) if @party.save! render_validation_error_response(@party) @@ -51,6 +56,22 @@ module Api return render json: PartyBlueprint.render(@party, view: :destroyed, root: :checkin) if @party.destroy end + def remix + new_party = @party.amoeba_dup + new_party.attributes = { + user: current_user, + name: remixed_name(@party.name), + source_party: @party + } + + if new_party.save + render json: PartyBlueprint.render(new_party, view: :full, root: :party, + meta: { remix: true }) + else + render_validation_error_response(new_party) + end + end + def index conditions = build_conditions(request.params) @@ -105,7 +126,7 @@ module Api def build_conditions(params) unless params['recency'].blank? start_time = (DateTime.current - params['recency'].to_i.seconds) - .to_datetime.beginning_of_day + .to_datetime.beginning_of_day end {}.tap do |hash| @@ -116,15 +137,31 @@ module Api end end - def random_string - num_chars = 6 - o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten - (0...num_chars).map { o[rand(o.length)] }.join + def remixed_name(name) + blanked_name = { + en: name.blank? ? 'Untitled team' : name, + ja: name.blank? ? '無名の編成' : name + } + + if current_user + case current_user.language + when 'en' + "Remix of #{blanked_name[:en]}" + when 'ja' + "#{blanked_name[:ja]}のリミックス" + end + else + "Remix of #{blanked_name[:en]}" + end end def set_from_slug @party = Party.where('shortcode = ?', params[:id]).first - @party.favorited = current_user && @party ? @party.is_favorited(current_user) : false + if @party + @party.favorited = current_user && @party ? @party.is_favorited(current_user) : false + else + render_not_found_response('party') + end end def set @@ -132,6 +169,8 @@ module Api end def party_params + return unless params[:party].present? + params.require(:party).permit( :user_id, :extra, @@ -139,10 +178,18 @@ module Api :description, :raid_id, :job_id, + :accessory_id, :skill0_id, :skill1_id, :skill2_id, - :skill3_id + :skill3_id, + :full_auto, + :auto_guard, + :charge_attack, + :clear_time, + :button_count, + :turn_count, + :chain_count ) end end diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index f259737..0ac50fe 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -40,30 +40,33 @@ module Api end def show - render_not_found_response('user') unless @user + if @user.nil? + render_not_found_response('user') + else - conditions = build_conditions(request.params) - conditions[:user_id] = @user.id + conditions = build_conditions(request.params) + conditions[:user_id] = @user.id - parties = Party - .where(conditions) - .order(created_at: :desc) - .paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE) - .each do |party| - party.favorited = current_user ? party.is_favorited(current_user) : false + parties = Party + .where(conditions) + .order(created_at: :desc) + .paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE) + .each do |party| + party.favorited = current_user ? party.is_favorited(current_user) : false + end + + count = Party.where(conditions).count + + render json: UserBlueprint.render(@user, + view: :profile, + root: 'profile', + parties: parties, + meta: { + count: count, + total_pages: count.to_f / COLLECTION_PER_PAGE > 1 ? (count.to_f / COLLECTION_PER_PAGE).ceil : 1, + per_page: COLLECTION_PER_PAGE + }) end - - count = Party.where(conditions).count - - render json: UserBlueprint.render(@user, - view: :profile, - root: 'profile', - parties: parties, - meta: { - count: count, - total_pages: count.to_f / COLLECTION_PER_PAGE > 1 ? (count.to_f / COLLECTION_PER_PAGE).ceil : 1, - per_page: COLLECTION_PER_PAGE - }) end def check_email @@ -81,7 +84,7 @@ module Api def build_conditions(params) unless params['recency'].blank? start_time = (DateTime.current - params['recency'].to_i.seconds) - .to_datetime.beginning_of_day + .to_datetime.beginning_of_day end {}.tap do |hash| diff --git a/app/models/app_update.rb b/app/models/app_update.rb new file mode 100644 index 0000000..b1ee7d2 --- /dev/null +++ b/app/models/app_update.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class AppUpdate < ApplicationRecord + +end diff --git a/app/models/grid_character.rb b/app/models/grid_character.rb index 2e3e16b..7b5a8fb 100644 --- a/app/models/grid_character.rb +++ b/app/models/grid_character.rb @@ -1,7 +1,78 @@ # frozen_string_literal: true class GridCharacter < ApplicationRecord - belongs_to :party + belongs_to :party, + counter_cache: :characters_count, + inverse_of: :characters + validates_presence_of :party + + validate :awakening_level, on: :update + validate :transcendence, on: :update + validate :validate_over_mastery_values, on: :update + validate :validate_aetherial_mastery_value, on: :update + validate :over_mastery_attack_matches_hp, on: :update + + ##### Amoeba configuration + amoeba do + set ring1: { modifier: nil, strength: nil } + set ring2: { modifier: nil, strength: nil } + set ring3: { modifier: nil, strength: nil } + set ring4: { modifier: nil, strength: nil } + set earring: { modifier: nil, strength: nil } + set perpetuity: false + end + + def awakening_level + return if awakening.nil? + + errors.add(:awakening, 'awakening level too low') if awakening['level'] < 1 + errors.add(:awakening, 'awakening level too high') if awakening['level'] > 9 + end + + def transcendence + errors.add(:transcendence_step, 'character has no transcendence') if transcendence_step.positive? && !character.ulb + errors.add(:transcendence_step, 'transcendence step too high') if transcendence_step > 5 && character.ulb + errors.add(:transcendence_step, 'transcendence step too low') if transcendence_step.negative? && character.ulb + end + + def over_mastery_attack + errors.add(:ring1, 'invalid value') unless ring1['modifier'].nil? || atk_values.include?(ring1['strength']) + end + + def over_mastery_hp + return if ring2['modifier'].nil? + + errors.add(:ring2, 'invalid value') unless hp_values.include?(ring2['strength']) + end + + def over_mastery_attack_matches_hp + return if ring1[:modifier].nil? && ring2[:modifier].nil? + + return if ring2[:strength] == (ring1[:strength] / 2) + + errors.add(:over_mastery, + 'over mastery attack and hp values do not match') + end + + def validate_over_mastery_values + [ring1, ring2, ring3, ring4].each_with_index do |ring, index| + next if ring['modifier'].nil? + + modifier = over_mastery_modifiers[ring['modifier']] + check_value({ "ring#{index}": { ring[modifier] => ring['strength'] } }, + 'over_mastery') + end + end + + def validate_aetherial_mastery_value + return if earring['modifier'].nil? + + return unless earring['modifier'].positive? + + modifier = aetherial_mastery_modifiers[earring['modifier']].to_sym + check_value({ "earring": { modifier => earring['strength'] } }, + 'aetherial_mastery') + end def character Character.find(character_id) @@ -10,4 +81,132 @@ class GridCharacter < ApplicationRecord def blueprint GridCharacterBlueprint end + + private + + def check_value(property, type) + # Input format + # { ring1: { atk: 300 } } + + key = property.keys.first + modifier = property[key].keys.first + + return if modifier.nil? + + case type + when 'over_mastery' + errors.add(key, 'invalid value') unless over_mastery_values.include?(key['strength']) + when 'aetherial_mastery' + errors.add(key, 'value too low') if aetherial_mastery_values[modifier][:min] > self[key]['strength'] + errors.add(key, 'value too high') if aetherial_mastery_values[modifier][:max] < self[key]['strength'] + end + end + + def over_mastery_modifiers + { + 1 => 'atk', + 2 => 'hp', + 3 => 'debuff_success', + 4 => 'skill_cap', + 5 => 'ca_dmg', + 6 => 'ca_cap', + 7 => 'stamina', + 8 => 'enmity', + 9 => 'crit', + 10 => 'da', + 11 => 'ta', + 12 => 'def', + 13 => 'heal', + 14 => 'debuff_resist', + 15 => 'dodge' + } + end + + def over_mastery_values + { + atk: [300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000], + hp: [150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500], + debuff_success: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + skill_cap: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ca_dmg: [10, 12, 14, 16, 18, 20, 22, 24, 27, 30], + ca_cap: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + crit: [10, 12, 14, 16, 18, 20, 22, 24, 27, 30], + enmity: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + stamina: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + def: [6, 7, 8, 9, 10, 12, 14, 16, 18, 20], + heal: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30], + debuff_resist: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + dodge: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + da: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ta: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + } + end + + def aetherial_mastery_modifiers + { + 1 => 'da', + 2 => 'ta', + 3 => 'ele_atk', + 4 => 'ele_resist', + 5 => 'stamina', + 6 => 'enmity', + 7 => 'supplemental', + 8 => 'crit', + 9 => 'counter_dodge', + 10 => 'counter_dmg' + } + end + + def aetherial_mastery_values + { + da: { + min: 10, + max: 17 + }, + ta: { + min: 5, + max: 12 + }, + ele_atk: { + min: 15, + max: 22 + }, + ele_resist: { + min: 5, + max: 12 + }, + stamina: { + min: 5, + max: 12 + }, + enmity: { + min: 5, + max: 12 + }, + supplemental: { + min: 5, + max: 12 + }, + crit: { + min: 18, + max: 35 + }, + counter_dodge: { + min: 5, + max: 12 + }, + counter_dmg: { + min: 10, + max: 17 + } + } + end + + def atk_values + [300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000] + end + + def hp_values + [150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500] + end end diff --git a/app/models/grid_summon.rb b/app/models/grid_summon.rb index 9857a9b..c62a33b 100644 --- a/app/models/grid_summon.rb +++ b/app/models/grid_summon.rb @@ -1,7 +1,13 @@ # frozen_string_literal: true class GridSummon < ApplicationRecord - belongs_to :party + belongs_to :party, + counter_cache: :summons_count, + inverse_of: :summons + validates_presence_of :party + + validate :compatible_with_position, on: :create + validate :no_conflicts, on: :create def summon Summon.find(summon_id) @@ -10,4 +16,29 @@ class GridSummon < ApplicationRecord def blueprint GridSummonBlueprint end + + # Returns conflicting summons if they exist + def conflicts(party) + return unless summon.limit + + party.summons.find do |grid_summon| + return unless grid_summon.id + grid_summon if summon.id == grid_summon.summon.id + end + end + + private + + # Validates whether there is a conflict with the party + def no_conflicts + # Check if the grid summon conflicts with any of the other grid summons in the party + errors.add(:series, 'must not conflict with existing summons') unless conflicts(party).nil? + end + + # Validates whether the summon can be added to the desired position + def compatible_with_position + return unless [4, 5].include?(position.to_i) && !summon.subaura + + errors.add(:position, 'must have subaura for position') + end end diff --git a/app/models/grid_weapon.rb b/app/models/grid_weapon.rb index bd97efb..1048290 100644 --- a/app/models/grid_weapon.rb +++ b/app/models/grid_weapon.rb @@ -2,26 +2,73 @@ class GridWeapon < ApplicationRecord belongs_to :party, - counter_cache: :weapons_count + counter_cache: :weapons_count, + inverse_of: :weapons + validates_presence_of :party belongs_to :weapon_key1, class_name: 'WeaponKey', foreign_key: :weapon_key1_id, optional: true belongs_to :weapon_key2, class_name: 'WeaponKey', foreign_key: :weapon_key2_id, optional: true belongs_to :weapon_key3, class_name: 'WeaponKey', foreign_key: :weapon_key3_id, optional: true + validate :compatible_with_position, on: :create + validate :no_conflicts, on: :create + + ##### Amoeba configuration + amoeba do + nullify :ax_modifier1 + nullify :ax_modifier2 + nullify :ax_strength1 + nullify :ax_strength2 + end + + # Helper methods + def blueprint + GridWeaponBlueprint + end + def weapon Weapon.find(weapon_id) end def weapon_keys - weapon_keys = [] - weapon_keys.push(weapon_key1) unless weapon_key1.nil? - weapon_keys.push(weapon_key2) unless weapon_key2.nil? - weapon_keys.push(weapon_key3) unless weapon_key3.nil? - - weapon_keys + [weapon_key1, weapon_key2, weapon_key3].compact end - def blueprint - GridWeaponBlueprint + # Returns conflicting weapons if they exist + def conflicts(party) + return unless weapon.limit + + party.weapons.find do |party_weapon| + return unless party_weapon.id + + id_match = weapon.id == party_weapon.id + series_match = weapon.series == party_weapon.weapon.series + both_opus_or_draconic = weapon.opus_or_draconic? && party_weapon.weapon.opus_or_draconic? + weapon if (series_match || both_opus_or_draconic) && !id_match + end + end + + private + + # Conflict management methods + + # Validates whether the weapon can be added to the desired position + def compatible_with_position + return unless [9, 10, 11].include?(position.to_i) && ![11, 16, 17, 28, 29].include?(weapon.series) + + errors.add(:series, 'must be compatible with position') + end + + # Validates whether the desired weapon key can be added to the weapon + def compatible_with_key + weapon_keys.each do |key| + errors.add(:weapon_keys, 'must be compatible with weapon') unless weapon.compatible_with_key?(key) + end + end + + # Validates whether there is a conflict with the party + def no_conflicts + # Check if the grid weapon conflicts with any of the other grid weapons in the party + errors.add(:series, 'must not conflict with existing weapons') unless conflicts(party).nil? end end diff --git a/app/models/job_accessory.rb b/app/models/job_accessory.rb new file mode 100644 index 0000000..bf6ba93 --- /dev/null +++ b/app/models/job_accessory.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class JobAccessory < ApplicationRecord + include PgSearch::Model + + belongs_to :job + + pg_search_scope :en_search, + against: :name_en, + using: { + tsearch: { + prefix: true, + dictionary: 'simple' + } + } + + pg_search_scope :jp_search, + against: :name_jp, + using: { + tsearch: { + prefix: true, + dictionary: 'simple' + } + } + + def blueprint + JobAccessoryBlueprint + end + + def display_resource(skill) + skill.name_en + end + + def ==(other) + self.class == other.class && id == other.id + end +end diff --git a/app/models/party.rb b/app/models/party.rb index c90c0c4..6e52a5d 100644 --- a/app/models/party.rb +++ b/app/models/party.rb @@ -2,10 +2,25 @@ class Party < ApplicationRecord ##### ActiveRecord Associations + belongs_to :source_party, + class_name: 'Party', + foreign_key: :source_party_id, + optional: true + + has_many :derivative_parties, + class_name: 'Party', + foreign_key: :source_party_id, + inverse_of: :source_party + belongs_to :user, optional: true belongs_to :raid, optional: true belongs_to :job, optional: true + belongs_to :accessory, + foreign_key: 'accessory_id', + class_name: 'JobAccessory', + optional: true + belongs_to :skill0, foreign_key: 'skill0_id', class_name: 'JobSkill', @@ -29,20 +44,35 @@ class Party < ApplicationRecord has_many :characters, foreign_key: 'party_id', class_name: 'GridCharacter', - dependent: :destroy + dependent: :destroy, + inverse_of: :party has_many :weapons, foreign_key: 'party_id', class_name: 'GridWeapon', - dependent: :destroy + dependent: :destroy, + inverse_of: :party has_many :summons, foreign_key: 'party_id', class_name: 'GridSummon', - dependent: :destroy + dependent: :destroy, + inverse_of: :party has_many :favorites + before_create :set_shortcode + + ##### Amoeba configuration + amoeba do + nullify :description + nullify :shortcode + + include_association :characters + include_association :weapons + include_association :summons + end + ##### ActiveRecord Validations validate :skills_are_unique @@ -52,22 +82,40 @@ class Party < ApplicationRecord user.favorite_parties.include? self end + def is_remix + self.source_party != nil + end + + def remixes + Party.where(source_party_id: self.id) + end + def blueprint PartyBlueprint end private + def set_shortcode + self.shortcode = random_string + end + + def random_string + num_chars = 6 + o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten + (0...num_chars).map { o[rand(o.length)] }.join + end + def skills_are_unique skills = [skill0, skill1, skill2, skill3].compact - return unless skills.uniq.length != skills.length + return if skills.uniq.length == skills.length - errors.add(:skill1, 'must be unique') if skill0 == skill1 + skills.each_with_index do |skill, index| + next if index.zero? - errors.add(:skill2, 'must be unique') if skill0 == skill2 || skill1 == skill2 - - errors.add(:skill3, 'must be unique') if skill0 == skill3 || skill1 == skill3 || skill2 == skill3 + errors.add(:"skill#{index + 1}", 'must be unique') if skills[0...index].include?(skill) + end errors.add(:job_skills, 'must be unique') end diff --git a/app/models/weapon.rb b/app/models/weapon.rb index ed45010..c18a313 100644 --- a/app/models/weapon.rb +++ b/app/models/weapon.rb @@ -27,4 +27,13 @@ class Weapon < ApplicationRecord def display_resource(weapon) weapon.name_en end + + def compatible_with_key?(key) + key.series == series + end + + # Returns whether the weapon is included in the Draconic or Dark Opus series + def opus_or_draconic? + [2, 3].include?(series) + end end diff --git a/bin/rails b/bin/rails index 5badb2f..efc0377 100755 --- a/bin/rails +++ b/bin/rails @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/bin/rake b/bin/rake index d87d5f5..4fbf10b 100755 --- a/bin/rake +++ b/bin/rake @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require_relative '../config/boot' -require 'rake' +require_relative "../config/boot" +require "rake" Rake.application.run diff --git a/bin/setup b/bin/setup index 0e39e8c..ec47b79 100755 --- a/bin/setup +++ b/bin/setup @@ -1,33 +1,33 @@ #!/usr/bin/env ruby -require 'fileutils' +require "fileutils" # path to your application root. -APP_ROOT = File.expand_path('..', __dir__) +APP_ROOT = File.expand_path("..", __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end FileUtils.chdir APP_ROOT do - # This script is a way to setup or update your development environment automatically. - # This script is idempotent, so that you can run it at anytime and get an expectable outcome. + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end puts "\n== Preparing database ==" - system! 'bin/rails db:prepare' + system! "bin/rails db:prepare" puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' + system! "bin/rails log:clear tmp:clear" puts "\n== Restarting application server ==" - system! 'bin/rails restart' + system! "bin/rails restart" end diff --git a/config/application.rb b/config/application.rb index 003ff00..d5011bc 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,4 @@ -require_relative 'boot' +require_relative "boot" require "rails" # Pick the frameworks you want: @@ -7,10 +7,11 @@ require "active_job/railtie" require "active_record/railtie" require "active_storage/engine" require "action_controller/railtie" +# require "action_mailer/railtie" +# require "action_mailbox/engine" require "action_text/engine" require "action_view/railtie" require "action_cable/engine" -# require "sprockets/railtie" require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems @@ -20,12 +21,15 @@ Bundler.require(*Rails.groups) module HenseiApi class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.0 + config.load_defaults 7.0 - # Settings in config/environments/* take precedence over those specified here. - # Application configuration can go into files in config/initializers - # -- all .rb files in that directory are automatically loaded after loading - # the framework and any gems in your application. + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") # Only loads a smaller set of middleware suitable for API only apps. # Middleware like session, flash, cookies can be added back manually. diff --git a/config/boot.rb b/config/boot.rb index b9e460c..988a5dd 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,4 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' # Speed up boot time by caching expensive operations. +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/database.yml b/config/database.yml index 40c39bf..fd320ab 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,13 +1,17 @@ default: &default adapter: postgresql pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - host: <%= ENV['DATABASE_HOST'] %> - port: <%= ENV['DATABASE_PORT'] %> - username: <%= ENV['DATABASE_USERNAME'] %> - password: <%= ENV['DATABASE_PASSWORD'] %> + host: <%= ENV['PGHOST'] %> + port: <%= ENV['PGPORT'] %> + username: <%= ENV['PGUSER'] %> + password: <%= ENV['PGPASSWORD'] %> encoding: utf8 timeout: 5000 +production: + <<: *default + database: <%= ENV['PGDATABASE'] %> + development: <<: *default database: hensei_dev @@ -15,7 +19,3 @@ development: test: <<: *default database: hensei_test - -production: - <<: *default - database: hensei diff --git a/config/environment.rb b/config/environment.rb index 426333b..cac5315 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative 'application' +require_relative "application" # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 6e13811..7b4cd61 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,60 +1,60 @@ +require "active_support/core_ext/integer/time" + Rails.application.configure do - config.after_initialize do - ActiveRecord::Base.logger = Rails.logger.clone - ActiveRecord::Base.logger.level = Logger::INFO - end + # Settings specified here will take precedence over those in config/application.rb. + config.hosts << "staging-api.granblue.team" - # Settings specified here will take precedence over those in config/application.rb. - config.hosts << "grid-api.ngrok.io" + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false + # Do not eager load code on boot. + config.eager_load = false - # Do not eager load code on boot. - config.eager_load = false + # Show full error reports. + config.consider_all_requests_local = true - # Show full error reports. - config.consider_all_requests_local = true + # Enable server timing + config.server_timing = true - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}" - } - else - config.action_controller.perform_caching = false + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.cache_store = :memory_store + config.public_file_server.headers = { + "Cache-Control" => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false - config.cache_store = :null_store - end - - config.action_controller.allow_forgery_protection = false + config.cache_store = :null_store + end - # Store uploaded files on the local file system (see config/storage.yml for options). - config.active_storage.service = :local + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local - # Don't care if the mailer can't send. - # config.action_mailer.raise_delivery_errors = false + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log - # config.action_mailer.perform_caching = false + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] - # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load - # Highlight code that triggered database queries in logs. - config.active_record.verbose_query_logs = true + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true - # Raises error for missing translations. - # config.action_view.raise_on_missing_translations = true + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true end diff --git a/config/environments/production.rb b/config/environments/production.rb index c70ea32..bf24f9a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/integer/time" + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -11,9 +13,7 @@ Rails.application.configure do config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - - config.action_controller.allow_forgery_protection = false + config.consider_all_requests_local = false # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). @@ -21,32 +21,32 @@ Rails.application.configure do # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = "http://assets.example.com" # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil - # config.action_cable.url = 'wss://example.com/cable' - # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -55,53 +55,26 @@ Rails.application.configure do # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "hensei_api_production" - # config.action_mailer.perform_caching = false - - # Ignore bad email addresses and do not raise email delivery errors. - # Set this to true and configure the email server for immediate delivery to raise delivery errors. - # config.action_mailer.raise_delivery_errors = false - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify + # Don't log any deprecations. + config.active_support.report_deprecations = false # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + # require "syslog/logger" + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) + logger = ActiveSupport::Logger.new(STDOUT) logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) + config.logger = ActiveSupport::TaggedLogging.new(logger) end # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false - - # Inserts middleware to perform automatic connection switching. - # The `database_selector` hash is used to pass options to the DatabaseSelector - # middleware. The `delay` is used to determine how long to wait after a write - # to send a subsequent read to the primary. - # - # The `database_resolver` class is used by the middleware to determine which - # database is appropriate to use based on the time delay. - # - # The `database_resolver_context` class is used by the middleware to set - # timestamps for the last write to the primary. The resolver uses the context - # class timestamps to determine how long to wait before reading from the - # replica. - # - # By default Rails will store a last write timestamp in the session. The - # DatabaseSelector middleware is designed as such you can define your own - # strategy for connection switching and pass that into the middleware through - # these configuration options. - # config.active_record.database_selector = { delay: 2.seconds } - # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver - # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session end diff --git a/config/environments/test.rb b/config/environments/test.rb index 0cb2424..8b5160c 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/integer/time" + # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -6,18 +8,18 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - config.cache_classes = false - config.action_view.cache_template_loading = true + # Turn false under Spring and add config.action_view.cache_template_loading = true. + config.cache_classes = true - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV["CI"].present? # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. @@ -34,16 +36,18 @@ Rails.application.configure do # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test - config.action_mailer.perform_caching = false - - # Tell Action Mailer not to deliver emails to the real world. - # The :test delivery method accumulates sent emails in the - # ActionMailer::Base.deliveries array. - config.action_mailer.delivery_method = :test - # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + # Raises error for missing translations. - # config.action_view.raise_on_missing_translations = true + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index cf80657..7d7e544 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -7,10 +7,14 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do - # origins ENV.fetch("CORS_ORIGIN","").split(",") - origins ["127.0.0.1:1234", "app.granblue.team", "hensei-web-production.up.railway.app"] - resource '*', - headers: :any, - methods: [:get, :post, :put, :patch, :delete, :options, :head] + if Rails.env.production? + origins %w[app.granblue.team hensei-web-production.up.railway.app] + else + origins %w[staging.granblue.team 127.0.0.1:1234] + end + + resource "*", + headers: :any, + methods: [:get, :post, :put, :patch, :delete, :options, :head] end end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1..adc6568 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,4 +1,8 @@ # Be sure to restart your server when you modify this file. -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +# Configure parameters to be filtered from the log file. Use this to limit dissemination of +# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported +# notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf..3860f65 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -4,13 +4,13 @@ # are locale specific, and you may define rules for as many different # locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" # inflect.uncountable %w( fish sheep ) # end # These inflection rules are supported but not enabled by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' +# inflect.acronym "RESTful" # end diff --git a/config/initializers/new_framework_defaults_7_0.rb b/config/initializers/new_framework_defaults_7_0.rb new file mode 100644 index 0000000..4d58024 --- /dev/null +++ b/config/initializers/new_framework_defaults_7_0.rb @@ -0,0 +1,135 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 7.0 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `7.0`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +# `button_to` view helper will render `