From 5d7f6f8041c6e7f687d2e031691763f432e0a57b Mon Sep 17 00:00:00 2001 From: zhouxingtao <657685705@qq.com> Date: Tue, 8 Dec 2020 16:22:01 +0800 Subject: [PATCH 1/4] tset --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b33efd126..34cb24b58 100644 --- a/README.md +++ b/README.md @@ -107,3 +107,5 @@ $ git checkout sign-up Experience shows that comparing code with the reference app is often helpful for debugging errors and tracking down discrepancies. For additional assistance with any issues in the tutorial, please consult the [Rails Tutorial Help page](https://www.railstutorial.org/help). Suspected errors, typos, and bugs can be emailed to . All such reports are gratefully received, but please double-check with the [online version of the tutorial](https://www.railstutorial.org/book) and this reference app before submitting. + +test \ No newline at end of file From abea5a4688cb4f0c57f60cd0f7ab86b29be1ef79 Mon Sep 17 00:00:00 2001 From: zhouxingtao <657685705@qq.com> Date: Tue, 8 Dec 2020 16:36:37 +0800 Subject: [PATCH 2/4] update application_record_spec.rb --- spec/models/application_record_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 spec/models/application_record_spec.rb diff --git a/spec/models/application_record_spec.rb b/spec/models/application_record_spec.rb new file mode 100644 index 000000000..92ca269ff --- /dev/null +++ b/spec/models/application_record_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' +RSpec.describe ApplicationRecord, type: :model do + it '#self' do + expect(ApplicationRecord.abstract_class).to eq true + end + +end \ No newline at end of file From 1d32ea5660d7883ce2cfa069f023c061cd4c82fe Mon Sep 17 00:00:00 2001 From: zhouxingtao <657685705@qq.com> Date: Thu, 10 Dec 2020 16:15:11 +0800 Subject: [PATCH 3/4] add spec --- spec/models/user_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 5d5b6d00d..2d2d8ecfc 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -16,4 +16,16 @@ expect(user.save).to be false end end + + describe "#validates" do + + context 'validates nil name' do + let(:user_nil_name) { User.new(name: 'test_name', password: 'test_password', email: 'test_email@test.com') } + + it 'should return false with nil name' do + result = user_nil_name.save + expect(result).to be false + end + end + end end From 89eadfd3ebe6c6c70a472d2c181bc0a95d98f902 Mon Sep 17 00:00:00 2001 From: zhouxingtao <657685705@qq.com> Date: Thu, 10 Dec 2020 17:03:24 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 56 +++++++++++++++++++++++++++++++++ .gitignore | 3 ++ .rubocop.yml | 12 +++++++ Gemfile | 8 +++-- Gemfile.lock | 15 ++++++--- README.md | 2 -- app/models/user.rb | 46 ++++++++++++++++----------- config/application.sample.yml | 7 +++++ config/database.yml | 19 +++++------ db/schema.rb | 9 ++++-- features/home_page.feature | 15 +++++++++ features/support/env.rb | 3 ++ features/users.feature | 1 - spec/models/user_spec.rb | 30 ++++++------------ spec/support/shoulda_matcher.rb | 6 ++++ 15 files changed, 172 insertions(+), 60 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 config/application.sample.yml create mode 100644 features/home_page.feature create mode 100644 spec/support/shoulda_matcher.rb diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..865a5ded8 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,56 @@ +version: 2.1 + +orbs: + ruby: circleci/ruby@1.1.0 + node: circleci/node@2 + +jobs: + build: + docker: + - image: cimg/ruby:2.6.6-node + steps: + - checkout + - ruby/install-deps + # Store bundle cache + - node/install-packages: + pkg-manager: yarn + cache-key: "yarn.lock" + test: + parallelism: 3 + docker: + - image: cimg/ruby:2.6.6-node + - image: circleci/postgres:9.5-alpine + environment: + POSTGRES_USER: circleci-ruby + POSTGRES_DB: sample_app_test + POSTGRES_PASSWORD: "" + environment: + BUNDLE_JOBS: "3" + BUNDLE_RETRY: "3" + PGHOST: 127.0.0.1 + DATABASE_USER: circleci-ruby + DATABASE_PASSWORD: "" + RAILS_ENV: test + steps: + - checkout + - ruby/install-deps + - node/install-packages: + pkg-manager: yarn + cache-key: "yarn.lock" + - run: + name: Wait for DB + command: dockerize -wait tcp://localhost:5432 -timeout 1m + - run: + name: Database setup + command: bundle exec rails db:schema:load --trace + # Run rspec in parallel + - ruby/rspec-test + +workflows: + version: 2 + build_and_test: + jobs: + - build + - test: + requires: + - build diff --git a/.gitignore b/.gitignore index 55c9f130c..28b1c394d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ yarn-debug.log* # Ignore db test files. db/test.* + +# Ignore application configuration +/config/application.yml diff --git a/.rubocop.yml b/.rubocop.yml index 3770f39c7..2598f8e2e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,3 +17,15 @@ RSpec/MultipleExpectations: Metrics/BlockLength: Enabled: false + +Bundler/OrderedGems: + Enabled: false + +Style/Documentation: + Enabled: false + +Style/SymbolArray: + Enabled: false + +Layout/ExtraSpacing: + Enabled: false diff --git a/Gemfile b/Gemfile index 9adf1695e..2352e005e 100644 --- a/Gemfile +++ b/Gemfile @@ -18,10 +18,11 @@ gem 'jbuilder', '2.10.0' gem 'bootsnap', '1.4.6', require: false gem 'rubocop', '1.5.2', require: false gem 'rubocop-rspec', '2.0.1', require: false +gem 'pg', '0.21.0' +gem "figaro", '1.2.0' group :development, :test do - gem 'sqlite3', '1.4.2' - gem 'byebug', '11.1.3', platforms: [:mri, :mingw, :x64_mingw] + gem 'byebug', '11.1.3', platforms: [:mri, :mingw, :x64_mingw] gem 'pry-rails' gem 'rspec-rails', '~> 4.0.1' gem 'factory_bot_rails', '~> 6.1.0' @@ -46,10 +47,11 @@ group :test do gem 'cucumber-rails', '2.2.0', require: false gem 'database_cleaner-active_record', '1.8.0' gem 'launchy', '2.5.0' + gem 'shoulda-matchers', '4.4.1' + gem 'rspec_junit_formatter', '0.4.1' end group :production do - gem 'pg', '1.2.3' gem 'aws-sdk-s3', '1.46.0', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 2bd2fea1a..6e04e9232 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -155,6 +155,8 @@ GEM faker (2.11.0) i18n (>= 1.6, < 2) ffi (1.13.1) + figaro (1.2.0) + thor (>= 0.14.0, < 2) formatador (0.2.5) globalid (0.4.2) activesupport (>= 4.2.0) @@ -219,7 +221,7 @@ GEM parallel (1.20.1) parser (2.7.2.0) ast (~> 2.4.1) - pg (1.2.3) + pg (0.21.0) protobuf-cucumber (3.10.8) activesupport (>= 3.2) middleware @@ -292,6 +294,8 @@ GEM rspec-mocks (~> 3.9) rspec-support (~> 3.9) rspec-support (3.10.0) + rspec_junit_formatter (0.4.1) + rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.5.2) parallel (~> 1.10) parser (>= 2.7.1.5) @@ -324,6 +328,8 @@ GEM childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) shellany (0.0.1) + shoulda-matchers (4.4.1) + activesupport (>= 4.2.0) spring (2.1.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) @@ -335,7 +341,6 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.4.2) sys-uname (1.2.2) ffi (~> 1.1) thor (1.0.1) @@ -384,6 +389,7 @@ DEPENDENCIES database_cleaner-active_record (= 1.8.0) factory_bot_rails (~> 6.1.0) faker (= 2.11.0) + figaro (= 1.2.0) guard (= 2.16.2) guard-minitest (= 2.4.6) image_processing (= 1.9.3) @@ -393,19 +399,20 @@ DEPENDENCIES mini_magick (= 4.9.5) minitest (= 5.11.3) minitest-reporters (= 1.3.8) - pg (= 1.2.3) + pg (= 0.21.0) pry-rails puma (= 5.0.4) rails (= 6.0.3.4) rails-controller-testing (= 1.0.4) rspec-rails (~> 4.0.1) + rspec_junit_formatter (= 0.4.1) rubocop (= 1.5.2) rubocop-rspec (= 2.0.1) sass-rails (= 6.0.0) selenium-webdriver (= 3.142.7) + shoulda-matchers (= 4.4.1) spring (= 2.1.1) spring-watcher-listen (= 2.0.1) - sqlite3 (= 1.4.2) turbolinks (= 5.2.1) web-console (= 4.0.2) webdrivers (= 4.3.0) diff --git a/README.md b/README.md index 34cb24b58..b33efd126 100644 --- a/README.md +++ b/README.md @@ -107,5 +107,3 @@ $ git checkout sign-up Experience shows that comparing code with the reference app is often helpful for debugging errors and tracking down discrepancies. For additional assistance with any issues in the tutorial, please consult the [Rails Tutorial Help page](https://www.railstutorial.org/help). Suspected errors, typos, and bugs can be emailed to . All such reports are gratefully received, but please double-check with the [online version of the tutorial](https://www.railstutorial.org/book) and this reference app before submitting. - -test \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index f59977d81..5568e7e05 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,18 +1,23 @@ class User < ApplicationRecord has_many :microposts, dependent: :destroy - has_many :active_relationships, class_name: "Relationship", + has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", - dependent: :destroy - has_many :passive_relationships, class_name: "Relationship", + dependent: :destroy + + has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", - dependent: :destroy + dependent: :destroy + has_many :following, through: :active_relationships, source: :followed has_many :followers, through: :passive_relationships, source: :follower attr_accessor :remember_token, :activation_token, :reset_token + before_save :downcase_email before_create :create_activation_digest validates :name, presence: true, length: { maximum: 50 } - VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze + validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: true @@ -20,14 +25,18 @@ class User < ApplicationRecord validates :password, presence: true, length: { minimum: 6 }, allow_nil: true # Returns the hash digest of the given string. - def User.digest(string) - cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : - BCrypt::Engine.cost + def self.digest(string) + cost = if ActiveModel::SecurePassword.min_cost + BCrypt::Engine::MIN_COST + else + BCrypt::Engine.cost + end + BCrypt::Password.create(string, cost: cost) end # Returns a random token. - def User.new_token + def self.new_token SecureRandom.urlsafe_base64 end @@ -48,6 +57,7 @@ def session_token def authenticated?(attribute, token) digest = send("#{attribute}_digest") return false if digest.nil? + BCrypt::Password.new(digest).is_password?(token) end @@ -109,14 +119,14 @@ def following?(other_user) private - # Converts email to all lower-case. - def downcase_email - self.email = email.downcase - end + # Converts email to all lower-case. + def downcase_email + self.email = email.downcase + end - # Creates and assigns the activation token and digest. - def create_activation_digest - self.activation_token = User.new_token - self.activation_digest = User.digest(activation_token) - end + # Creates and assigns the activation token and digest. + def create_activation_digest + self.activation_token = User.new_token + self.activation_digest = User.digest(activation_token) + end end diff --git a/config/application.sample.yml b/config/application.sample.yml new file mode 100644 index 000000000..2c48531b7 --- /dev/null +++ b/config/application.sample.yml @@ -0,0 +1,7 @@ +development: + DATABASE_USER: '' + DATABASE_PASSWORD: '' + +test: + DATABASE_USER: '' + DATABASE_PASSWORD: '' diff --git a/config/database.yml b/config/database.yml index bad265644..9466b55cd 100644 --- a/config/database.yml +++ b/config/database.yml @@ -5,27 +5,28 @@ # gem 'sqlite3' # default: &default - adapter: sqlite3 + adapter: postgresql + encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 development: <<: *default - database: db/development.sqlite3 + database: sample_app_development + username: <%= ENV.fetch("DATABASE_USER") %> + password: <%= ENV.fetch("DATABASE_PASSWORD") %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default - database: db/test.sqlite3 + database: sample_app_test + username: <%= ENV.fetch("DATABASE_USER") %> + password: <%= ENV.fetch("DATABASE_PASSWORD") %> production: - adapter: postgresql - encoding: unicode - # For details on connection pooling, see Rails configuration guide - # https://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + <<: *default database: sample_app_production username: sample_app - password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %> + password: <%= ENV['DATABASE_PASSWORD'] %> diff --git a/db/schema.rb b/db/schema.rb index 71eac3e5b..7b58f01d5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,11 +12,14 @@ ActiveRecord::Schema.define(version: 2019_08_27_030205) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false - t.integer "record_id", null: false - t.integer "blob_id", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false t.datetime "created_at", null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true @@ -35,7 +38,7 @@ create_table "microposts", force: :cascade do |t| t.text "content" - t.integer "user_id", null: false + t.bigint "user_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at" diff --git a/features/home_page.feature b/features/home_page.feature new file mode 100644 index 000000000..78efb2196 --- /dev/null +++ b/features/home_page.feature @@ -0,0 +1,15 @@ +Feature: Users + Background: + Given I am logged in as an activated user + + @javascript_driver + Scenario: User can create micropost + When I go to home page + Then I should see "Compose new micropost" textare + When I create a new micropost + Then I should see the new micropost from "Micropost Feed" + + Scenario: User can destroy owned microposts + + + Scenario: User can see microposts from followed users diff --git a/features/support/env.rb b/features/support/env.rb index 003fb1319..dc94db95e 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -58,3 +58,6 @@ # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature Cucumber::Rails::Database.javascript_strategy = :truncation +Capybara.javascript_driver = :selenium_chrome + + diff --git a/features/users.feature b/features/users.feature index 3d4cdacb4..48eef3a51 100644 --- a/features/users.feature +++ b/features/users.feature @@ -5,4 +5,3 @@ Feature: Users Scenario: Users List When I go to the list of users Then I should see "Users" - And I should see "All users" diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2d2d8ecfc..dcd1199f8 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -5,27 +5,17 @@ expect(create(:user)).to be_valid end - context 'when name is not present' do - let(:user) { described_class.new(email: Faker::Internet.email, password: described_class.digest('password')) } - - it 'is inivalid' do - expect(user.valid?).to be false - end - - it 'is failed to save' do - expect(user.save).to be false - end + describe 'associations' do + it { is_expected.to have_many(:microposts).dependent(:destroy) } + it { is_expected.to have_many(:active_relationships).with_foreign_key('follower_id').class_name('Relationship').dependent(:destroy) } + it { is_expected.to have_many(:passive_relationships).with_foreign_key('followed_id').class_name('Relationship').dependent(:destroy) } + it { is_expected.to have_many(:following).through(:active_relationships).source(:followed) } + it { is_expected.to have_many(:followers).through(:passive_relationships).source(:follower) } end - describe "#validates" do - - context 'validates nil name' do - let(:user_nil_name) { User.new(name: 'test_name', password: 'test_password', email: 'test_email@test.com') } - - it 'should return false with nil name' do - result = user_nil_name.save - expect(result).to be false - end - end + describe 'validations' do + it { is_expected.to validate_presence_of(:name)} + it { is_expected.to validate_presence_of(:email) } + it { is_expected.to validate_presence_of(:password) } end end diff --git a/spec/support/shoulda_matcher.rb b/spec/support/shoulda_matcher.rb new file mode 100644 index 000000000..7d045f359 --- /dev/null +++ b/spec/support/shoulda_matcher.rb @@ -0,0 +1,6 @@ +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end