20220109のRailsに関する記事は14件です。

Railsのデザインパターン QueryObjectの作り方

はじめに Railsで利用されるデザインパターンの一つにQueryオブジェクトがあります。 複雑なActiveRecordモデルの絞り込みを簡単なクラスに閉じ込めることで、可読性と疎結合を保つことができます。 QueryオブジェクトをRelationに対し結合や絞り込み、ソートなどの操作を定義し、Relationを返すクラスとして定義し、 モデルのscopeとして利用することで、メソッドチェーンが利用できるようになります。 動作環境 Rails 6.1.3 Ruby 2.7.4 前提 レビューに対する通報機能があり、アイテムの詳細ページでアイテムに紐づいたレビューを一覧表示しています。 そして、レビューに対して通報がすると表示がされないように、自分が通報していないレビューの一覧を取得します。 app/model/review.rb class Review < ApplicationRecord belongs_to :product belongs_to :user has_many :reports, dependent: :destroy def self.unreported_reviews(product_id) #誰にも通報されていないレビュー eager_load(:reports).where(product_id: product_id, reports: { id: nil }) end def self.reviews_reported_other(product_id, user_id) #自分以外が通報したレビュー eager_load(:reports).where(product_id: product_id).where.not(reports: { user_id: user_id }) end def self.exclude_reported_reviews(product_id, user_id) #自分が通報していないレビュー self.unreported_reviews(product_id).or(self.reviews_reported_other(product_id, user_id)) end end これだと一つのコントローラーでしか使われない複雑なメソッドができてしまいます。 ここにあるメソッドをQueryオブジェクトとしてモデルから切り離します。 Qureyオブジェクト モデルから切り離すためappディレクトリの下にqureyディレクトリを作成し、ベースとなるQureyクラスを作成し、それを継承したExcludeReportedReviewsQueryクラスを定義します。 app/query/query.eb class Query class << self delegate :call, to: :new end def initialize(relation) @relation = relation end def call raise NotImplementedError end private attr_reader :relation end app/query/exclude_reported_reviews_query.rb class ExcludeReportedReviewsQuery < Query def initialize(relation = Review.all) super(relation) @relation = relation end def call(product_id, user_id) unreported_reviews(product_id).or(reviews_reported_other(product_id, user_id)) end private def unreported_reviews(product_id) @relation.includes(:reports, :user, :product_review_likes).eager_load(:reports).where(product_id: product_id, reports: { id: nil }) end def reviews_reported_other(product_id, user_id) @relation.eager_load(:reports).where(product_id: product_id).where.not(reports: { user_id: user_id }) end end app/model/review.rb class Review < ApplicationRecord belongs_to :product belongs_to :user has_many :reports, dependent: :destroy scope :exclude_reported_reviews, ->(product_id, user_id) { ExcludeReportedReviewsQuery.call(product_id, user_id) } end このように定義することで複雑なクエリをコントローラーやモデルから分離することができました。 ActiveRecordモデルに変更があって影響が少なくなります。 まとめ ひとつの責務をひとつのクラスに切り出し、単一責任の原則を守って設計することで、保守しやすいコードにすることができます。 このようなデザインパターンを使うことでRailsのフットモデルやフットコントローラーの問題を解決することができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails 7.0では`config/initializers`配下のファイルが少なくなっている

概要 rails new直後のconfig/initializers配下の初期化ファイルの数が多くユーザの認知的負荷につながっていました。 その中には必ずしもすべてのアプリケーションに必要とは限らない設定や、過去のアップグレードの中で既に不要になっている可能性がある設定が存在していたため、今回ファイル構成が見直され不要あったと判断されたファイルが削除されました。 rails7.0以前 バージョン7.0以前のrailsではrails new時にアプリケーションの初期設定のファイルがconfig/initializers配下に10ファイル作成されていました。 $ rails -v Rails 6.1.4.4 $ rails new sample_app $ tree config/initializers/ config/initializers/ ├── application_controller_renderer.rb ├── assets.rb ├── backtrace_silencers.rb ├── content_security_policy.rb ├── cookies_serializer.rb ├── filter_parameter_logging.rb ├── inflections.rb ├── mime_types.rb ├── permissions_policy.rb └── wrap_parameters.rb rails7.0 rails7.0では認知的負荷を低減するために初期化ファイルの構成をシンプルにするため、初期化設定の一部をフレームワーク内のデフォルトの設定として定義することでrails new時に生成されるconfig/initializers配下ファイルの数を10ファイルから5ファイルに減らしています。 $ rails -v Rails 7.0.0 $ rails new sample_app $ tree config/initializers/ config/initializers/ ├── assets.rb ├── content_security_policy.rb ├── filter_parameter_logging.rb ├── inflections.rb └── permissions_policy.rb 削除されたファイルの解説 削除された初期化ファイルは以下の5つです。 application_controller_renderer.rb backtrace_silencers.rb cookies_serializer.rb mime_types.rb wrap_parameters.rb 各ファイルについての役割は以下の通りです。 application_controller_renderer.rb コントローラーの以外でviewテンプレートをrenderする際のデフォルト設定値を指定します。 多くのアプリケーションでは不要な設定であることと、少なくともrails new直後の状態では不要なため削除されたようです。 対応されたPR: https://github.com/rails/rails/pull/42538 backtrace_silencers.rb 任意の処理でbacktrace表示を無効化する設定ファイルです。 削除された理由についての詳細は書かれていませんが、このファイルが存在することにより上記の機能が提供されるメリットよりもinitializersファイルが増えてしまうことによる認知的負荷のデメリットの方が大きいと判断がされたようです。 対応されたPR: https://github.com/rails/rails/pull/43237 cookies_serializer.rb cookieのシリアライザの方式を指定するファイルです。バージョン4.1以降のrailsアプリではデフォルトで:marshalが定義されrails new時に作成されるconfig/initializers/cookies_serializer.rbによっての値を:jsonに上書きされていました。 背景としては、過去rails4.0から4.1へのバージョンアップの際に、互換性を維持する為デフォルトの設定は変更せず、新しく作るアプリケーションのみ変更するためにconfig/initializers/cookies_serializer.rbで上書きしてた為です。 rails7になりinitializersファイルを減らすため、デフォルト値が:jsonに変更されconfig/initializers/cookies_serializer.rbは不要になりました。 対応されたPR: https://github.com/rails/rails/pull/42538 mime_types.rb mime typeの追加を行うファイルです。アプリケーション固有のカスタムmime typeをすることも可能です。 削除された理由についての詳細は書かれていませんが、このファイルが存在することにより上記の機能が提供されるメリットよりもinitializersファイルが増えてしまうことによる認知的負荷のデメリットの方が大きいと判断がされたようです。 対応されたPR: https://github.com/rails/rails/pull/43237 wrap_parameters.rb これまでファイルの中で定義されていた以下の設定値がデフォルトの設定になりこのファイルは不要になりました。 ActiveSupport.on_load(:action_controller) do wrap_parameters format: [:json] end wrap_parameter自体についてはこちらの記事でも書いています。 対応されたPR: https://github.com/rails/rails/pull/43237
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第十四章 フォロワー

フォロワー 受動的関係を使ってuser.followersを実装する app/models/user.rb class User < ApplicationRecord has_many :microposts, dependent: :destroy # 他のモデルとの間に「1対多」のつながり # has_many関連付けが使われている場合、 # 「反対側」のモデルでは多くの場合belongs_toが使われます。 # dependent: :destroy # ユーザーが破棄された場合 # 、ユーザーのマイクロポストも同様に破棄される。 has_many :active_relationships, class_name: "Relationship", # :active_relationsshitpsデータモデル 能動的関係(フォローしているがフォローはされていない関係) # class_name: テーブル名 "Relationship" foreign_key: "follower_id", # foreign_key: 外部キー # データベースの二つのテーブルを繋げる # Micropostモデルとrelationshipsモデルを繋げる dependent: :destroy # ユーザーが削除されるとマイクロポストも削除される has_many :passive_relationships, class_name: "Relationship", # passive_relationsshipsデータモデル 受動的関係(フォローされている) # class_name: テーブル名 Relationsship foreign_key: "followed_id", # 外部キー テーブル同士の紐づけに用いるカラムのこと。 dependent: :destroy has_many :following, through: :active_relationships, source: :followed # :following followingデータモデル # has_many :through関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。 # この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。 # フォローするには:active_relationshipsデータモデルが介在する # source: following配列の元はfollowed idの集合である has_many :followers, through: :passive_relationships, source: :follower # folloersデータモデル # userとfollowersの間にはpasseive_relationsshipsが介在する # souce: 関連付けにおける「ソースの」関連付け名、つまり関連付け元の名前を指定します。 # 関連づけ名とは関連付けたいデータモデルの名前 # この場合 :followerが関連付けられる . . . end followersに対するテスト test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end . . . test "should follow and unfollow a user" do michael = users(:michael) # テストユーザーを代入 archer = users(:archer) assert_not michael.following?(archer) # archerはmichaelをふぉろーしていないことを確認 michael.follow(archer) # archerをフォロー assert michael.following?(archer) # フォローしているか? assert archer.followers.include?(michael) # archerのフォロワーの中にmichaelは入っているか? michael.unfollow(archer) assert_not michael.following?(archer) end end テスト ubuntu:~/environment/sample_app (following-users) $ rails t Running via Spring preloader in process 8487 Started with run options --seed 30377 63/63: [==============================] 100% Time: 00:00:08, Time: 00:00:08 Finished in 8.39715s 63 tests, 320 assertions, 0 failures, 0 errors, 0 skips 演習 1. コンソールを開き、何人かのユーザーが最初のユーザーをフォローしている状況を作ってみてください。最初のユーザーをuserとすると、user.followers.map(&:id)の値はどのようになっているでしょうか? >> user = User.first User Load (1.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2021-10-27 07:03:00", updated_at: "2021-10-27 07:03:00", password_digest: [FILTERED], remember_digest: nil, admin: true, activation_digest: "$2a$12$i2cTpO7yNDagp6oMpVyIq.lyvQ1yEuoSJsOj7G9qZaX...", activated: true, activated_at: "2021-10-27 07:03:00", reset_digest: nil, reset_sent_at: nil> >> user2 = User.second User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 1]] => #<User id: 2, name: "Isaias Ebert", email: "example-1@railstutorial.org", created_at: "2021-10-27 07:03:01", updated_at: "2021-10-27 07:03:01", password_digest: [FILTERED], remember_digest: nil, admin: false, activation_digest: "$2a$12$2NxDk9cW24wyZiN3f4LI7OI3D6NSgj7mvZ/huoEFRbE...", activated: true, activated_at: "2021-10-27 07:03:00", reset_digest: nil, reset_sent_at: nil> >> user3 = User.third User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 2]] => #<User id: 3, name: "Freddy Hills Jr.", email: "example-2@railstutorial.org", created_at: "2021-10-27 07:03:01", updated_at: "2021-10-27 07:03:01", password_digest: [FILTERED], remember_digest: nil, admin: false, activation_digest: "$2a$12$LkEsBxgeobFCiJ3ssmWkZ.AxMinGX35D2x4yas988fL...", activated: true, activated_at: "2021-10-27 07:03:01", reset_digest: nil, reset_sent_at: nil> >> user.active_relationships.create(followed_id: user2.id) (0.1ms) begin transaction User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Relationship Create (2.5ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 14:07:57.360763"], ["updated_at", "2021-10-28 14:07:57.360763"]] (7.1ms) commit transaction => #<Relationship id: 2, follower_id: 1, followed_id: 2, created_at: "2021-10-28 14:07:57", updated_at: "2021-10-28 14:07:57"> >> user.active_relationships.create(followed_id: user3.id) (0.1ms) begin transaction User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]] Relationship Create (1.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 3], ["created_at", "2021-10-28 14:08:02.784912"], ["updated_at", "2021-10-28 14:08:02.784912"]] (9.7ms) commit transaction => #<Relationship id: 3, follower_id: 1, followed_id: 3, created_at: "2021-10-28 14:08:02", updated_at: "2021-10-28 14:08:02"> >> user.followers.map(&:id) Traceback (most recent call last): 1: from (irb):17 NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>) Did you mean? follow following >> user.followers.map(&:id) Traceback (most recent call last): 2: from (irb):18 1: from (irb):18:in `rescue in irb_binding' NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>) >> user.followers.count Traceback (most recent call last): 2: from (irb):19 1: from (irb):19:in `rescue in irb_binding' NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>) >> user.followers.map(&:id) Traceback (most recent call last): 2: from (irb):20 1: from (irb):20:in `rescue in irb_binding' NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>) >> user.following.map(&:id) User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]] => [2, 3] 2. 上の演習が終わったら、user.followers.countの実行結果が、先ほどフォローさせたユーザー数と一致していることを確認してみましょう。 >> user.following.count (0.2ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]] => 2 3. user.followers.countを実行した結果、出力されるSQL文はどのような内容になっているでしょうか? また、user.followers.to_a.countの実行結果と違っている箇所はありますか? ヒント: もしuserに100万人のフォロワーがいた場合、どのような違いがあるでしょうか? 考えてみてください。 FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]] => 2 uesrsからrelationshipsに繋がれた。 フォローされているidカラムとフォローしているカラム >> user.following.to_a.count => 2 SQL文が表示されない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第十四章 フォローしているユーザー

フォローしているユーザー Userモデルにfollowingの関連付けを追加する app/models/user.rb class User < ApplicationRecord has_many :microposts, dependent: :destroy # 他のモデルとの間に「1対多」のつながり # has_many関連付けが使われている場合、 # 「反対側」のモデルでは多くの場合belongs_toが使われます。 # dependent: :destroy # ユーザーが破棄された場合 # 、ユーザーのマイクロポストも同様に破棄される。 has_many :active_relationships, class_name: "Relationship", # :active_relationsshitpsデータモデル 能動的関係(フォローしているがフォローはされていない関係) # class_name: テーブル名 "Relationship" foreign_key: "follower_id", # foreign_key: 外部キー # データベースの二つのテーブルを繋げる # Micropostモデルとrelationshipsモデルを繋げる dependent: :destroy # ユーザーが削除されるとマイクロポストも削除される has_many :following, through: :active_relationships, source: :followed # :following followingデータモデル # has_many :through関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。 # この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。 # フォローするには:active_relationshipsデータモデルが介在する # source: following配列の元はfollowed idの集合である . . . end “following” 関連のメソッドをテストする test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end . . . test "should follow and unfollow a user" do michael = users(:michael) # テストユーザーを代入 archer = users(:archer) assert_not michael.following?(archer) # archerはmichaelをふぉろーしていないことを確認 michael.follow(archer) # archerをフォロー assert michael.following?(archer) # フォローしているか? michael.unfollow(archer) assert_not michael.following?(archer) end end "following" 関連のメソッド app/models/user.rb class User < ApplicationRecord . . . # ユーザーをフォローする def follow(other_user) following << other_user # followingは配列になっている # << 指定された obj を自身の末尾に破壊的に追加します。 # following配列にothe_userを破壊的に追加させる end # ユーザーをフォロー解除する def unfollow(other_user) active_relationships.find_by(followed_id: other_user.id).destroy # active_relationshiipsデータモデルからフォロワーをidを探す # それを削除する end # 現在のユーザーがフォローしてたらtrueを返す def following?(other_user) following.include?(other_user) # followingデータモデル # 引数(othe r_user)を配列の名から探す # それがあればtureを返す。 end private . . . end テスト ubuntu:~/environment/sample_app (following-users) $ rails t Running via Spring preloader in process 5103 Started with run options --seed 54684 63/63: [=============================] 100% Time: 00:00:07, Time: 00:00:07 Finished in 7.01682s 63 tests, 319 assertions, 0 failures, 0 errors, 0 skips 演習 1.コンソールを開き、リスト 14.9のコードを順々に実行してみましょう。 >> michael.follow(archer) (0.1ms) begin transaction User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] Relationship Create (1.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 12:59:26.258244"], ["updated_at", "2021-10-28 12:59:26.258244"]] (0.1ms) rollback transaction Traceback (most recent call last): 3: from (irb):5 2: from (irb):5:in `rescue in irb_binding' 1: from app/models/user.rb:135:in `follow' ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: relationships.follower_id, relationships.followed_id) >> michael.following?(archer) User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]] => true >> michael.follow(archer) (0.1ms) begin transaction User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] Relationship Create (0.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 13:00:30.403974"], ["updated_at", "2021-10-28 13:00:30.403974"]] (0.1ms) rollback transaction Traceback (most recent call last): 2: from (irb):7 1: from app/models/user.rb:135:in `follow' ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: relationships.follower_id, relationships.followed_id) # 一意性がないらしい >> michael.following?(archer) User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]] => true >> michael.unfollow(archer) Relationship Load (0.2ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]] (0.0ms) begin transaction Relationship Destroy (6.1ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 1]] (7.7ms) commit transaction => #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2021-10-28 06:05:06", updated_at: "2021-10-28 06:05:06"> >> michael.following?(archer) User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]] => false 2.先ほどの演習の各コマンド実行時の結果を見返してみて、実際にはどんなSQLが出力されたのか確認してみましょう。 SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? わからない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第十三章 画像のリサイズ 本番環境での画像アップロード

画像のリサイズ ユーザーに手元で画像サイズを変更させるのは不便です。 なので、画像を表示させる前にサイズを変更する(リサイズする)ようにしてみましょう。 画像をリサイズするためには、画像を操作するプログラムが必要になります。 開発環境にインストールします。 sudo apt-get -y install imagemagick 画像処理用のgemを追加する Gemfile source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem 'rails', '6.0.3' gem 'image_processing', '1.9.3' gem 'mini_magick', '4.9.5' gem 'active_storage_validations', '0.8.2' . . . $ bundle install 表示用のリサイズ済み画像を追加 app/models/micropost.rb class Micropost < ApplicationRecord . . . # 表示用のリサイズ済み画像を返す def display_image image.variant(resize_to_limit: [500, 500]) # variantメソッドで変換済み画像を作成できる # resize_to_limitオプションを用いて, # 画像の幅や高さが500ピクセルを超えることのないように制約をかけます。 end end リサイズ済みのdisplay_imageを使う app/views/microposts/_micropost.html.erb <li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"><%= micropost.content %></span> <span class="content"> <%= micropost.content %> <%= image_tag micropost.display_image if micropost.image.attached? %> <!--image_tagヘルパーを用いて、関連付けられたmicropost.imageを    描画(レンダリング)できるようになります--> <!--画像がアタッチされていたら画像を表示させる--> <!--image.attached?されたら画像の編集される--> </span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <!--メソッド名の表すとおりですが、「3分前に投稿」といった文字列を出力します。--> <% if current_user?(micropost.user) %> <!--もし自分の投稿だったら--> <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li> 演習 解像度の高い画像をアップロードし、リサイズされているかどうか確認してみましょう。画像が長方形だった場合、リサイズはうまく行われているでしょうか? アイコンが出てきた。 本番環境での画像アップロード AWS用のgemを追加する Gemfile source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem 'rails', '6.0.3' gem 'aws-sdk-s3', '1.46.0', require: false gem 'image_processing', '1.9.3' gem 'mini_magick', '4.9.5' . . . $ bundle install ストレージオプションにAWSを追加する config/storage.yml test: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> amazon: service: S3 access_key_id: <%= ENV['AWS_ACCESS_KEY'] %> secret_access_key: <%= ENV['AWS_SECRET_KEY'] %> region: <%= ENV['AWS_REGION'] %> bucket: <%= ENV['AWS_BUCKET'] %> 本番でAWS S3を使うよう設定する config/environments/production.rb . . . # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session    # アップロードされたファイルをAWSに保存する config.active_storage.service = :amazon end 困ったこと git push heroku masterをすることができなかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsの関連付けのオプションについて

概要 前回のモデルの関連付けの種類の記事の続きです。 モデルの関連付けの際に使用できるオプションについてアウトプットしていきたいと思います。 前回の記事 環境 ruby: '3.0.1' rails: '6.1.4' 前提 今回説明するにあたって下記のモデルを想定しています。 usersテーブル id name 1 user1 2 user2 departmentsテーブル(所属部署を管理するテーブル) id name 1 営業部 2 開発部 Assignmentsテーブル(所属部署を管理するテーブル) id user_id department_id 1 1 1 2 1 2 3 2 1 オプションの種類 1. through throughは多対多の関係のモデル同士を関連付ける際に、そのモデル同士の中間に設置するモデルを経由させる際に使用します。 使い方 models/user.rb class User < ApplicationRecord has_many :assignments # まずは中間のモデルとの関連付け has_many :departments, through: :assignments # 多対多のモデルを関連付ける際にthroughオプションに中間のモデルを記載 end models/assignment.rb class Assignment < ApplicationRecord belongs_to :user # userと従属の関係 belongs_to :department # departmentと従属の関係 end models/department.rb class Department < ApplicationRecord has_many :assignments has_many :users, through: :assignments end 関連付けを行うことで下記のように直接departmentsやusersにアクセスできます。 user = User.find(1) user.departments #=> user1が所属する部署一覧を取得 department = Department.find(1) department.users #=> その部署に所属しているユーザーを取得 <備考> 中間テーブルの必要性 中間テーブルがなぜ必要かを説明します。 結論から言うと、テーブル設計の冗長性をなくすためです。 たとえば、一人のユーザーが営業部と開発部の両方に所属、また営業部には複数のユーザーが所属していると仮定します。 このような場合、テーブルを管理する際に下記のように所属部署が増えるたびにカラムが増えることになります。 usersテーブル id name department_id_1 department_id_2 ・・・ 1 user1 1 2 ・・・ departmentsテーブルも同様に所属ユーザーが増えればカラムが増えてしまいます。 これを解決するために中間テーブルを設置します。 Assignmentsテーブル(所属部署を管理するテーブル) id user_id department_id 1 1 1 2 1 2 3 2 1 ユーザーの所属部署を中間テーブル(Assignmentsテーブル)で管理することで usersテーブルやdepartmentsテーブルでは先程のような冗長性はなくなります。 2. foreign_key foreign_keyは外部キーを変更したい場合に使用するオプションです。 通常関連付けを行うと外部キーは'関連付けモデル名'+_idになります。 これを別の名称に変更することができます。 使い方 models/user.rb class User < ApplicationRecord has_many :assignments, foreign_key: 'user_number' end 上記のように記載することで参照元の外部キーをuser_idからuser_nubmer変更することができます。 フォロー、フォロワー機能の実装のように同じテーブル同士を中間テーブルを介して関連付けを行う際に使用したりします。 3. class_name class_nameは関連付けを行う関連名を変更する際に使用するオプションです。 関連名を変更してしまうと、どのモデルと関連付けされているのか判断できなくなるため 関連付け先のモデル名を記載します。 使い方 models/department.rb (他のモデルの設定は) class Department < Application has_many :assignments has_many :employees, through: :assignments, class_name: 'User' # class_name追加 end models/user.rb class User < ApplicationRecord has_many :assignments has_many :departments, through :assignments end models/assignment.rb class Assignment < ApplicationRecord belongs_to :user belongs_to :department end これにより下記のように参照の仕方を変えることができます。 department = Department.find(1) # class_name追加前 # department.users # class_name追加後 department.employees # 部署に所属してるユーザーが取得できる 4. dependent dependentはテーブルのデータが削除された際に関連付けられているデータも削除するときに使用します。 使い方 models/user.rb class User < ApplicationRecord has_many :assignments, dependent: :destroy end 上記のように設定するとユーザーが削除された際に紐付いているassignmentsテーブルのデータも削除されるようになります。 まとめ 以上がよく使われるオプションになります。 他にもオプションはたくさんありますので詳しく確認したい場合は下記参考文献を参照ください。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Heroku + MySQL + Rails6.0でstaging環境を用意する。

背景 個人アプリを開発していて、現状はローカル環境と本番環境しか用意していなかった。 本来であればstaging環境で確認後、本番環境にデプロイする流れが望ましいため、staging環境を準備することにした。 前提 Herokuで本番環境が用意できている。 手順 staging環境の作成 heroku create --remote staging これでstaging環境が作成される。 参考記事 本番環境の環境変数をインポート + stagingの環境変数にセットする。 heroku config -s -a "本番環境のアプリ名" > envvars.txt cat envvars.txt | tr '\n' ' ' | xargs heroku config:set -a "staging環境のアプリ名" CLEARDB_DATABASE_URL、DATABASE_URL、DB_NAME、DB_USERNAME、DB_PASSWORD、DB_HOSTNAMEもコピーしてしまっているが、そちらは削除しておく。 参考記事 cat envvars.txt | tr '\n' ' ' | xargsの解説 cat envvars.txtでインポートした本番環境の環境変数のファイルを出力する。 tr '\n' ' 'でennvars.txtの改行部分を空白に置換する。 xargsコマンドでcat envvars.txt | tr '\n' ' 'の結果を受け取って、次のコマンドの引数に渡してくれる。 参考記事 MySQLアドオンを追加する。 + stagingの環境変数の修正 heroku addons:create cleardb:ignite --remote staging CLEARDB_DATABASE_URLから、 DATABASE_URL、DB_NAME、DB_USERNAME、DB_PASSWORD、DB_HOSTNAMEの環境変数をセットする。 例: heroku config:set DB_NAME=xxxx --remote staging RAILS_ENVとRACK_ENVをstagingに変更する。 heroku config:add RACK_ENV=staging --remote staging heroku config:add RAILS_ENV=staging --remote staging staging.rbを用意する。 cp config/environments/production.rb config/environments/staging.rb staging.rb # 下記のように修正 config.action_mailer.default_url_options = { protocol: 'https', host: 'stagingのアプリ名' } staging環境にデプロイする。 git push staging master staging環境のDBを作成する。 heroku run rake db:migrate --remote staging staging環境のアプリ名を変更する。 heroku apps:rename "変更したいアプリ名" --remote staging 参考記事 以上で完了。 最後に 今回は取り急ぎstaging環境を用意することだけを行ったが、今後は プルリクを出したら自動的にstaging環境にデプロイされるようにする。 本番環境のデータと1日おきに同期する。 などといったことにも挑戦していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 7.0.1 のプロジェクトで RSpec 実行環境をセットアップ

Ruby 3.0 x Rails 7 のプロジェクトで RSpec の実行環境を作る方法をメモ的に記します。 具体的には RSpec と FactoryBot gem をインストールして設定していきます。 # 環境情報 $ ruby -v ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [arm64-darwin21] $ rails -v Rails 7.0.1 結論からいうと Rails 7 独自の設定は特に必要ありません。 プロジェクトを作成 Rails 7 では rails new のオプションでフロントエンド関連の色々を指定できますが、特に指定してません。 $ rails new rails7_rspec RSpec 環境用の gem をインストール rails new した時に rails7_rspec ディレクトリに生成された Gemfile に追記します。 group :development, :test do gem "debug", platforms: %i[ mri mingw x64_mingw ] gem "rspec-rails" # 追加 gem "factory_bot_rails" # 追加 end Gemfile に従って gem をインストールします。 $ bundle install 各種 gem を設定 RSpec を設定 RSpec の基本設定は rails のジェネレータコマンド (rails) を使って作成します。 rails g rspec:install これで rspec コマンドから RSpec を即時実行できるようになります。 試しに叩いてみると実行されるはずです。(テストは作ってないので 0 examples になります。) $ rspec No examples found. Finished in 0.0002 seconds (files took 0.07517 seconds to load) 0 examples, 0 failures FactoryBot を設定 RSpec のテストコードで FactoryBot のメソッドを使う際、クラス名 (FactoryBot) を省略できるようにしておくと便利です。 省略するためには RSpec の基本設定をした時に自動生成された spec/rails_helper.rb ファイルに追記します。 spec/rails_helper.rb RSpec.configure do |config| # RSpec.configure ブロックの中に追記 config.include FactoryBot::Syntax::Methods end この記述により、以下のように FactoryBot というクラス名をつけなくても FactoryBot のメソッドを spec ファイル内で呼び出せるようになります。 user = create(:user) # 参考:クラス名をつける場合  user = FactoryBot.create(:user)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

{自分用メモ}Githubにプッシュ&Herokuにデプロイまで

開発環境 mac OS バージョン11.6 エディタ VScode % rails new toy_app % cd toy_app group :production do gem 'pg', '1.1.4' end % bundle config set --local without 'production' % bundle install bundle install(もしくはbundle update)が完了したら、1.2.2でセットアップしたYarnを使ってWebpackerをインストールします。もしWebpackerを既にインストール済みの場合は設定を上書きするかどうかを聞かれるので、そのときは「no」と入力してください。(自分は表示が出ませんでした) % rails webpacker:install 最後に、GitでこのToyアプリケーションをバージョン管理下に置きます。 % git init % git add -A % git commit -m "Initialize repository" ここまではOK 次に、1.4.3と同じ手順でGitHubで新しいリポジトリを作成します(このときリポジトリを図 2.1のようにprivateにしておくことをお忘れなく)。←やってなかった 続いて、生成したファイルをこの新しいリモートリポジトリにプッシュします。 % git remote add origin https://github.com/<あなたのGitHubアカウント名>/toy_app.git (実際にやったの→git remote add origin https://github.com/ctmajor07@gmail.com/toy_app.git) Gihubアカウント名のところを間違えて、メールアドレスで実行してしまう。 % git push -u origin master fatal: unable to access 'https://github.com/ctmajor07@gmail.com/toy_app.git/': The requested URL returned error: 400 ここでエラーが起きる ローカルgitリポジトリでリモートのリポジトリURL確認方法 ローカルにクローンしたリポジトリのリモートURLを確認する方法はいくつあります。 % git remote -v origin https://github.com/ctmajor07@gmail.com/toy_app.git (fetch) origin https://github.com/ctmajor07@gmail.com/toy_app.git (push) % git remote add origin https://github.com/tomoya007/toy_app.git % git remote -v origin https://github.com/tomoya007/toy_app.git (fetch) origin https://github.com/tomoya007/toy_app.git (push) % git push -u origin master remote: Repository not found. fatal: repository 'https://github.com/tomoya007/toy_app.git/' not found 何が起きているかというと、、、 エラーの原因は→プライベートリポジトリへのアクセス認証に失敗しているためです。gitの接続プロトコルを https にしているのが原因のことが多いです。githubから普通にcloneすると大体 https になっているのですが、仕事とプライベートで複数のgitアカウントを切り替えて使っていると、いつのまにかgithubの認証情報が切り替わって、アクセスできなくなるようです。 remote.origin.url が https になっているかを確認します。 % git config -l | grep remote.origin.url remote.origin.url=https〜 # httpsから始まるURLがセットされているか ↓実際にやってみたら % git config -l | grep remote.origin.url remote.origin.url=https://github.com/tomoya007/toy_app.git ②リモートURLをSSHのパスに書き換え git remote set-url origin [リポジトリのSSHパス] git remote set-url origin git@github.com:xxx/abc.git # 例 SSHパスは、以下の画面よりコピーすると簡単です。 % git remote set-url origin git@github.com:tomoya007/toy_app.git 実行後、もう一度 git config -l をして、remote.origin.url が git@ で始まるSSHパスに変わっているかを確認しておきましょう。 % git config -l | grep remote.origin.url remote.origin.url=git@github.com:tomoya007/toy_app.git ③公開鍵をgithubに登録 自分のローカルマシンの公開鍵をgithubのアカウントに登録します。(既に登録済の場合はスキップして構いません。) https://github.com/settings/ssh まず自分の公開鍵をクリップボードにコピーします。Macの場合は pbcopy < ~/.ssh/id_rsa.pub でコピーできます。 ↓実際に実行した結果 % pbcopy < ~/.ssh/id_rsa.pub zsh: no such file or directory: /Users/chiharatomoya/.ssh/id_rsa.pub 試しにペーストしてみても、ちゃんとコピーされてない。。 原因 そもそも公開鍵というものを作成してなかった。なのでまずは公開鍵を作成します。手順は以下の通りです。 (注意:公開鍵の作成方法はOSによって異なります。こちらはMac環境での手順になります。) ターミナルで以下のコマンドを実行し、SSH鍵の保存先ディレクトリを作成する % mkdir -m 700 ~/.ssh 以下のコマンドを入力してEnterキーを押す % ssh-keygen -t rsa 保存場所とファイル名を確認し、Enterキーを押す Enter file in which to save the key (/Users/hogehoge/.ssh/id_rsa): パスフレーズを入力する Enter passphrase (empty for no passphrase): (パスフレーズを入力) Enter same passphrase again: (もう一度入力) 以下のコマンドで公開鍵の内容を確認する % pbcopy < ~/.ssh/id_rsa.pub 上記のコマンドでssh-rsa AAAAB3Nza...のような長い文が出てくればOKです。 接続を確かめます % ssh -T git@github.com Hi (account名)! You've successfully authenticated, but GitHub does not provide shell access. 上記のように返ってきたら接続完了です。 筆者の場合は、 The authenticity of host 'github.com (13.114.40.48)' can't be established. ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM. Are you sure you want to continue connecting (yes/no/[fingerprint])? Host key verification failed. 最後の行に「ホストキーの検証に失敗しました」と出ていますが、その前の行で間違えてEnterを押したのが原因でした。正しくはyesと打ってからEnterです。 ↓ yesと打って、パスワードも入れると下記のように出ます。 Are you sure you want to continue connecting (yes/no/[fingerprint])? y Please type 'yes', 'no' or the fingerprint: y Please type 'yes', 'no' or the fingerprint: yes Warning: Permanently added 'github.com,52.192.72.89' (ECDSA) to the list of known hosts. Enter passphrase for key '/Users/chiharatomoya/.ssh/id_rsa': Hi tomoya007! You've successfully authenticated, but GitHub does not provide shell access. 今度こそgit push -u origin masterを実行して、リモートリポジトリにプッシュします。 更に、デプロイもできるように準備していきます。Hello, world!だけ出るようにコントローラーに書き加えます(ここでは省略。なくてもいいです) $ git commit -am "Add hello" $ heroku create $ git push && git push heroku master 何とか実行できました。 今回はここまで。 参考にした記事URL https://qiita.com/shizuma/items/2b2f873a0034839e47ce
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Githubにプッシュ&Herokuにデプロイまで

開発環境 mac OS バージョン11.6 エディタ VScode % rails new toy_app % cd toy_app group :production do gem 'pg', '1.1.4' end % bundle config set --local without 'production' % bundle install bundle install(もしくはbundle update)が完了したら、1.2.2でセットアップしたYarnを使ってWebpackerをインストールします。もしWebpackerを既にインストール済みの場合は設定を上書きするかどうかを聞かれるので、そのときは「no」と入力してください。(自分は表示が出ませんでした) % rails webpacker:install 最後に、GitでこのToyアプリケーションをバージョン管理下に置きます。 % git init % git add -A % git commit -m "Initialize repository" ここまではOK 次に、1.4.3と同じ手順でGitHubで新しいリポジトリを作成します(このときリポジトリを図 2.1のようにprivateにしておくことをお忘れなく)。←やってなかった 続いて、生成したファイルをこの新しいリモートリポジトリにプッシュします。 % git remote add origin https://github.com/<あなたのGitHubアカウント名>/toy_app.git (実際にやったの→git remote add origin https://github.com/ctmajor07@gmail.com/toy_app.git) Gihubアカウント名のところを間違えて、メールアドレスで実行してしまう。 % git push -u origin master fatal: unable to access 'https://github.com/ctmajor07@gmail.com/toy_app.git/': The requested URL returned error: 400 ここでエラーが起きる ローカルgitリポジトリでリモートのリポジトリURL確認方法 ローカルにクローンしたリポジトリのリモートURLを確認する方法はいくつあります。 % git remote -v origin https://github.com/ctmajor07@gmail.com/toy_app.git (fetch) origin https://github.com/ctmajor07@gmail.com/toy_app.git (push) % git remote add origin https://github.com/tomoya007/toy_app.git % git remote -v origin https://github.com/tomoya007/toy_app.git (fetch) origin https://github.com/tomoya007/toy_app.git (push) % git push -u origin master remote: Repository not found. fatal: repository 'https://github.com/tomoya007/toy_app.git/' not found 何が起きているかというと、、、 エラーの原因は→プライベートリポジトリへのアクセス認証に失敗しているためです。gitの接続プロトコルを https にしているのが原因のことが多いです。githubから普通にcloneすると大体 https になっているのですが、仕事とプライベートで複数のgitアカウントを切り替えて使っていると、いつのまにかgithubの認証情報が切り替わって、アクセスできなくなるようです。 remote.origin.url が https になっているかを確認します。 % git config -l | grep remote.origin.url remote.origin.url=https〜 # httpsから始まるURLがセットされているか ↓実際にやってみたら % git config -l | grep remote.origin.url remote.origin.url=https://github.com/tomoya007/toy_app.git ②リモートURLをSSHのパスに書き換え git remote set-url origin [リポジトリのSSHパス] git remote set-url origin git@github.com:xxx/abc.git # 例 SSHパスは、以下の画面よりコピーすると簡単です。 % git remote set-url origin git@github.com:tomoya007/toy_app.git 実行後、もう一度 git config -l をして、remote.origin.url が git@ で始まるSSHパスに変わっているかを確認しておきましょう。 % git config -l | grep remote.origin.url remote.origin.url=git@github.com:tomoya007/toy_app.git ③公開鍵をgithubに登録 自分のローカルマシンの公開鍵をgithubのアカウントに登録します。(既に登録済の場合はスキップして構いません。) https://github.com/settings/ssh まず自分の公開鍵をクリップボードにコピーします。Macの場合は pbcopy < ~/.ssh/id_rsa.pub でコピーできます。 ↓実際に実行した結果 % pbcopy < ~/.ssh/id_rsa.pub zsh: no such file or directory: /Users/chiharatomoya/.ssh/id_rsa.pub 試しにペーストしてみても、ちゃんとコピーされてない。。 原因 そもそも公開鍵というものを作成してなかった。なのでまずは公開鍵を作成します。手順は以下の通りです。 (注意:公開鍵の作成方法はOSによって異なります。こちらはMac環境での手順になります。) ターミナルで以下のコマンドを実行し、SSH鍵の保存先ディレクトリを作成する % mkdir -m 700 ~/.ssh 以下のコマンドを入力してEnterキーを押す % ssh-keygen -t rsa 保存場所とファイル名を確認し、Enterキーを押す Enter file in which to save the key (/Users/hogehoge/.ssh/id_rsa): パスフレーズを入力する Enter passphrase (empty for no passphrase): (パスフレーズを入力) Enter same passphrase again: (もう一度入力) 以下のコマンドで公開鍵の内容を確認する % pbcopy < ~/.ssh/id_rsa.pub 上記のコマンドでssh-rsa AAAAB3Nza...のような長い文が出てくればOKです。 接続を確かめます % ssh -T git@github.com Hi (account名)! You've successfully authenticated, but GitHub does not provide shell access. 上記のように返ってきたら接続完了です。 筆者の場合は、 The authenticity of host 'github.com (13.114.40.48)' can't be established. ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM. Are you sure you want to continue connecting (yes/no/[fingerprint])? Host key verification failed. 最後の行に「ホストキーの検証に失敗しました」と出ていますが、その前の行で間違えてEnterを押したのが原因でした。正しくはyesと打ってからEnterです。 ↓ yesと打って、パスワードも入れると下記のように出ます。 Are you sure you want to continue connecting (yes/no/[fingerprint])? y Please type 'yes', 'no' or the fingerprint: y Please type 'yes', 'no' or the fingerprint: yes Warning: Permanently added 'github.com,52.192.72.89' (ECDSA) to the list of known hosts. Enter passphrase for key '/Users/chiharatomoya/.ssh/id_rsa': Hi tomoya007! You've successfully authenticated, but GitHub does not provide shell access. 今度こそgit push -u origin masterを実行して、リモートリポジトリにプッシュします。 更に、デプロイもできるように準備していきます。Hello, world!だけ出るようにコントローラーに書き加えます(ここでは省略。なくてもいいです) $ git commit -am "Add hello" $ heroku create $ git push && git push heroku master 何とか実行できました。 今回はここまで。 参考にした記事URL https://qiita.com/shizuma/items/2b2f873a0034839e47ce
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】paramsについて

paramsとは? →データの入れ物 どんなデータが入ってるのか? 基本的には以下の2種類の方法で収集されたデータが入ってる。 ・投稿フォームなどからPOSTで送信されたデータ ・getのクエリパラメータ 【クエリパラメータとは?】 例えば、基本のURLが「https:// ○△×□.jp/」だとして、基本のURLにクエリ文字列(URLパラメーター)を加えると「https:// ○△×□.jp/?●=▲×■&○=△×□」となります。 「?●=▲×■&○=△×□」の部分が、クエリパラメーター(URLパラメーター)です。 引用:https://online.dhw.co.jp/kuritama/query-string/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(初心者)Rails Active Storage  画像が割れて表示されてしまった件

実行内容 Ruby on Rails にてアプリを作成中。 Active Storageを使ってアップロード済のユーザーアイコン画像を、 以下のコードにより表示させようとした。 <% if @user.avatar.attached? %> <%= image_tag @user.avatar, size: "100x100" %> <% end %> 結果 以下のように割れた画像が表示された。 解決策 以下のようにコードを修正し、うまく表示された。 <% if @user.avatar.attached? %> <%= image_tag url_for(rails_blob_path(@user.avatar)), size: "100x100" %> <% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新規Railsアプリを作成するには

まずはターミナル上で、下記を実行する。 新規アプリ名(例:tweet)を入力、 -dオプションでMySQLを使用することを明示 % rails new tweet -d mysql
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クラスとインスタンスの違いを簡潔にまとめる

はじめに どんどんアップデートしていく記事にしていこうと思っています。 結論 クラス全体で共通した振る舞いを示す クラスから生成されたインスタンスの振る舞いを示す 属性と属性値の関係 属性はみんなが共通で持っている項目で属性値は個別で異なる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む