diff --git a/Gemfile b/Gemfile index 4ceebcc..a8bcce6 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,7 @@ gem "ruby-vips", "~> 2.0", require: false group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" - + gem "pry" # Audits gems for known security defects (use config/bundler-audit.yml to ignore issues) gem "bundler-audit", require: false diff --git a/Gemfile.lock b/Gemfile.lock index d923c23..402d23f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,6 +81,7 @@ GEM base64 (0.3.0) bcrypt (3.1.22) bcrypt_pbkdf (1.1.2) + bcrypt_pbkdf (1.1.2-arm64-darwin) bigdecimal (4.1.2) bindex (0.8.1) bootsnap (1.24.6) @@ -100,6 +101,7 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) + coderay (1.1.3) concurrent-ruby (1.3.8) connection_pool (3.0.2) crass (1.0.7) @@ -174,6 +176,7 @@ GEM net-smtp marcel (1.2.1) matrix (0.4.3) + method_source (1.1.0) mini_mime (1.1.5) minitest (6.0.6) drb (~> 2.0) @@ -222,6 +225,10 @@ GEM actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack + pry (0.16.0) + coderay (~> 1.1) + method_source (~> 1.0) + reline (>= 0.6.0) public_suffix (7.0.5) puma (8.0.2) nio4r (~> 2.0) @@ -396,6 +403,7 @@ PLATFORMS aarch64-linux-musl arm-linux-gnu arm-linux-musl + arm64-darwin-22 arm64-darwin-23 arm64-darwin-25 x86_64-linux @@ -414,6 +422,7 @@ DEPENDENCIES jbuilder kamal propshaft + pry puma (>= 5.0) rack-cors rails (~> 8.1.3) @@ -448,6 +457,7 @@ CHECKSUMS base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032 bcrypt_pbkdf (1.1.2) sha256=c2414c23ce66869b3eb9f643d6a3374d8322dfb5078125c82792304c10b94cf6 + bcrypt_pbkdf (1.1.2-arm64-darwin) sha256=afdd6feb6ed5a97b8e44caacb3f2d641b98af78e6a516d4a3520b69af5cf9fea bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd bindex (0.8.1) sha256=7b1ecc9dc539ed8bccfc8cb4d2732046227b09d6f37582ff12e50a5047ceb17e bootsnap (1.24.6) sha256=c60bab88c70332290f0a2636a288f675299eb4f804a02a3c085b42eca9da164a @@ -455,6 +465,7 @@ CHECKSUMS builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef + coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1 connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295 @@ -491,6 +502,7 @@ CHECKSUMS mail (2.9.1) sha256=06574eca475253d6c18145dd70af80d0eb970182d55053497c5f4d797ea160e8 marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b + method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1 msgpack (1.8.3) sha256=8bda4a6428d3244e50d6bd55854d354edbada88a4e1f4f5731a39a0f86bee6a1 @@ -517,6 +529,7 @@ CHECKSUMS prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 propshaft (1.3.2) sha256=1d56a3e56a92c21bfc29caf07406b5386b00d4c47ddf357cf989a5a234b1389e + pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb raabro (1.5.0) sha256=3f998a7bc84f9c84df3ab580634d2e0a5bda4f0841168d56035f529c9877440a diff --git a/app/controllers/addresses_controller.rb b/app/controllers/addresses_controller.rb new file mode 100644 index 0000000..ddfba73 --- /dev/null +++ b/app/controllers/addresses_controller.rb @@ -0,0 +1,56 @@ +class AddressesController < ApplicationController + before_action :set_person + before_action :set_address, only: %i[ show update destroy ] + + def show + end + + def create + @address = @person.build_address(address_params) + + respond_to do |format| + if @address.save + format.html { redirect_to person_address_url(@person), notice: "Address was successfully created." } + format.json { render :show, status: :created, location: person_address_url(@person) } + else + format.html { render :new, status: :unprocessable_content } + format.json { render json: @address.errors, status: :unprocessable_content } + end + end + end + + def update + respond_to do |format| + if @address.update(address_params) + format.html { redirect_to person_address_url(@person), notice: "Address was successfully updated.", status: :see_other } + format.json { render :show, status: :ok, location: person_address_url(@person) } + else + format.html { render :edit, status: :unprocessable_content } + format.json { render json: @address.errors, status: :unprocessable_content } + end + end + end + + def destroy + @address.destroy! + + respond_to do |format| + format.html { redirect_to person_url(@person), notice: "Address was successfully destroyed.", status: :see_other } + format.json { head :no_content } + end + end + + private + + def set_person + @person = Person.find(params[:person_id]) + end + + def set_address + @address = @person.address + end + + def address_params + params.expect(address: [ :city, :state ]) + end +end diff --git a/app/models/address.rb b/app/models/address.rb new file mode 100644 index 0000000..d7153de --- /dev/null +++ b/app/models/address.rb @@ -0,0 +1,3 @@ +class Address < ApplicationRecord + belongs_to :person +end diff --git a/app/models/person.rb b/app/models/person.rb index 9791ac6..e8c2489 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -1,3 +1,5 @@ class Person < ApplicationRecord belongs_to :relationship + + has_one :address, dependent: :destroy end diff --git a/app/views/addresses/_address.html.erb b/app/views/addresses/_address.html.erb new file mode 100644 index 0000000..1f0a08b --- /dev/null +++ b/app/views/addresses/_address.html.erb @@ -0,0 +1,12 @@ +
+
+ City: + <%= address.city %> +
+ +
+ State: + <%= address.state %> +
+ +
diff --git a/app/views/addresses/_address.json.jbuilder b/app/views/addresses/_address.json.jbuilder new file mode 100644 index 0000000..95169b4 --- /dev/null +++ b/app/views/addresses/_address.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! address, :id, :city, :state, :created_at, :updated_at +json.url address_url(address, format: :json) diff --git a/app/views/addresses/index.html.erb b/app/views/addresses/index.html.erb new file mode 100644 index 0000000..a7f798a --- /dev/null +++ b/app/views/addresses/index.html.erb @@ -0,0 +1,16 @@ +

<%= notice %>

+ +<% content_for :title, "Addresses" %> + +

Addresses

+ +
+ <% @addresses.each do |address| %> + <%= render address %> +

+ <%= link_to "Show this address", address %> +

+ <% end %> +
+ +<%= link_to "New address", new_address_path %> diff --git a/app/views/addresses/index.json.jbuilder b/app/views/addresses/index.json.jbuilder new file mode 100644 index 0000000..475be0e --- /dev/null +++ b/app/views/addresses/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @addresses, partial: "addresses/address", as: :address diff --git a/app/views/addresses/show.html.erb b/app/views/addresses/show.html.erb new file mode 100644 index 0000000..dd36790 --- /dev/null +++ b/app/views/addresses/show.html.erb @@ -0,0 +1,9 @@ +

<%= notice %>

+ +<%= render @address %> + +
+ <%= link_to "Back to person", @person %> + + <%= button_to "Destroy this address", person_address_path(@person), method: :delete %> +
diff --git a/app/views/addresses/show.json.jbuilder b/app/views/addresses/show.json.jbuilder new file mode 100644 index 0000000..8d0edad --- /dev/null +++ b/app/views/addresses/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "addresses/address", address: @address diff --git a/config/routes.rb b/config/routes.rb index 499ad5c..bce874b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,9 @@ devise_for :users resources :medications resources :medication_types - resources :people + resources :people do + resource :address, only: [ :show, :create, :update, :destroy ] + end # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # JSON API consumed by the Expo app in mobile/. diff --git a/db/migrate/20260828192329_create_addresses.rb b/db/migrate/20260828192329_create_addresses.rb new file mode 100644 index 0000000..0b35579 --- /dev/null +++ b/db/migrate/20260828192329_create_addresses.rb @@ -0,0 +1,10 @@ +class CreateAddresses < ActiveRecord::Migration[8.1] + def change + create_table :addresses do |t| + t.string :city + t.string :state + + t.timestamps + end + end +end diff --git a/db/migrate/20260829145325_add_person_ref_to_address.rb b/db/migrate/20260829145325_add_person_ref_to_address.rb new file mode 100644 index 0000000..2367ad6 --- /dev/null +++ b/db/migrate/20260829145325_add_person_ref_to_address.rb @@ -0,0 +1,5 @@ +class AddPersonRefToAddress < ActiveRecord::Migration[8.1] + def change + add_reference :addresses, :person, null: false, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index c2e519c..36dd645 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,16 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_08_29_113836) do +ActiveRecord::Schema[8.1].define(version: 2026_08_29_145325) do + create_table "addresses", force: :cascade do |t| + t.string "city" + t.datetime "created_at", null: false + t.integer "person_id", null: false + t.string "state" + t.datetime "updated_at", null: false + t.index ["person_id"], name: "index_addresses_on_person_id" + end + create_table "medication_types", force: :cascade do |t| t.datetime "created_at", null: false t.string "name" @@ -86,6 +95,7 @@ t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true end + add_foreign_key "addresses", "people" add_foreign_key "medications", "medication_types" add_foreign_key "people", "relationships" end diff --git a/test/controllers/addresses_controller_test.rb b/test/controllers/addresses_controller_test.rb new file mode 100644 index 0000000..93dfabf --- /dev/null +++ b/test/controllers/addresses_controller_test.rb @@ -0,0 +1,28 @@ +require "test_helper" + +class AddressesControllerTest < ActionDispatch::IntegrationTest + setup do + @address = addresses(:one) + @person = @address.person + end + + test "should show address" do + get person_address_url(@person) + assert_response :success + end + + test "should create address" do + @person.address.destroy + assert_difference("Address.count") do + post person_address_url(@person), params: { address: { city: @address.city, state: @address.state } } + end + assert_redirected_to person_address_url(@person) + end + + test "should destroy address" do + assert_difference("Address.count", -1) do + delete person_address_url(@person) + end + assert_redirected_to person_url(@person) + end +end diff --git a/test/fixtures/addresses.yml b/test/fixtures/addresses.yml new file mode 100644 index 0000000..3a1770c --- /dev/null +++ b/test/fixtures/addresses.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + city: MyString + state: MyString + person: one + +two: + city: MyString + state: MyString + person: two