├── .dockerignore ├── .env.example ├── .github ├── dependabot.yml └── workflows │ ├── automerge.yml │ ├── ci.yml │ ├── docker.yml │ └── review.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── Dockerfile ├── Dockerfile.release ├── Gemfile ├── Gemfile.lock ├── Makefile ├── README.md ├── Rakefile ├── api_schema ├── components │ └── schemas │ │ └── peopleProperties.yml ├── openapi.yml └── paths │ └── people.yml ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── logo-horizontal.svg │ │ ├── logo-vertical.svg │ │ └── sign-in-user-icon.svg │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ └── application.css.erb ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── activities │ │ └── on_term_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── sessions_controller.rb │ ├── shared_links_controller.rb │ └── welcome_controller.rb ├── helpers │ ├── activities_helper.rb │ ├── application_helper.rb │ └── sessions_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── activity.rb │ ├── application_record.rb │ ├── authentication.rb │ ├── concerns │ │ └── .keep │ ├── shared_link.rb │ └── user.rb └── views │ ├── activities │ ├── github │ │ └── _event.html.slim │ └── on_term │ │ └── index.html.slim │ ├── layouts │ ├── _navigator.html.slim │ ├── application.html.slim │ ├── mailer.html.erb │ └── mailer.text.erb │ └── welcome │ ├── _annual_report.slim │ ├── _for_user.html.slim │ └── index.html.slim ├── bin ├── bundle ├── generate_stub_server.sh ├── rails ├── rake ├── run_stub_server.sh ├── setup ├── spring ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── 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 │ ├── octokit.rb │ ├── omniauth.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── spring.rb └── storage.yml ├── db ├── migrate │ ├── 20180709154513_create_users.rb │ ├── 20180709223556_create_authentications.rb │ ├── 20180714072854_create_activities.rb │ ├── 20191026092654_create_shared_links.rb │ └── 20200107134431_add_index_to_shared_links_token.rb ├── schema.rb └── seeds.rb ├── docker-compose.yml ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── spec ├── factories │ ├── activities.rb │ ├── authentications.rb │ ├── shared_links.rb │ └── users.rb ├── models │ ├── activity_spec.rb │ └── shared_link_spec.rb ├── rails_helper.rb ├── requests │ └── sessions_spec.rb ├── spec_helper.rb └── support │ ├── factory_bot.rb │ └── omniauth.rb ├── tmp └── .keep └── vendor └── .keep /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | tmp 4 | spec 5 | log 6 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | GHE_APP_KEY="**********" 2 | GHE_APP_SECRET="**********" 3 | GHE_HOST="github.example.com" 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: 3 | pull_request_target: 4 | 5 | jobs: 6 | automerge: 7 | runs-on: ubuntu-latest 8 | if: ${{ github.actor == 'dependabot[bot]' }} 9 | steps: 10 | - name: Dependabot metadata 11 | uses: dependabot/fetch-metadata@v1 12 | id: metadata 13 | - name: Wait for status checks 14 | uses: lewagon/wait-on-check-action@v1.3.3 15 | with: 16 | repo-token: ${{ secrets.MATZBOT_GITHUB_TOKEN }} 17 | ref: ${{ github.event.pull_request.head.sha || github.sha }} 18 | check-regexp: build* 19 | wait-interval: 30 20 | - name: Auto-merge for Dependabot PRs 21 | if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 22 | run: gh pr merge --auto --merge "$PR_URL" 23 | env: 24 | PR_URL: ${{github.event.pull_request.html_url}} 25 | GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | [push] 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | services: 8 | postgres: 9 | image: postgres:latest 10 | ports: 11 | - 5432:5432 12 | env: 13 | POSTGRES_PASSWORD: password 14 | strategy: 15 | matrix: 16 | version: ["3.2.2"] 17 | steps: 18 | - uses: actions/checkout@v1 19 | - name: Set up Ruby 20 | uses: ruby/setup-ruby@master 21 | with: 22 | ruby-version: ${{ matrix.version }} 23 | bundler-cache: true 24 | - name: Set up bundler 25 | run: gem install bundler 26 | - name: Run bundle install 27 | run: bundle install --jobs=4 28 | - name: Rubocop 29 | run: bundle exec rubocop -c .rubocop.yml 30 | - name: Set up Database 31 | env: 32 | RAILS_ENV: test 33 | run: bundle exec rake db:setup 34 | - name: Test 35 | env: 36 | RAILS_ENV: test 37 | run: bundle exec rspec 38 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: docker 2 | 3 | on: 4 | schedule: 5 | - cron: "0 10 * * *" 6 | push: 7 | branches: 8 | - "master" 9 | tags: 10 | - "v*.*.*" 11 | jobs: 12 | docker: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: Checkout 17 | uses: actions/checkout@v3 18 | - 19 | name: Docker meta 20 | id: meta 21 | uses: docker/metadata-action@v4 22 | with: 23 | images: | 24 | ghcr.io/sokusekiya/sokuseki 25 | tags: | 26 | type=schedule 27 | type=ref,event=branch 28 | type=ref,event=pr 29 | type=semver,pattern={{version}} 30 | type=semver,pattern={{major}}.{{minor}} 31 | type=semver,pattern={{major}} 32 | type=sha 33 | - 34 | name: Set up QEMU 35 | uses: docker/setup-qemu-action@v2 36 | - 37 | name: Set up Docker Buildx 38 | uses: docker/setup-buildx-action@v2 39 | - 40 | name: Login to GHCR 41 | uses: docker/login-action@v2 42 | with: 43 | registry: ghcr.io 44 | username: ${{ github.repository_owner }} 45 | password: ${{ secrets.GITHUB_TOKEN }} 46 | - 47 | name: Build and push 48 | uses: docker/build-push-action@v3 49 | with: 50 | file: Dockerfile.release 51 | context: . 52 | push: true 53 | tags: ${{ steps.meta.outputs.tags }} 54 | labels: ${{ steps.meta.outputs.labels }} 55 | -------------------------------------------------------------------------------- /.github/workflows/review.yml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | on: [pull_request] 3 | jobs: 4 | brakeman: 5 | name: runner / brakeman 6 | runs-on: ubuntu-latest 7 | container: 8 | image: ruby:3.2 9 | steps: 10 | - name: Check out code 11 | uses: actions/checkout@v3 12 | - name: brakeman 13 | uses: reviewdog/action-brakeman@v2 14 | with: 15 | github_token: ${{ secrets.github_token }} 16 | reporter: github-pr-review 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | /vendor/bundle 10 | 11 | # Ignore all logfiles and tempfiles. 12 | /log/* 13 | /tmp/* 14 | !/log/.keep 15 | !/tmp/.keep 16 | 17 | # Ignore uploaded files in development 18 | /storage/* 19 | 20 | /node_modules 21 | /yarn-error.log 22 | 23 | /public/assets 24 | .byebug_history 25 | 26 | # Ignore master key for decrypting credentials and more. 27 | /config/master.key 28 | 29 | .env 30 | 31 | /bundled_openapi.yml 32 | stub_server 33 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-performance 2 | 3 | AllCops: 4 | NewCops: 5 | enable 6 | Exclude: # 自動生成されるものはチェック対象から除外する 7 | - "bin/yarn" 8 | - "config.ru" 9 | - "Rakefile" 10 | - "node_modules/**/*" # rubocop config/default.yml 11 | - "vendor/**/*" # rubocop config/default.yml 12 | - "db/schema.rb" 13 | 14 | #################### Layout ################################ 15 | 16 | Layout/BeginEndAlignment: 17 | Enabled: true 18 | 19 | # メソッドをグループ分けして書き順を揃えておくと読みやすくなる。 20 | # 多少のツラミはあるかもしれない。 21 | # TODO: Categories を調整することで 22 | # https://github.com/pocke/rubocop-rails-order_model_declarative_methods 23 | # を再現できそう。 24 | Layout/ClassStructure: 25 | Enabled: true 26 | 27 | # メソッドチェーンの改行は末尾に . を入れる 28 | # * REPL に貼り付けた際の暴発を防ぐため 29 | # * 途中にコメントをはさむことができて実用上圧倒的に便利 30 | Layout/DotPosition: 31 | EnforcedStyle: trailing 32 | 33 | Layout/EmptyLinesAroundAttributeAccessor: 34 | Enabled: true 35 | 36 | # 桁揃えが綺麗にならないことが多いので migration は除外 37 | Layout/ExtraSpacing: 38 | Exclude: 39 | - "db/migrate/*.rb" 40 | 41 | # special_inside_parentheses (default) と比べて 42 | # * 横に長くなりづらい 43 | # * メソッド名の長さが変わったときに diff が少ない 44 | Layout/FirstArrayElementIndentation: 45 | EnforcedStyle: consistent 46 | 47 | # ({ と hash を開始した場合に ( の位置にインデントさせる 48 | # そもそも {} が必要ない可能性が高いが Style/BracesAroundHashParameters はチェックしないことにしたので 49 | Layout/FirstHashElementIndentation: 50 | EnforcedStyle: consistent 51 | 52 | # private/protected は一段深くインデントする 53 | Layout/IndentationConsistency: 54 | EnforcedStyle: indented_internal_methods 55 | 56 | # * 警告 120文字 57 | # * 禁止 160文字 58 | # のイメージ 59 | Layout/LineLength: 60 | Max: 160 61 | Exclude: 62 | - "db/migrate/*.rb" 63 | 64 | # メソッドチェーン感がより感じられるインデントにする 65 | Layout/MultilineMethodCallIndentation: 66 | EnforcedStyle: indented_relative_to_receiver 67 | 68 | Layout/SpaceAroundMethodCallOperator: 69 | Enabled: true 70 | 71 | # ブロック変数のところは {|var| ... } じゃなくて { |var| ... } 72 | Layout/SpaceInsideBlockBraces: 73 | SpaceBeforeBlockParameters: true 74 | 75 | # ガード句と本質を分けるのは良いコードスタイルなので有効化 76 | Layout/EmptyLineAfterGuardClause: 77 | Enabled: true 78 | 79 | #################### Lint ################################## 80 | 81 | # spec 内では 82 | # expect { subject }.to change { foo } 83 | # という書き方をよく行うので () を省略したい。 84 | # { foo } は明らかに change に紐付く。 85 | Lint/AmbiguousBlockAssociation: 86 | Exclude: 87 | - "spec/**/*_spec.rb" 88 | 89 | Lint/BinaryOperatorWithIdenticalOperands: 90 | Enabled: true 91 | 92 | Lint/ConstantDefinitionInBlock: 93 | Enabled: true 94 | 95 | Lint/DeprecatedOpenSSLConstant: 96 | Enabled: true 97 | 98 | Lint/DuplicateElsifCondition: 99 | Enabled: true 100 | 101 | Lint/DuplicateRequire: 102 | Enabled: true 103 | 104 | Lint/DuplicateRescueException: 105 | Enabled: true 106 | 107 | Lint/EmptyConditionalBody: 108 | Enabled: true 109 | 110 | Lint/EmptyFile: 111 | Enabled: true 112 | 113 | # Style/EmptyCaseCondition と同じく網羅の表現力が empty when を認めた方が高いし、 114 | # 頻出する対象を最初の when で撥ねるのはパフォーマンス向上で頻出する。 115 | # また、 116 | # case foo 117 | # when 42 118 | # # nop 119 | # when 1..100 120 | # ... 121 | # end 122 | # と、下の when がキャッチしてしまう場合等に対応していない。 123 | # See. http://tech.sideci.com/entry/2016/11/01/105900 124 | Lint/EmptyWhen: 125 | Enabled: false 126 | 127 | Lint/FloatComparison: 128 | Enabled: true 129 | 130 | Lint/IdentityComparison: 131 | Enabled: true 132 | 133 | # RuntimeError は「特定の Error を定義できない場合」なので、 134 | # 定義できるエラーは RuntimeError ではなく StandardError を継承する。 135 | Lint/InheritException: 136 | EnforcedStyle: standard_error 137 | 138 | Lint/MissingSuper: 139 | Enabled: true 140 | 141 | Lint/MixedRegexpCaptureTypes: 142 | Enabled: true 143 | 144 | Lint/OutOfRangeRegexpRef: 145 | Enabled: true 146 | 147 | Lint/RaiseException: 148 | Enabled: true 149 | 150 | Lint/SelfAssignment: 151 | Enabled: true 152 | 153 | Lint/StructNewOverride: 154 | Enabled: true 155 | 156 | Lint/TopLevelReturnWithArgument: 157 | Enabled: true 158 | 159 | Lint/TrailingCommaInAttributeDeclaration: 160 | Enabled: true 161 | 162 | # * 同名のメソッドがある場合にローカル変数に `_` を付ける 163 | # * 一時変数として `_` を付ける 164 | # というテクニックは頻出する 165 | Lint/UnderscorePrefixedVariableName: 166 | Enabled: false 167 | 168 | Lint/UnreachableLoop: 169 | Enabled: true 170 | 171 | # 子クラスで実装させるつもりで中身が 172 | # raise NotImplementedError 173 | # のみのメソッドが引っかかるので。 174 | # (raise せずに中身が空だと IgnoreEmptyMethods でセーフ) 175 | Lint/UnusedMethodArgument: 176 | Enabled: false 177 | 178 | Lint/UselessMethodDefinition: 179 | Enabled: true 180 | 181 | Lint/UselessTimes: 182 | Enabled: true 183 | 184 | # select 以外では引っかからないと思うので 185 | # mutating_methods のチェックを有効に。 186 | # TODO: select は引数が無い (ブロックのみ) の場合にだけチェックする 187 | # ようにすると誤検知がほぼ無くなる? 188 | Lint/Void: 189 | CheckForMethodsWithNoSideEffects: true 190 | 191 | #################### Metrics ############################### 192 | 193 | # 30 まではギリギリ許せる範囲だったけど 194 | # リリースごとに 3 ずつぐらい下げていきます。20 まで下げたい。 195 | Metrics/AbcSize: 196 | Max: 24 197 | 198 | # Gemfile, Guardfile は DSL 的で基本的に複雑にはならないので除外 199 | # rake, rspec, environments, routes は巨大な block 不可避なので除外 200 | # TODO: ExcludedMethods の精査 201 | Metrics/BlockLength: 202 | Exclude: 203 | - "Rakefile" 204 | - "**/*.rake" 205 | - "spec/**/*.rb" 206 | - "Gemfile" 207 | - "Guardfile" 208 | - "config/environments/*.rb" 209 | - "config/routes.rb" 210 | - "config/routes/**/*.rb" 211 | - "*.gemspec" 212 | 213 | # 6 は強すぎるので緩める 214 | Metrics/CyclomaticComplexity: 215 | Max: 10 216 | 217 | # 20 行超えるのは migration ファイル以外滅多に無い 218 | Metrics/MethodLength: 219 | Max: 20 220 | Exclude: 221 | - "db/migrate/*.rb" 222 | 223 | # 分岐の数。ガード句を多用しているとデフォルト 7 だと厳しい 224 | Metrics/PerceivedComplexity: 225 | Max: 8 226 | 227 | 228 | #################### Naming ################################ 229 | 230 | # has_ から始まるメソッドは許可する 231 | Naming/PredicateName: 232 | ForbiddenPrefixes: 233 | - "is_" 234 | - "have_" 235 | NamePrefix: 236 | - "is_" 237 | - "have_" 238 | 239 | # 3 文字未満だと指摘されるが、未使用を示す _ や e(rror), b(lock), 240 | # n(umber) といった 1 文字変数は頻出するし、前置詞(by, to, ...)や 241 | # よく知られた省略語 (op: operator とか pk: primary key とか) も妥当。 242 | # 変数 s にどんな文字列かを形容したい場合と、不要な場合とがある=無効 243 | Naming/MethodParameterName: 244 | Enabled: false 245 | 246 | #################### Performance ########################### 247 | 248 | Performance/AncestorsInclude: 249 | Enabled: true 250 | 251 | Performance/BigDecimalWithNumericArgument: 252 | Enabled: true 253 | 254 | # downcase or upcase しての比較はイディオムの域なので、多少の 255 | # パフォーマンスの違いがあろうが casecmp に変える意義を感じない 256 | Performance/Casecmp: 257 | Enabled: false 258 | 259 | Performance/RedundantSortBlock: 260 | Enabled: true 261 | 262 | Performance/RedundantStringChars: 263 | Enabled: true 264 | 265 | Performance/ReverseFirst: 266 | Enabled: true 267 | 268 | Performance/SortReverse: 269 | Enabled: true 270 | 271 | Performance/Squeeze: 272 | Enabled: true 273 | 274 | Performance/StringInclude: 275 | Enabled: true 276 | 277 | Performance/Sum: 278 | Enabled: true 279 | 280 | 281 | #################### Security ############################## 282 | 283 | # 毎回 YAML.safe_load(yaml_str, [Date, Time]) するのは面倒で。。 284 | Security/YAMLLoad: 285 | Enabled: false 286 | 287 | 288 | #################### Style ################################# 289 | 290 | Style/AccessorGrouping: 291 | Enabled: true 292 | 293 | # レキシカルスコープの扱いが alias_method の方が自然。 294 | # https://ernie.io/2014/10/23/in-defense-of-alias/ のように 295 | # 問題になる場合は自分で緩める。 296 | Style/Alias: 297 | EnforcedStyle: prefer_alias_method 298 | 299 | # redirect_to xxx and return のイディオムを維持したい 300 | Style/AndOr: 301 | EnforcedStyle: conditionals 302 | 303 | Style/ArrayCoercion: 304 | Enabled: true 305 | 306 | # 日本語のコメントを許可する 307 | Style/AsciiComments: 308 | Enabled: false 309 | 310 | Style/BisectedAttrAccessor: 311 | Enabled: true 312 | 313 | # ブロックの { .. } / do .. end は好きなように 314 | Style/BlockDelimiters: 315 | Enabled: false 316 | 317 | Style/CaseLikeIf: 318 | Enabled: true 319 | 320 | # scope が違うとか親 module の存在確認が必要とかデメリットはあるが、 321 | # namespace 付きのクラスはかなり頻繁に作るので簡単に書きたい。 322 | Style/ClassAndModuleChildren: 323 | Enabled: false 324 | 325 | # Style/CollectionMethods 自体は無効になっているのだが、 326 | # https://github.com/bbatsov/rubocop/issues/1084 327 | # https://github.com/bbatsov/rubocop/issues/1334 328 | # Performance/Detect がこの設定値を見るので PreferredMethods だけ変更しておく。 329 | # 330 | # デフォルト値から変えたのは 331 | # find -> detect 332 | # ActiveRecord の find と間違えやすいため 333 | # reduce -> inject 334 | # detect, reject, select と並べたときに韻を踏んでいるため。 335 | # collect -> map を維持しているのは文字数が圧倒的に少ないため。 336 | Style/CollectionMethods: 337 | PreferredMethods: 338 | detect: "detect" 339 | find: "detect" 340 | inject: "inject" 341 | reduce: "inject" 342 | 343 | Style/CombinableLoops: 344 | Enabled: true 345 | 346 | # ドキュメントの無い public class を許可する 347 | Style/Documentation: 348 | Enabled: false 349 | 350 | # !! のイディオムは積極的に使う 351 | Style/DoubleNegation: 352 | Enabled: false 353 | 354 | # case 355 | # when ios? 356 | # when android? 357 | # end 358 | # のようなものは case の方が網羅の表現力が高い 359 | Style/EmptyCaseCondition: 360 | Enabled: false 361 | 362 | # 明示的に else で nil を返すのは分かりやすいので許可する 363 | Style/EmptyElse: 364 | EnforcedStyle: empty 365 | 366 | # 空メソッドの場合だけ1行で書かなければいけない理由が無い 367 | # 「セミコロンは使わない」に寄せた方がルールがシンプル 368 | Style/EmptyMethod: 369 | EnforcedStyle: expanded 370 | 371 | Style/ExplicitBlockArgument: 372 | Enabled: true 373 | 374 | Style/ExponentialNotation: 375 | Enabled: true 376 | 377 | # いずれかに揃えるのならば `sprintf` や `format` より String#% が好きです 378 | Style/FormatString: 379 | EnforcedStyle: percent 380 | 381 | # まだ対応するには早い 382 | Style/FrozenStringLiteralComment: 383 | Enabled: false 384 | 385 | Style/GlobalStdStream: 386 | Enabled: true 387 | 388 | # if 文の中に 3 行程度のブロックを書くぐらいは許容した方が現実的 389 | # NOTE: https://github.com/bbatsov/rubocop/commit/29945958034db13af9e8ff385ec58cb9eb464596 390 | # の影響で、if 文の中身が 1 行の場合に警告されるようになっている。 391 | # Style/IfUnlessModifier の設定見てくれないかなぁ? (v0.36.0) 392 | Style/GuardClause: 393 | MinBodyLength: 5 394 | 395 | Style/HashAsLastArrayItem: 396 | Enabled: true 397 | 398 | # rake タスクの順序の hash は rocket を許可する 399 | Style/HashSyntax: 400 | Exclude: 401 | - "**/*.rake" 402 | - "Rakefile" 403 | 404 | Style/HashEachMethods: 405 | Enabled: true 406 | 407 | Style/HashLikeCase: 408 | Enabled: true 409 | 410 | Style/HashTransformKeys: 411 | Enabled: true 412 | 413 | Style/HashTransformValues: 414 | Enabled: true 415 | 416 | # 平たくしてしまうと条件のグルーピングが脳内モデルとズレやすい 417 | Style/IfInsideElse: 418 | Enabled: false 419 | 420 | # 条件式の方を意識させたい場合には後置の if/unless を使わない方が分かりやすい 421 | Style/IfUnlessModifier: 422 | Enabled: false 423 | 424 | Style/KeywordParametersOrder: 425 | Enabled: true 426 | 427 | # scope 等は複数行でも lambda ではなく ->{} で揃えた方が見た目が綺麗 428 | Style/Lambda: 429 | EnforcedStyle: literal 430 | 431 | # end.some_method とチェインするのはダサい 432 | # Style/BlockDelimiters と相性が悪いけど、頑張ってコードを修正してください 433 | Style/MethodCalledOnDoEndBlock: 434 | Enabled: true 435 | 436 | # この 2 つは単発で動かすのが分かっているので Object を汚染しても問題ない。 437 | # spec/dummy は Rails Engine を開発するときに絶対に引っかかるので入れておく。 438 | Style/MixinUsage: 439 | Exclude: 440 | - "bin/setup" 441 | - "bin/update" 442 | - "spec/dummy/bin/setup" 443 | - "spec/dummy/bin/update" 444 | 445 | # 複数行ブロックはお好きなように 446 | Style/MultilineBlockChain: 447 | Enabled: false 448 | 449 | # 1_000_000 と区切り文字が 2 個以上必要になる場合のみ _ 区切りを必須にする 450 | # 10_000_00 は許可しない。(これは例えば 10000 ドルをセント単位にする時に便利だが 451 | # 頻出しないので foolproof に振る 452 | Style/NumericLiterals: 453 | MinDigits: 7 454 | Strict: true 455 | 456 | # foo.positive? は foo > 0 に比べて意味が曖昧になる 457 | # foo.zero? は許可したいけどメソッドごとに指定できないので一括で disable に 458 | Style/NumericPredicate: 459 | Enabled: false 460 | 461 | Style/OptionalBooleanParameter: 462 | Enabled: true 463 | 464 | # falsy な場合という条件式の方を意識させたい場合がある。 465 | # Style/IfUnlessModifier と同じ雰囲気。 466 | Style/OrAssignment: 467 | Enabled: false 468 | 469 | # 正規表現にマッチさせた時の特殊変数の置き換えは Regex.last_match ではなく 470 | # 名前付きキャプチャを使って参照したいので auto-correct しない 471 | Style/PerlBackrefs: 472 | AutoCorrect: false 473 | 474 | # Hash#has_key? の方が key? よりも意味が通る 475 | Style/PreferredHashMethods: 476 | EnforcedStyle: verbose 477 | 478 | Style/RedundantAssignment: 479 | Enabled: true 480 | 481 | Style/RedundantFetchBlock: 482 | Enabled: true 483 | 484 | Style/RedundantFileExtensionInRequire: 485 | Enabled: true 486 | 487 | Style/RedundantRegexpCharacterClass: 488 | Enabled: true 489 | 490 | Style/RedundantRegexpEscape: 491 | Enabled: true 492 | 493 | # 受け取り側で multiple assignment しろというのを明示 494 | Style/RedundantReturn: 495 | AllowMultipleReturnValues: true 496 | 497 | # 特に model 内において、ローカル変数とメソッド呼び出しの区別をつけた方が分かりやすい場合が多い 498 | Style/RedundantSelf: 499 | Enabled: false 500 | 501 | Style/RedundantSelfAssignment: 502 | Enabled: true 503 | 504 | # 無指定だと StandardError を rescue するのは常識の範疇なので。 505 | Style/RescueStandardError: 506 | EnforcedStyle: implicit 507 | 508 | # user&.admin? が、[nil, true, false] の 3 値を返すことに一瞬で気づけず 509 | # boolean を返すっぽく見えてしまうので無効に。 510 | # user && user.admin? なら短絡評価で nil が返ってくるのが一目で分かるので。 511 | # (boolean を返すメソッド以外なら積極的に使いたいんだけどねぇ 512 | # 513 | # 他に auto-correct してはいけないパターンとして 514 | # if hoge && hoge.count > 1 515 | # がある。 516 | Style/SafeNavigation: 517 | Enabled: false 518 | 519 | # spec 内は見た目が綺麗になるので許可 520 | Style/Semicolon: 521 | Exclude: 522 | - "spec/**/*_spec.rb" 523 | 524 | # dig で揃えた方が読みやすいときがある 525 | Style/SingleArgumentDig: 526 | Enabled: false 527 | 528 | Style/SlicingWithRange: 529 | Enabled: true 530 | 531 | Style/SoleNestedConditional: 532 | Enabled: true 533 | 534 | Style/StringConcatenation: 535 | Enabled: true 536 | 537 | # * 式展開したい場合に書き換えるのが面倒 538 | # * 文章ではダブルクォートよりもシングルクォートの方が頻出する 539 | # ことから EnforcedStyle: double_quotes 推奨 540 | Style/StringLiterals: 541 | EnforcedStyle: double_quotes 542 | 543 | # 式展開中でもダブルクォートを使う 544 | # 普段の文字列リテラルがダブルクォートなので使い分けるのが面倒 545 | Style/StringLiteralsInInterpolation: 546 | EnforcedStyle: double_quotes 547 | 548 | # String#intern は ruby の内部表現すぎるので String#to_sym を使う 549 | Style/StringMethods: 550 | Enabled: true 551 | 552 | # %w() と %i() が見分けづらいので Style/WordArray と合わせて無効に。 553 | # 書き手に委ねるという意味で、Enabled: false にしています。使っても良い。 554 | Style/SymbolArray: 555 | Enabled: false 556 | 557 | # 三項演算子は分かりやすく使いたい。 558 | # () を外さない方が条件式が何なのか読み取りやすいと感じる。 559 | Style/TernaryParentheses: 560 | EnforcedStyle: require_parentheses_when_complex 561 | 562 | # 複数行の場合はケツカンマを入れる(引数) 563 | # Ruby は関数の引数もカンマを許容しているので 564 | # * 単行は常にケツカンマ無し 565 | # * 複数行は常にケツカンマ有り 566 | # に統一したい。 567 | # 見た目がアレだが、ES2017 でも関数引数のケツカンマが許容されるので 568 | # 世界はそちらに向かっている。 569 | Style/TrailingCommaInArguments: 570 | EnforcedStyleForMultiline: comma 571 | 572 | # 複数行の場合はケツカンマを入れる(Arrayリテラル) 573 | # JSON がケツカンマを許していないという反対意見もあるが、 574 | # 古い JScript の仕様に縛られる必要は無い。 575 | # IE9 以降はリテラルでケツカンマ OK なので正しい差分行の検出に寄せる。 576 | # 2 insertions(+), 1 deletion(-) ではなく、1 insertions 577 | Style/TrailingCommaInArrayLiteral: 578 | EnforcedStyleForMultiline: comma 579 | 580 | # 複数行の場合はケツカンマを入れる(Hashリテラル) 581 | Style/TrailingCommaInHashLiteral: 582 | EnforcedStyleForMultiline: comma 583 | 584 | # %w() と %i() が見分けづらいので Style/SymbolArray と合わせて無効に。 585 | # 書き手に委ねるという意味で、Enabled: false にしています。使っても良い。 586 | Style/WordArray: 587 | Enabled: false 588 | 589 | # 0 <= foo && foo < 5 のように数直線上に並べるのは 590 | # コードを読みやすくするテクニックなので forbid_for_equality_operators_only に。 591 | Style/YodaCondition: 592 | EnforcedStyle: forbid_for_equality_operators_only 593 | 594 | # 条件式で arr.size > 0 が使われた時に 595 | # if !arr.empty? 596 | # else 597 | # end 598 | # に修正されるのが嫌。 599 | # 中身を入れ替えて否定外しても良いんだけど、どちらが例外的な処理なのかが分かりづらくなる。 600 | Style/ZeroLengthPredicate: 601 | Enabled: false 602 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.5.0] - 2020-02-07 8 | ### Added 9 | - アクティビティの一覧、リポジトリごとにわけた表示も用意する [#150](https://github.com/sokusekiya/sokuseki/pull/150) 10 | - 自分のアクティビティを他者に共有できるリンクを作成できるようにする[#169](https://github.com/sokusekiya/sokuseki/pull/169) 11 | - OpenAPI 3.0を導入する [#176](https://github.com/sokusekiya/sokuseki/pull/176) 12 | 13 | ### Changed 14 | - グラフのラベルも term_string を表示する [#162](https://github.com/sokusekiya/sokuseki/pull/162) 15 | 16 | 17 | ## [1.4.0] - 2019-09-03 18 | ### Added 19 | - GitHub の username 変更に追随する [#142](https://github.com/sokusekiya/sokuseki/pull/142) 20 | - 期ごとに閲覧できるビューをつくる [#148](https://github.com/sokusekiya/sokuseki/pull/148) 21 | 22 | ### Changed 23 | - Rails 6 [#146](https://github.com/sokusekiya/sokuseki/pull/146) 24 | 25 | ## [1.3.0] - 2019-06-28 26 | 27 | ### Added 28 | - 全体のデザインを整える [#126](https://github.com/june29/sokuseki/pull/126) [#127](https://github.com/june29/sokuseki/pull/127) 29 | 30 | ### Changed 31 | - faviconの変更 [#121](https://github.com/june29/sokuseki/pull/121) 32 | 33 | ## [1.2.0] - 2019-05-21 34 | ### Added 35 | - favicon追加 [#85](https://github.com/june29/sokuseki/pull/85) 36 | - RSpecを導入 [#106](https://github.com/june29/sokuseki/pull/106) 37 | - トップページに1年間のアクティビティ推移を表示できるようにする [#111](https://github.com/june29/sokuseki/pull/111) [#112](https://github.com/june29/sokuseki/pull/112) 38 | 39 | ### Changed 40 | - Ruby 2.6.3対応 [#103](https://github.com/june29/sokuseki/pull/103) 41 | 42 | ### Removed 43 | - 使用していないomniauthのstrategy (developer) を削除 [#88](https://github.com/june29/sokuseki/pull/88) 44 | 45 | ## [1.1.0] - 2019-02-23 46 | 47 | ### Added 48 | - 月別アクティビティのチャート表示 [#75](https://github.com/june29/sokuseki/pull/75) [#78](https://github.com/june29/sokuseki/pull/78) 49 | 50 | ### Changed 51 | - Docker起動時に、tmp/pids/server.pid を削除 [#77](https://github.com/june29/sokuseki/pull/77) 52 | 53 | ## 1.0.0 - 2019-02-16 54 | ### Added 55 | - initial release! 56 | 57 | [Unreleased]: https://github.com/june29/sokuseki/compare/v1.4.0...HEAD 58 | [1.4.0]: https://github.com/june29/sokuseki/compare/v1.3.0...v1.4.0 59 | [1.3.0]: https://github.com/june29/sokuseki/compare/v1.2.0...v1.3.0 60 | [1.2.0]: https://github.com/june29/sokuseki/compare/v1.1.0...v1.2.0 61 | [1.1.0]: https://github.com/june29/sokuseki/compare/v1.0.0...v1.1.0 62 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.1.3 2 | RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs 3 | RUN mkdir /myapp 4 | WORKDIR /myapp 5 | COPY Gemfile /myapp/Gemfile 6 | COPY Gemfile.lock /myapp/Gemfile.lock 7 | RUN bundle install 8 | ENV ELASTIC_APM_ENABLED false 9 | COPY . /myapp 10 | -------------------------------------------------------------------------------- /Dockerfile.release: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:experimental 2 | FROM ruby:3.2.2 3 | RUN apt update -qqy && apt upgrade -qqy \ 4 | && apt-get clean \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | ENV PATH /opt/node/bin:$PATH 8 | ENV ELASTIC_APM_ENABLED false 9 | RUN useradd -m -u 1000 rails 10 | RUN mkdir -p /app /app/log /app/tmp && chown -R rails /app 11 | USER rails 12 | 13 | WORKDIR /app 14 | COPY --chown=rails Gemfile Gemfile.lock /app/ 15 | RUN gem install bundler 16 | RUN bundle config set app_config .bundle 17 | RUN bundle config set path .cache/bundle 18 | RUN --mount=type=cache,uid=1000,target=/app/.cache/bundle \ 19 | bundle install --without=development && \ 20 | mkdir -p vendor && \ 21 | cp -ar .cache/bundle vendor/bundle 22 | RUN bundle config set path vendor/bundle 23 | 24 | COPY --chown=rails . /app 25 | 26 | RUN --mount=type=cache,uid=1000,target=/app/tmp/cache bin/rails assets:precompile 27 | CMD ["bin/rails", "s", "-b", "0.0.0.0"] 28 | 29 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby "3.2.2" 5 | 6 | gem "rails", "~> 8.0.1" 7 | 8 | gem "bootsnap", ">= 1.1.0", require: false 9 | gem "jbuilder", "~> 2.13" 10 | gem "octokit" 11 | gem "omniauth-github" 12 | gem "omniauth-rails_csrf_protection" 13 | gem "parallel" 14 | gem "pg", ">= 0.18", "< 2.0" 15 | gem "puma", "~> 6.5" 16 | gem "sass-rails", "~> 6.0" 17 | gem "sentry-raven" 18 | gem "slim-rails" 19 | gem "uglifier", ">= 1.3.0" 20 | 21 | group :development, :test do 22 | gem "byebug", platforms: [:mri, :mingw, :x64_mingw] 23 | gem "dotenv-rails" 24 | gem "factory_bot_rails" 25 | gem "mini_racer" 26 | gem "pry" 27 | gem "rspec-rails", "~> 6.1" 28 | gem "rubocop", require: false 29 | gem "rubocop-performance" 30 | end 31 | 32 | group :development do 33 | gem "listen", ">= 3.0.5", "< 3.10" 34 | gem "spring", "~> 4.2" 35 | gem "spring-watcher-listen" 36 | gem "web-console", ">= 3.3.0" 37 | gem "yaml_ref_resolver" 38 | end 39 | gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] 40 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (8.0.1) 5 | actionpack (= 8.0.1) 6 | activesupport (= 8.0.1) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | zeitwerk (~> 2.6) 10 | actionmailbox (8.0.1) 11 | actionpack (= 8.0.1) 12 | activejob (= 8.0.1) 13 | activerecord (= 8.0.1) 14 | activestorage (= 8.0.1) 15 | activesupport (= 8.0.1) 16 | mail (>= 2.8.0) 17 | actionmailer (8.0.1) 18 | actionpack (= 8.0.1) 19 | actionview (= 8.0.1) 20 | activejob (= 8.0.1) 21 | activesupport (= 8.0.1) 22 | mail (>= 2.8.0) 23 | rails-dom-testing (~> 2.2) 24 | actionpack (8.0.1) 25 | actionview (= 8.0.1) 26 | activesupport (= 8.0.1) 27 | nokogiri (>= 1.8.5) 28 | rack (>= 2.2.4) 29 | rack-session (>= 1.0.1) 30 | rack-test (>= 0.6.3) 31 | rails-dom-testing (~> 2.2) 32 | rails-html-sanitizer (~> 1.6) 33 | useragent (~> 0.16) 34 | actiontext (8.0.1) 35 | actionpack (= 8.0.1) 36 | activerecord (= 8.0.1) 37 | activestorage (= 8.0.1) 38 | activesupport (= 8.0.1) 39 | globalid (>= 0.6.0) 40 | nokogiri (>= 1.8.5) 41 | actionview (8.0.1) 42 | activesupport (= 8.0.1) 43 | builder (~> 3.1) 44 | erubi (~> 1.11) 45 | rails-dom-testing (~> 2.2) 46 | rails-html-sanitizer (~> 1.6) 47 | activejob (8.0.1) 48 | activesupport (= 8.0.1) 49 | globalid (>= 0.3.6) 50 | activemodel (8.0.1) 51 | activesupport (= 8.0.1) 52 | activerecord (8.0.1) 53 | activemodel (= 8.0.1) 54 | activesupport (= 8.0.1) 55 | timeout (>= 0.4.0) 56 | activestorage (8.0.1) 57 | actionpack (= 8.0.1) 58 | activejob (= 8.0.1) 59 | activerecord (= 8.0.1) 60 | activesupport (= 8.0.1) 61 | marcel (~> 1.0) 62 | activesupport (8.0.1) 63 | base64 64 | benchmark (>= 0.3) 65 | bigdecimal 66 | concurrent-ruby (~> 1.0, >= 1.3.1) 67 | connection_pool (>= 2.2.5) 68 | drb 69 | i18n (>= 1.6, < 2) 70 | logger (>= 1.4.2) 71 | minitest (>= 5.1) 72 | securerandom (>= 0.3) 73 | tzinfo (~> 2.0, >= 2.0.5) 74 | uri (>= 0.13.1) 75 | addressable (2.8.7) 76 | public_suffix (>= 2.0.2, < 7.0) 77 | ast (2.4.2) 78 | base64 (0.2.0) 79 | benchmark (0.4.0) 80 | bigdecimal (3.1.9) 81 | bindex (0.8.1) 82 | bootsnap (1.18.4) 83 | msgpack (~> 1.2) 84 | builder (3.3.0) 85 | byebug (11.1.3) 86 | coderay (1.1.3) 87 | concurrent-ruby (1.3.4) 88 | connection_pool (2.4.1) 89 | crass (1.0.6) 90 | date (3.4.1) 91 | diff-lcs (1.5.1) 92 | dotenv (3.1.7) 93 | dotenv-rails (3.1.7) 94 | dotenv (= 3.1.7) 95 | railties (>= 6.1) 96 | drb (2.2.1) 97 | erubi (1.13.1) 98 | execjs (2.9.1) 99 | factory_bot (6.5.0) 100 | activesupport (>= 5.0.0) 101 | factory_bot_rails (6.4.4) 102 | factory_bot (~> 6.5) 103 | railties (>= 5.0.0) 104 | faraday (2.12.0) 105 | faraday-net_http (>= 2.0, < 3.4) 106 | json 107 | logger 108 | faraday-net_http (3.3.0) 109 | net-http 110 | ffi (1.16.3) 111 | globalid (1.2.1) 112 | activesupport (>= 6.1) 113 | hashie (5.0.0) 114 | i18n (1.14.6) 115 | concurrent-ruby (~> 1.0) 116 | io-console (0.8.0) 117 | irb (1.14.3) 118 | rdoc (>= 4.0.0) 119 | reline (>= 0.4.2) 120 | jbuilder (2.13.0) 121 | actionview (>= 5.0.0) 122 | activesupport (>= 5.0.0) 123 | json (2.9.1) 124 | jwt (2.7.1) 125 | language_server-protocol (3.17.0.3) 126 | libv8-node (18.19.0.0-x86_64-linux) 127 | listen (3.9.0) 128 | rb-fsevent (~> 0.10, >= 0.10.3) 129 | rb-inotify (~> 0.9, >= 0.9.10) 130 | logger (1.6.4) 131 | loofah (2.23.1) 132 | crass (~> 1.0.2) 133 | nokogiri (>= 1.12.0) 134 | mail (2.8.1) 135 | mini_mime (>= 0.1.1) 136 | net-imap 137 | net-pop 138 | net-smtp 139 | marcel (1.0.4) 140 | method_source (1.1.0) 141 | mini_mime (1.1.5) 142 | mini_racer (0.16.0) 143 | libv8-node (~> 18.19.0.0) 144 | minitest (5.25.4) 145 | msgpack (1.7.2) 146 | multi_xml (0.6.0) 147 | net-http (0.4.1) 148 | uri 149 | net-imap (0.5.1) 150 | date 151 | net-protocol 152 | net-pop (0.1.2) 153 | net-protocol 154 | net-protocol (0.2.2) 155 | timeout 156 | net-smtp (0.5.0) 157 | net-protocol 158 | nio4r (2.7.4) 159 | nokogiri (1.18.1-x86_64-linux-gnu) 160 | racc (~> 1.4) 161 | oauth2 (2.0.9) 162 | faraday (>= 0.17.3, < 3.0) 163 | jwt (>= 1.0, < 3.0) 164 | multi_xml (~> 0.5) 165 | rack (>= 1.2, < 4) 166 | snaky_hash (~> 2.0) 167 | version_gem (~> 1.1) 168 | octokit (9.2.0) 169 | faraday (>= 1, < 3) 170 | sawyer (~> 0.9) 171 | omniauth (2.1.2) 172 | hashie (>= 3.4.6) 173 | rack (>= 2.2.3) 174 | rack-protection 175 | omniauth-github (2.0.1) 176 | omniauth (~> 2.0) 177 | omniauth-oauth2 (~> 1.8) 178 | omniauth-oauth2 (1.8.0) 179 | oauth2 (>= 1.4, < 3) 180 | omniauth (~> 2.0) 181 | omniauth-rails_csrf_protection (1.0.2) 182 | actionpack (>= 4.2) 183 | omniauth (~> 2.0) 184 | parallel (1.26.3) 185 | parser (3.3.7.0) 186 | ast (~> 2.4.1) 187 | racc 188 | pg (1.5.9) 189 | pry (0.15.2) 190 | coderay (~> 1.1) 191 | method_source (~> 1.0) 192 | psych (5.2.2) 193 | date 194 | stringio 195 | public_suffix (6.0.1) 196 | puma (6.5.0) 197 | nio4r (~> 2.0) 198 | racc (1.8.1) 199 | rack (3.1.8) 200 | rack-protection (4.0.0) 201 | base64 (>= 0.1.0) 202 | rack (>= 3.0.0, < 4) 203 | rack-session (2.0.0) 204 | rack (>= 3.0.0) 205 | rack-test (2.2.0) 206 | rack (>= 1.3) 207 | rackup (2.2.1) 208 | rack (>= 3) 209 | rails (8.0.1) 210 | actioncable (= 8.0.1) 211 | actionmailbox (= 8.0.1) 212 | actionmailer (= 8.0.1) 213 | actionpack (= 8.0.1) 214 | actiontext (= 8.0.1) 215 | actionview (= 8.0.1) 216 | activejob (= 8.0.1) 217 | activemodel (= 8.0.1) 218 | activerecord (= 8.0.1) 219 | activestorage (= 8.0.1) 220 | activesupport (= 8.0.1) 221 | bundler (>= 1.15.0) 222 | railties (= 8.0.1) 223 | rails-dom-testing (2.2.0) 224 | activesupport (>= 5.0.0) 225 | minitest 226 | nokogiri (>= 1.6) 227 | rails-html-sanitizer (1.6.2) 228 | loofah (~> 2.21) 229 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 230 | railties (8.0.1) 231 | actionpack (= 8.0.1) 232 | activesupport (= 8.0.1) 233 | irb (~> 1.13) 234 | rackup (>= 1.0.0) 235 | rake (>= 12.2) 236 | thor (~> 1.0, >= 1.2.2) 237 | zeitwerk (~> 2.6) 238 | rainbow (3.1.1) 239 | rake (13.2.1) 240 | rb-fsevent (0.11.2) 241 | rb-inotify (0.10.1) 242 | ffi (~> 1.0) 243 | rdoc (6.10.0) 244 | psych (>= 4.0.0) 245 | regexp_parser (2.10.0) 246 | reline (0.6.0) 247 | io-console (~> 0.5) 248 | rspec-core (3.13.0) 249 | rspec-support (~> 3.13.0) 250 | rspec-expectations (3.13.1) 251 | diff-lcs (>= 1.2.0, < 2.0) 252 | rspec-support (~> 3.13.0) 253 | rspec-mocks (3.13.1) 254 | diff-lcs (>= 1.2.0, < 2.0) 255 | rspec-support (~> 3.13.0) 256 | rspec-rails (6.1.4) 257 | actionpack (>= 6.1) 258 | activesupport (>= 6.1) 259 | railties (>= 6.1) 260 | rspec-core (~> 3.13) 261 | rspec-expectations (~> 3.13) 262 | rspec-mocks (~> 3.13) 263 | rspec-support (~> 3.13) 264 | rspec-support (3.13.1) 265 | rubocop (1.71.0) 266 | json (~> 2.3) 267 | language_server-protocol (>= 3.17.0) 268 | parallel (~> 1.10) 269 | parser (>= 3.3.0.2) 270 | rainbow (>= 2.2.2, < 4.0) 271 | regexp_parser (>= 2.9.3, < 3.0) 272 | rubocop-ast (>= 1.36.2, < 2.0) 273 | ruby-progressbar (~> 1.7) 274 | unicode-display_width (>= 2.4.0, < 4.0) 275 | rubocop-ast (1.37.0) 276 | parser (>= 3.3.1.0) 277 | rubocop-performance (1.23.1) 278 | rubocop (>= 1.48.1, < 2.0) 279 | rubocop-ast (>= 1.31.1, < 2.0) 280 | ruby-progressbar (1.13.0) 281 | sass-rails (6.0.0) 282 | sassc-rails (~> 2.1, >= 2.1.1) 283 | sassc (2.4.0) 284 | ffi (~> 1.9) 285 | sassc-rails (2.1.2) 286 | railties (>= 4.0.0) 287 | sassc (>= 2.0) 288 | sprockets (> 3.0) 289 | sprockets-rails 290 | tilt 291 | sawyer (0.9.2) 292 | addressable (>= 2.3.5) 293 | faraday (>= 0.17.3, < 3) 294 | securerandom (0.4.1) 295 | sentry-raven (3.1.2) 296 | faraday (>= 1.0) 297 | slim (5.2.1) 298 | temple (~> 0.10.0) 299 | tilt (>= 2.1.0) 300 | slim-rails (3.7.0) 301 | actionpack (>= 3.1) 302 | railties (>= 3.1) 303 | slim (>= 3.0, < 6.0, != 5.0.0) 304 | snaky_hash (2.0.1) 305 | hashie 306 | version_gem (~> 1.1, >= 1.1.1) 307 | spring (4.2.1) 308 | spring-watcher-listen (2.1.0) 309 | listen (>= 2.7, < 4.0) 310 | spring (>= 4) 311 | sprockets (4.2.1) 312 | concurrent-ruby (~> 1.0) 313 | rack (>= 2.2.4, < 4) 314 | sprockets-rails (3.4.2) 315 | actionpack (>= 5.2) 316 | activesupport (>= 5.2) 317 | sprockets (>= 3.0.0) 318 | stringio (3.1.2) 319 | temple (0.10.3) 320 | thor (1.3.2) 321 | tilt (2.5.0) 322 | timeout (0.4.2) 323 | tzinfo (2.0.6) 324 | concurrent-ruby (~> 1.0) 325 | uglifier (4.2.1) 326 | execjs (>= 0.3.0, < 3) 327 | unicode-display_width (3.1.4) 328 | unicode-emoji (~> 4.0, >= 4.0.4) 329 | unicode-emoji (4.0.4) 330 | uri (1.0.2) 331 | useragent (0.16.11) 332 | version_gem (1.1.3) 333 | web-console (4.2.1) 334 | actionview (>= 6.0.0) 335 | activemodel (>= 6.0.0) 336 | bindex (>= 0.4.0) 337 | railties (>= 6.0.0) 338 | websocket-driver (0.7.6) 339 | websocket-extensions (>= 0.1.0) 340 | websocket-extensions (0.1.5) 341 | yaml_ref_resolver (0.7.1) 342 | zeitwerk (2.7.1) 343 | 344 | PLATFORMS 345 | x86_64-linux 346 | 347 | DEPENDENCIES 348 | bootsnap (>= 1.1.0) 349 | byebug 350 | dotenv-rails 351 | factory_bot_rails 352 | jbuilder (~> 2.13) 353 | listen (>= 3.0.5, < 3.10) 354 | mini_racer 355 | octokit 356 | omniauth-github 357 | omniauth-rails_csrf_protection 358 | parallel 359 | pg (>= 0.18, < 2.0) 360 | pry 361 | puma (~> 6.5) 362 | rails (~> 8.0.1) 363 | rspec-rails (~> 6.1) 364 | rubocop 365 | rubocop-performance 366 | sass-rails (~> 6.0) 367 | sentry-raven 368 | slim-rails 369 | spring (~> 4.2) 370 | spring-watcher-listen 371 | tzinfo-data 372 | uglifier (>= 1.3.0) 373 | web-console (>= 3.3.0) 374 | yaml_ref_resolver 375 | 376 | RUBY VERSION 377 | ruby 3.2.2p53 378 | 379 | BUNDLED WITH 380 | 2.2.3 381 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: release_major 2 | ## release_major: release nke (major) 3 | release_major: releasedeps 4 | git semv major --bump 5 | 6 | .PHONY: release_minor 7 | ## release_minor: release nke (minor) 8 | release_minor: releasedeps 9 | git semv minor --bump 10 | 11 | .PHONY: release_patch 12 | ## release_patch: release nke (patch) 13 | release_patch: releasedeps 14 | git semv patch --bump 15 | 16 | .PHONY: releasedeps 17 | releasedeps: git-semv 18 | 19 | .PHONY: git-semv 20 | git-semv: 21 | which git-semv > /dev/null || brew tap linyows/git-semv 22 | which git-semv > /dev/null || brew install git-semv 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sokuseki 2 | 3 | 足跡 4 | 5 | [![CircleCI](https://circleci.com/gh/sokusekiya/sokuseki/tree/master.svg?style=svg)](https://circleci.com/gh/sokusekiya/sokuseki/tree/master) 6 | 7 | ## 開発のはじめかた 8 | 9 | ### 開発環境の起動をする 10 | 11 | ```bash 12 | $ docker-compose up -d 13 | $ docker-compose run --rm web bundle 14 | $ docker-compose run --rm web bin/rails db:create db:migrate 15 | ``` 16 | 17 | ### GitHub Enterpriseの情報を取得して、個人ページを表示する 18 | https://github.example.com/settings/applications/new で、OAuth Appを作成して、`Client ID`, `Client Secret`を取得する 19 | 20 | `.env` ファイルを作成する 21 | 22 | ```bash 23 | $ echo GHE_APP_KEY="YOUR_CLIENT_ID" >> .env 24 | $ echo GHE_APP_SECRET="YOUR_CLIENT_SECRET" >> .env 25 | $ echo GHE_HOST="github.example.com" >> .env 26 | ``` 27 | 28 | Dockerを起動し直してからサインインすると、個人ページが表示できる 29 | 30 | ## Stub Serverの立ち上げ方 31 | 32 | ```sh 33 | $ bin/generate_stub_server.sh 34 | $ bin/run_stub_server.sh 35 | ``` -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /api_schema/components/schemas/peopleProperties.yml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - name 5 | properties: 6 | id: 7 | type: integer 8 | example: 1 9 | name: 10 | type: string 11 | example: june29 12 | -------------------------------------------------------------------------------- /api_schema/openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.2 2 | info: 3 | title: Sokuseki API 4 | version: 0.0.1 5 | servers: 6 | - url: http://localhost:3000 7 | description: development 8 | paths: 9 | /people: 10 | $ref: "./paths/people.yml" 11 | components: 12 | schemas: 13 | PeopleProperties: 14 | $ref: "./components/schemas/peopleProperties.yml" 15 | -------------------------------------------------------------------------------- /api_schema/paths/people.yml: -------------------------------------------------------------------------------- 1 | get: 2 | description: Sokuseki屋さんたち 3 | operationId: getPeople 4 | responses: 5 | "200": 6 | description: A paged array of Sokuseki Ya 7 | content: 8 | application/json: 9 | schema: 10 | type: array 11 | items: 12 | $ref: "../openapi.yml#/components/schemas/PeopleProperties" 13 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/logo-horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 18 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 48 | 55 | 61 | 67 | 80 | 85 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/assets/images/logo-vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 18 | 25 | 31 | 37 | 50 | 55 | 61 | 64 | 72 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/assets/images/sign-in-user-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require activestorage 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.erb: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | body { 17 | background: #fafafa; 18 | } 19 | .header { 20 | background: #333333; 21 | display: flex; 22 | color: #dddddd; 23 | justify-content: space-between; 24 | padding: 1em; 25 | } 26 | .header-logo { 27 | color: #ffffff; 28 | font-weight: bold; 29 | width: 130px; 30 | display: block; 31 | } 32 | .content { 33 | padding: 0 2em; 34 | height: 90vh; 35 | } 36 | .before-signed-in { 37 | display: flex; 38 | align-items: center; 39 | justify-content: center; 40 | width: 100%; 41 | height: 100%; 42 | } 43 | .before-signed-in__content { 44 | display: flex; 45 | flex-direction: column; 46 | align-items: center; 47 | justify-content: center; 48 | border: 2px solid #e5e5e5; 49 | border-bottom-width: 3px; 50 | padding: 3em 0 0; 51 | border-radius: 10px; 52 | background: #ffffff; 53 | } 54 | .before-signed-in__title { 55 | width: 150px; 56 | margin-bottom: .2em !important; 57 | } 58 | .before-signed-in__text { 59 | margin-bottom: 3.5em !important; 60 | } 61 | .before-signed-in__button-area { 62 | padding: 3.5em 5em; 63 | border-top: 2px solid #e5e5e5; 64 | background: #f5f5f5; 65 | position: relative; 66 | border-radius: 0 0 8px 8px; 67 | } 68 | .before-signed-in__button-area::before { 69 | content: url(<%= asset_path "sign-in-user-icon.svg" %>); 70 | width: 60px; 71 | height: 60px; 72 | display: block; 73 | position: absolute; 74 | top: -30px; 75 | left: 50%; 76 | margin-left: -30px; 77 | } -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/activities/on_term_controller.rb: -------------------------------------------------------------------------------- 1 | class Activities::OnTermController < ApplicationController 2 | before_action :redirect_to_root_unless_signed_in 3 | 4 | def index 5 | @term_string = params[:term_string] 6 | @owner_name = current_user.name 7 | @activities = current_user.activities.on(@term_string) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include SessionsHelper 3 | 4 | def redirect_to_root_unless_signed_in 5 | unless signed_in? 6 | flash[:notice] = "サインインしてください" 7 | 8 | redirect_to root_path 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | def create 3 | omniauth = request.env["omniauth.auth"] 4 | provider = omniauth.dig("provider") 5 | uid = omniauth.dig("uid") 6 | 7 | authentication = Authentication.find_by(provider:, uid:) 8 | 9 | if authentication 10 | authentication.update( 11 | name: omniauth.dig("info", "nickname"), 12 | access_token: omniauth.dig("credentials", "token"), 13 | ) 14 | 15 | sign_in authentication.user 16 | else 17 | sign_in User.create_with(omniauth) 18 | end 19 | 20 | redirect_to root_path 21 | end 22 | 23 | def destroy 24 | sign_out 25 | 26 | redirect_to root_path 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/shared_links_controller.rb: -------------------------------------------------------------------------------- 1 | class SharedLinksController < ApplicationController 2 | before_action :redirect_to_root_unless_signed_in, :shared_link_params 3 | 4 | def show 5 | shared_link = SharedLink.available.find_by(token: shared_link_params[:token]) 6 | return redirect_to root_path unless shared_link 7 | 8 | user = shared_link.user 9 | @owner_name = user.name 10 | @term_string = shared_link.on 11 | @activities = user.activities.on(@term_string) 12 | render "activities/on_term/index" 13 | end 14 | 15 | def create 16 | shared_link = current_user.shared_links.build(on: shared_link_params[:on]) 17 | if shared_link.save 18 | flash[:success] = "共有リンクを作成しました" 19 | else 20 | flash[:error] = "共有リンクの作成に失敗しました" 21 | end 22 | 23 | redirect_to root_path 24 | end 25 | 26 | def destroy 27 | available_link = current_user.shared_links.available.find_by(token: shared_link_params[:token]) 28 | return redirect_to root_path unless available_link 29 | 30 | if available_link.destroy 31 | flash[:success] = "共有リンクを削除しました" 32 | else 33 | flash[:error] = "共有リンクの削除に失敗しました" 34 | end 35 | 36 | redirect_to root_path 37 | end 38 | 39 | private 40 | 41 | def shared_link_params 42 | params.permit(:on, :token) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | TERMS = %w[1st 2nd].freeze 3 | 4 | def index 5 | return unless signed_in? 6 | 7 | @activities = current_user.activities 8 | @terms = extract_terms(@activities) 9 | @months = extract_months(@activities) 10 | @target_duration = (0..12).map { |n| n.month.ago.strftime("%Y-%m") }.reverse 11 | @annual_activities = extract_annual(@activities) 12 | @shared_links = extract_shared_links(current_user.shared_links) 13 | 14 | current_user.authentications.each(&:fetch_activities) if @activities.count.zero? 15 | end 16 | 17 | private 18 | 19 | def extract_terms(activities) 20 | activities.distinct.select("to_char(acted_at, 'YYYY') as year", 21 | "to_char(acted_at, 'MM') as month").map { |a| 22 | "%d-%s" % { year: a.year, term: TERMS[a.month.to_i / 7] } 23 | }.uniq.sort.reverse 24 | end 25 | 26 | def extract_months(activities) 27 | activities.distinct.select("to_char(acted_at, 'YYYY-MM') as ym").map(&:ym).uniq.sort.reverse 28 | end 29 | 30 | def extract_annual(activities) 31 | activities.in_the_last_year. 32 | group("TO_CHAR(acted_at, 'YYYY-MM')", :activity_type).order(Arel.sql("TO_CHAR(acted_at, 'YYYY-MM')")).count. 33 | each_with_object(Hash.new { |h, k| h[k] = {} }) { |(key, value), result| 34 | acted_month, activity_type = key 35 | result[activity_type][acted_month] = value 36 | } 37 | end 38 | 39 | def extract_shared_links(shared_links) 40 | shared_links.each_with_object({}) do |obj, hash| 41 | hash[obj.on] = { expired_at: obj.expired_at.strftime("%m-%d %H:%M"), token: obj.token } 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/helpers/activities_helper.rb: -------------------------------------------------------------------------------- 1 | module ActivitiesHelper 2 | def markdown_list(activities) 3 | activities.reject { |activity| 4 | activity.activity_type == "IssuesEvent" && activity.original_data["action"] != "opened" 5 | }.map { |activity| 6 | data = activity.original_data 7 | target = (activity.activity_type == "IssuesEvent") ? data["issue"] : data["pull_request"] 8 | 9 | "- [%s](%<url>s)" % { title: target["title"], url: target["html_url"] } 10 | }.uniq.join("\n") 11 | end 12 | 13 | def markdown_list_group_by_repository(activities) 14 | activities.reject { |activity| 15 | activity.activity_type == "IssuesEvent" && activity.original_data["action"] != "opened" 16 | }.reject { |activity| 17 | activity.repo_name.nil? 18 | }.group_by(&:repo_name).map { |repo_name, repo_activities| 19 | list = 20 | repo_activities.map { |activity| 21 | data = activity.original_data 22 | target = (activity.activity_type == "IssuesEvent") ? data["issue"] : data["pull_request"] 23 | 24 | "- [%<title>s](%<url>s)" % { title: target["title"], url: target["html_url"] } 25 | }.uniq.join("\n") 26 | 27 | "### #{repo_name}\n\n#{list}\n" 28 | }.join("\n") 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | def sign_in(user) 3 | session[:user_id] = user.id 4 | @current_user = user 5 | end 6 | 7 | def sign_out 8 | @current_user = nil 9 | session.delete(:user_id) 10 | end 11 | 12 | def signed_in? 13 | !!self.current_user 14 | end 15 | 16 | def current_user 17 | return if session[:user_id].nil? 18 | 19 | @current_user ||= User.find_by(id: session[:user_id]) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /app/models/activity.rb: -------------------------------------------------------------------------------- 1 | class Activity < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :authentication 4 | 5 | validates :activity_type, presence: true 6 | validates :activity_id, presence: true, uniqueness: { scope: :activity_type } 7 | validates :acted_at, presence: true 8 | 9 | scope :on, ->(string) { 10 | if string =~ /(\d{4})-(1st|2nd)/ 11 | year = Regexp.last_match[1].to_i 12 | 13 | if Regexp.last_match[2] == "1st" 14 | where(acted_at: (Time.new(year))..(Time.new(year, 6).end_of_month)) 15 | else 16 | where(acted_at: (Time.new(year, 7))..(Time.new(year, 12).end_of_month)) 17 | end 18 | else 19 | where(acted_at: Time.zone.parse("#{string}-01").all_month) 20 | end 21 | } 22 | 23 | scope :in_the_last_year, -> { 24 | where("acted_at >= ?", 1.year.ago.beginning_of_month) 25 | } 26 | 27 | scope :issue_and_pr, -> { 28 | where(activity_type: %w[IssuesEvent PullRequestEvent]) 29 | } 30 | 31 | def repo_name 32 | original_data.dig("repo", "name") 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/authentication.rb: -------------------------------------------------------------------------------- 1 | class Authentication < ApplicationRecord 2 | belongs_to :user 3 | has_many :activities 4 | 5 | validates :provider, presence: true 6 | validates :uid, presence: true, uniqueness: { scope: :provider } 7 | validates :name, presence: true 8 | validates :access_token, presence: true 9 | 10 | def fetch_activities 11 | case provider 12 | when "github" 13 | fetch_activities_from_github 14 | else 15 | [] 16 | end 17 | rescue => e 18 | Rails.logger.info e 19 | [] 20 | end 21 | 22 | def fetch_activities_from_github 23 | events = github_client.user_events(name) 24 | 25 | events.each do |event| 26 | activity_id = event.id.to_s 27 | activity_type = event.type 28 | 29 | activity = Activity.find_or_initialize_by( 30 | user_id: self.user_id, 31 | authentication_id: self.id, 32 | activity_id:, 33 | activity_type:, 34 | ) 35 | 36 | acted_at = event.created_at 37 | original_data = event.payload.to_h 38 | 39 | original_data[:repo] = event.repo.to_h if event.repo 40 | 41 | activity.acted_at = acted_at 42 | activity.original_data = original_data 43 | 44 | activity.save 45 | end 46 | end 47 | 48 | def github_client 49 | @github_client ||= Octokit::Client.new(access_token:) 50 | @github_client.auto_paginate = true 51 | 52 | @github_client 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/shared_link.rb: -------------------------------------------------------------------------------- 1 | class SharedLink < ApplicationRecord 2 | belongs_to :user 3 | before_validation :build_param 4 | 5 | validates :token, presence: true, uniqueness: true, length: { maximum: 255 } 6 | validates :on, presence: true, length: { maximum: 255 } 7 | 8 | scope :available, -> { 9 | where("expired_at > ?", Time.current) 10 | } 11 | 12 | scope :on, ->(term) { 13 | where(on: term) 14 | } 15 | 16 | private 17 | 18 | def build_param 19 | self.token = SecureRandom.hex(32) 20 | self.expired_at = ENV.fetch("SOKUSEKI_SHARED_LINK_TTL", 30).to_i.minutes.after 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :authentications 3 | has_many :activities 4 | has_many :shared_links 5 | 6 | validates :name, presence: true, uniqueness: true 7 | validates :avatar_url, presence: true 8 | 9 | def self.create_with(omniauth) 10 | provider = omniauth.dig("provider") 11 | uid = omniauth.dig("uid") 12 | name = omniauth.dig("info", "nickname") 13 | avatar_url = omniauth.dig("info", "image") 14 | access_token = omniauth.dig("credentials", "token") 15 | 16 | ApplicationRecord.transaction do 17 | user = User.create( 18 | name:, 19 | avatar_url:, 20 | ) 21 | user.authentications.create( 22 | provider:, 23 | uid:, 24 | name:, 25 | access_token:, 26 | ) 27 | 28 | user 29 | end 30 | end 31 | 32 | def has_available_link_on?(term) 33 | @shared_links ||= shared_links.available.all 34 | @shared_links.find { |s| s.on == term } 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/views/activities/github/_event.html.slim: -------------------------------------------------------------------------------- 1 | - data = activity.original_data 2 | - acted_at = activity.acted_at 3 | 4 | li 5 | => acted_at.strftime("%Y-%m-%d %H:%M:%S") 6 | ' : 7 | - case activity.activity_type 8 | - when "CheckRunEvent" 9 | | CheckRunEvent 10 | - when "CheckSuiteEvent" 11 | | CheckSuiteEvent 12 | - when "CommitCommentEvent" 13 | - repo_name = data.dig("repo", "name") 14 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 15 | - commit_id = data.dig('comment', 'commit_id') 16 | - commit_url = "#{repo_url}/commit/#{commit_id}" 17 | ' リポジトリ 18 | => link_to repo_name, repo_url 19 | ' のコミット 20 | => link_to commit_id[0..6], commit_url 21 | ' にコメントした 22 | - when "CreateEvent" 23 | - case data.dig("ref_type") 24 | - when "branch" 25 | - branch_name = data.dig('ref') 26 | - repo_name = data.dig("repo", "name") 27 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 28 | ' リポジトリ 29 | => link_to repo_name, repo_url 30 | ' にブランチ 31 | => branch_name 32 | ' を作成した 33 | - when "repository" 34 | - repo_name = data.dig("repo", "name") 35 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 36 | ' リポジトリ 37 | => link_to repo_name, repo_url 38 | ' を作成した 39 | - when "tag" 40 | - else 41 | ' CreateEvent 42 | = data.dig("ref_type") 43 | - when "DeleteEvent" 44 | - case data.dig("ref_type") 45 | - when "branch" 46 | - branch_name = data.dig('ref') 47 | - repo_name = data.dig("repo", "name") 48 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 49 | ' リポジトリ 50 | => link_to repo_name, repo_url 51 | ' のブランチ 52 | => branch_name 53 | ' を削除した 54 | - when "repository" 55 | - when "tag" 56 | - else 57 | ' CreateEvent 58 | = data.dig("ref_type") 59 | - when "DeploymentEvent" 60 | | DeploymentEvent 61 | - when "DeploymentStatusEvent" 62 | | DeploymentStatusEvent 63 | - when "DownloadEvent" 64 | | DownloadEvent 65 | - when "FollowEvent" 66 | | FollowEvent 67 | - when "ForkEvent" 68 | - repo_name = data.dig("repo", "name") 69 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 70 | - forkee_name = data.dig('forkee', 'full_name') 71 | - forkee_url = data.dig('forkee', 'html_url') 72 | ' リポジトリ 73 | => link_to repo_name, repo_url 74 | ' を 75 | => link_to forkee_name, forkee_url 76 | ' にフォークした 77 | - when "ForkApplyEvent" 78 | | ForkApplyEvent 79 | - when "GistEvent" 80 | | GistEvent 81 | - when "GollumEvent" 82 | - repo_name = data.dig("repo", "name") 83 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 84 | ' リポジトリ 85 | => link_to repo_name, repo_url 86 | ' の Wiki を更新した 87 | ul 88 | - data["pages"].each do |page| 89 | li 90 | => { created: '作成', edited: '編集' }[page.dig('action').to_sym] 91 | => link_to page.dig('title'), page.dig('html_url') 92 | => page.dig('summary') 93 | - when "InstallationEvent" 94 | | InstallationEvent 95 | - when "InstallationRepositoriesEvent" 96 | | InstallationRepositoriesEvent 97 | - when "IssueCommentEvent" 98 | - issue_title = data.dig("issue", "title") 99 | - comment_url = data.dig("comment", "html_url") 100 | => data.dig('issue', 'pull_request') ? 'Pull Request' : 'Issue' 101 | => link_to issue_title, comment_url 102 | | にコメントした 103 | - when "IssuesEvent" 104 | - issue_title = data.dig("issue", "title") 105 | - issue_url = data.dig("issue", "html_url") 106 | - action = data.dig("action") 107 | ' Issue 108 | => link_to issue_title, issue_url 109 | - case action 110 | - when "opened" 111 | | を立てた 112 | - when "closed" 113 | | を閉じた 114 | - else 115 | = action 116 | - when "LabelEvent" 117 | | LabelEvent 118 | - when "MarketplacePurchaseEvent" 119 | | MarketplacePurchaseEvent 120 | - when "MemberEvent" 121 | - repo_name = data.dig("repo", "name") 122 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 123 | - member_name = data.dig('member', 'login') 124 | - member_url = data.dig('member', 'html_url') 125 | - action = data.dig("action") 126 | ' リポジトリ 127 | => link_to repo_name, repo_url 128 | - case action 129 | - when "added" 130 | ' にメンバー 131 | => link_to "@#{member_name}", member_url 132 | ' を追加した 133 | - else 134 | => action 135 | => link_to "@#{member_name}", member_url 136 | - when "MembershipEvent" 137 | | MembershipEvent 138 | - when "MilestoneEvent" 139 | | MilestoneEvent 140 | - when "OrganizationEvent" 141 | | OrganizationEvent 142 | - when "OrgBlockEvent" 143 | | OrgBlockEvent 144 | - when "PageBuildEvent" 145 | | PageBuildEvent 146 | - when "ProjectCardEvent" 147 | | ProjectCardEvent 148 | - when "ProjectColumnEvent" 149 | | ProjectColumnEvent 150 | - when "ProjectEvent" 151 | | ProjectEvent 152 | - when "PublicEvent" 153 | | PublicEvent 154 | - when "PullRequestEvent" 155 | - pull_request_title = data.dig("pull_request", "title") 156 | - pull_request_url = data.dig("pull_request", "html_url") 157 | ' Pull Request 158 | => link_to pull_request_title, pull_request_url 159 | | を立てた 160 | - when "PullRequestReviewEvent" 161 | | PullRequestReviewEvent 162 | - when "PullRequestReviewCommentEvent" 163 | - pull_request_title = data.dig("pull_request", "title") 164 | - comment_url = data.dig("comment", "html_url") 165 | ' Pull Request 166 | => link_to pull_request_title, comment_url 167 | | にレビューコメントを書いた 168 | - when "PushEvent" 169 | - repo_name = data.dig("repo", "name") 170 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 171 | ' リポジトリ 172 | => link_to repo_name, repo_url 173 | ' にプッシュした 174 | ul 175 | - data["commits"].each do |commit| 176 | li 177 | - sha = commit["sha"] 178 | => link_to sha[0..6], "#{repo_url}/commit/#{sha}" 179 | => "%s by %s" % [commit["message"], commit.dig("author", "name")] 180 | - when "ReleaseEvent" 181 | - repo_name = data.dig("repo", "name") 182 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 183 | - release_name = data.dig("release", "name") 184 | - release_url = data.dig("release", "html_url") 185 | ' リポジトリ 186 | => link_to repo_name, repo_url 187 | ' にリリース 188 | => link_to release_name, release_url 189 | ' を作成した 190 | - when "RepositoryEvent" 191 | | RepositoryEvent 192 | - when "RepositoryVulnerabilityAlertEvent" 193 | | RepositoryVulnerabilityAlertEvent 194 | - when "StatusEvent" 195 | | StatusEvent 196 | - when "TeamEvent" 197 | | TeamEvent 198 | - when "TeamAddEvent" 199 | | TeamAddEvent 200 | - when "WatchEvent" 201 | - repo_name = data.dig("repo", "name") 202 | - repo_url = %[https://#{ENV["GHE_HOST"]}/#{repo_name}] 203 | ' リポジトリ 204 | => link_to repo_name, repo_url 205 | | をウォッチした 206 | - else 207 | = "#{activity.activity_type} : #{activity.activity_id}" 208 | -------------------------------------------------------------------------------- /app/views/activities/on_term/index.html.slim: -------------------------------------------------------------------------------- 1 | h2 2 | = "#{@owner_name} の #{@term_string} のアクティビティ" 3 | p 4 | = "全 #{@activities.count} 件" 5 | canvas#js-activities-chart 6 | input#js-activities type="hidden" value="#{@activities.group(:activity_type).count.to_json}" 7 | h3 8 | | Issue と Pull Request を Markdown で 9 | h4 10 | | 全列挙 11 | p 12 | - text1 = markdown_list(@activities.issue_and_pr.order(:acted_at)) 13 | = text_area_tag(:markdown, text1, rows: 20, style: 'width: 100%;') 14 | h4 15 | | リポジトリごと 16 | p 17 | - text2 = markdown_list_group_by_repository(@activities.issue_and_pr.order(:acted_at)) 18 | = text_area_tag(:markdown, text2, rows: 20, style: 'width: 100%;') 19 | h3 20 | | タイムライン 21 | h4 22 | | 全列挙 23 | ul 24 | = render partial: "activities/github/event", collection: @activities.order(:acted_at), as: :activity 25 | h4 26 | | リポジトリごと 27 | - @activities.reject { |a| a.repo_name.nil? }.group_by { |a| a.repo_name }.each do |repo_name, repo_activities| 28 | h5 29 | = repo_name 30 | ul 31 | = render partial: "activities/github/event", collection: repo_activities.sort_by(&:acted_at), as: :activity 32 | 33 | script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js" 34 | javascript: 35 | window.addEventListener('load', () => { 36 | const colorPalettes = [ 37 | '#1abc9c', 38 | '#2ecc71', 39 | '#3498db', 40 | '#9b59b6', 41 | '#34495e', 42 | '#f1c40f', 43 | '#e67e22', 44 | '#e74c3c', 45 | '#ecf0f1', 46 | '#95a5a6' 47 | ] 48 | 49 | const activities = JSON.parse(document.getElementById('js-activities').value) 50 | let datasets = [] 51 | let i = 0 52 | for (let key in activities) { 53 | datasets.push({ 54 | label: key.replace('Event', ''), 55 | data: [activities[key]], 56 | backgroundColor: colorPalettes[i % colorPalettes.length] 57 | }) 58 | i++ 59 | } 60 | 61 | const ctx = document.getElementById('js-activities-chart').getContext('2d') 62 | new Chart(ctx, { 63 | type: 'bar', 64 | 65 | data: { 66 | labels: ['#{@term_string}のアクティビティ'], 67 | datasets: datasets 68 | }, 69 | 70 | options: { 71 | title: { 72 | display: true, 73 | text: '種類ごと', 74 | fontSize: 20 75 | }, 76 | scales: { 77 | xAxes: [{ 78 | stacked: true, 79 | categoryPercentage: 0.4 80 | }], 81 | yAxes: [{ 82 | stacked: true, 83 | ticks: { 84 | beginAtZero:true 85 | } 86 | }] 87 | } 88 | } 89 | }) 90 | }, false) 91 | -------------------------------------------------------------------------------- /app/views/layouts/_navigator.html.slim: -------------------------------------------------------------------------------- 1 | nav 2 | - if signed_in? 3 | = render "welcome/for_user", user: current_user, activities: @activities -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title Sokuseki 5 | = csrf_meta_tags 6 | = csp_meta_tag 7 | = stylesheet_link_tag 'application', media: 'all' 8 | = stylesheet_link_tag 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css', media: 'all' 9 | = javascript_include_tag 'application' 10 | body 11 | header.header 12 | h1 13 | = link_to root_path, class:'header-logo' 14 | = image_tag "logo-horizontal.svg", alt:'Sokuseki' 15 | = render "layouts/navigator" 16 | #content.content 17 | - if signed_in? 18 | = yield 19 | - else 20 | .before-signed-in 21 | .before-signed-in__content 22 | h1.before-signed-in__title 23 | = image_tag "logo-vertical.svg", alt: "Sokuseki", class: "before-signed-in__logo" 24 | p.before-signed-in__text 25 | = "つかまえよう あなたの足跡" 26 | .before-signed-in__button-area 27 | = form_tag("/auth/github", method: "post") do 28 | button.button.is-link.is-medium 29 | | GitHub Enterprise Server でサインイン 30 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 | <style> 6 | /* Email styles need to be inline */ 7 | </style> 8 | </head> 9 | 10 | <body> 11 | <%= yield %> 12 | </body> 13 | </html> 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/welcome/_annual_report.slim: -------------------------------------------------------------------------------- 1 | canvas#js-annual-activities-chart 2 | - @annual_activities.each do |type, value| 3 | input.js-annual-activities type="hidden" activityType=type value="#{value.to_json}" 4 | 5 | input#js-target-months type="hidden" value="#{@target_duration}" 6 | 7 | script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js" 8 | javascript: 9 | window.addEventListener('load', () => { 10 | const colorPalettes = [ 11 | '#1abc9c', 12 | '#2ecc71', 13 | '#3498db', 14 | '#9b59b6', 15 | '#34495e', 16 | '#f1c40f', 17 | '#e67e22', 18 | '#e74c3c', 19 | '#ecf0f1', 20 | '#95a5a6' 21 | ] 22 | 23 | const activities = document.getElementsByClassName('js-annual-activities') 24 | const monthLabels = JSON.parse(document.getElementById("js-target-months").value) 25 | let datasets = [] 26 | 27 | let i = 0 28 | for (let activity of activities) { 29 | const activityType = activity.getAttribute("activityType").replace('Event', '') 30 | let data = [] 31 | const parsedValue = JSON.parse(activity.value) 32 | for(let month of monthLabels) { 33 | if (parsedValue[month] === undefined) { 34 | data.push(0) 35 | } else { 36 | data.push(parsedValue[month]) 37 | } 38 | } 39 | datasets.push({ 40 | label: activityType, 41 | data, 42 | backgroundColor: colorPalettes[i % colorPalettes.length] 43 | }) 44 | i++ 45 | } 46 | 47 | const ctx = document.getElementById('js-annual-activities-chart').getContext('2d') 48 | new Chart(ctx, { 49 | type: 'bar', 50 | 51 | data: { 52 | labels: monthLabels, 53 | datasets 54 | }, 55 | 56 | options: { 57 | title: { 58 | display: true, 59 | text: '1年間のアクティビティ推移', 60 | fontSize: 20 61 | }, 62 | scales: { 63 | xAxes: [{ 64 | stacked: true, 65 | categoryPercentage: 0.4 66 | }], 67 | yAxes: [{ 68 | stacked: true, 69 | ticks: { 70 | beginAtZero:true 71 | } 72 | }] 73 | } 74 | } 75 | }) 76 | }, false) 77 | -------------------------------------------------------------------------------- /app/views/welcome/_for_user.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | ' こんにちは 3 | => user.name 4 | ' さん 5 | = link_to "サインアウト", sessions_path, method: :delete, class:'button is-black is-small' -------------------------------------------------------------------------------- /app/views/welcome/index.html.slim: -------------------------------------------------------------------------------- 1 | - if signed_in? 2 | h2 3 | | 全期間のアクティビティ 4 | p 5 | = "全 #{@activities.count} 件" 6 | h3 7 | | 種類ごと 8 | ul 9 | - @activities.group(:activity_type).count.each do |activity_type, count| 10 | li 11 | = "#{activity_type} : #{count}" 12 | h2 13 | | 期ごとに見る 14 | ul 15 | - @terms.each do |term| 16 | li 17 | = link_to term, activities_on_term_path(term) 18 | - if current_user.has_available_link_on?(term) 19 | = link_to " 共有リンク(#{@shared_links[term][:expired_at]} に失効)", shared_link_path(token: @shared_links[term][:token]) 20 | = link_to ' 失効させる', shared_link_path(token: @shared_links[term][:token]), method: :delete 21 | - else 22 | = link_to ' 共有する', shared_links_path(on: term), method: :post 23 | h2 24 | | 月ごとに見る 25 | ul 26 | - @months.each do |month| 27 | li 28 | = link_to month, activities_on_term_path(month) 29 | - if current_user.has_available_link_on?(month) 30 | = link_to " 共有リンク(#{@shared_links[month][:expired_at]} に失効)", shared_link_path(token: @shared_links[month][:token]) 31 | = link_to ' 失効させる', shared_link_path(token: @shared_links[month][:token]), method: :delete 32 | - else 33 | = link_to ' 共有する', shared_links_path(on: month), method: :post 34 | = render 'annual_report' 35 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 3 | load Gem.bin_path("bundler", "bundle") 4 | -------------------------------------------------------------------------------- /bin/generate_stub_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose run --no-deps --rm web bundle exec yaml_ref_resolver -i api_schema/openapi.yml -o bundled_openapi.yml 4 | 5 | docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli:v4.2.0 generate \ 6 | -i /local/bundled_openapi.yml \ 7 | -g spring \ 8 | -o /local/stub_server 9 | 10 | docker run --rm -v ${PWD}/stub_server:/usr/src/mymaven \ 11 | -w /usr/src/mymaven maven mvn package 12 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("spring", __dir__) 4 | rescue LoadError => e 5 | raise unless e.message.include?("spring") 6 | end 7 | APP_PATH = File.expand_path("../config/application", __dir__) 8 | require_relative "../config/boot" 9 | require "rails/commands" 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("spring", __dir__) 4 | rescue LoadError => e 5 | raise unless e.message.include?("spring") 6 | end 7 | require_relative "../config/boot" 8 | require "rake" 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/run_stub_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run --rm -p 3000:3000 \ 4 | -v ${PWD}/stub_server:/usr/src/myapp -w /usr/src/myapp \ 5 | java java -jar target/openapi-spring-0.0.1.jar 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path("..", __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a starting point to setup your application. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! "bin/rails db:setup" 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! "bin/rails log:clear tmp:clear" 33 | 34 | puts "\n== Restarting application server ==" 35 | system! "bin/rails restart" 36 | end 37 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem "spring", spring.version 15 | require "spring/binstub" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path("..", __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! "bin/rails db:migrate" 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! "bin/rails log:clear tmp:clear" 28 | 29 | puts "\n== Restarting application server ==" 30 | system! "bin/rails restart" 31 | end 32 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | require "action_controller/railtie" 10 | require "action_mailer/railtie" 11 | require "action_view/railtie" 12 | require "action_cable/engine" 13 | require "sprockets/railtie" 14 | # require "rails/test_unit/railtie" 15 | 16 | # Require the gems listed in Gemfile, including any gems 17 | # you've limited to :test, :development, or :production. 18 | Bundler.require(*Rails.groups) 19 | 20 | if Rails.env.development? || Rails.env.test? 21 | Dotenv::Railtie.load 22 | end 23 | 24 | module Sokuseki 25 | class Application < Rails::Application 26 | # Initialize configuration defaults for originally generated Rails version. 27 | config.load_defaults 7.0 28 | # Settings in config/environments/* take precedence over those specified here. 29 | # Application configuration can go into files in config/initializers 30 | # -- all .rb files in that directory are automatically loaded after loading 31 | # the framework and any gems in your application. 32 | 33 | # Don't generate system test files. 34 | config.generators.system_tests = nil 35 | 36 | config.time_zone = "Asia/Tokyo" 37 | end 38 | end 39 | 40 | Raven.configure do |config| 41 | config.dsn = ENV.fetch("SENTRY_DSN", nil) 42 | config.environments = %w[production] 43 | end 44 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: sokuseki_production 11 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | fdGlJL1XrzDBcAjP0lJw3cbAu/xWGUSwI7JevRQV70d4taev6G+lQNQZaM/5PmGJsU8fB0szmJ5iL8otIbkAZav5O13r+3f3HZ8hJnSK4SuBIFYijRLRq+HEUDKEW+KYwXAmsy/XAkn9cyYr41IOlpl+/LNaMvBLr2ZVQEnnh8p4HhhxXhgppBxHjVyyhUw/rW8HL8KuZFioNvePV7osotchD6u5ScrLqLyuHb/SRRcorWL9Ve8JOZlrBQvJAT5vQIGIrqs8CQXEl0+NN4adxNpWoigKO1RrBYXj/A0Li+rjzu5p/GBwupeo4AvDym41J/GCd0ewNYrYo/JSr3aUpPZW+ynrDDQmyYNt220/z4nJk1KVApwPD7uVzGH8I9Vd3xX1D+B4eT/jgVLjy7+4LT81fyVw1lDN8+A4--igkbetfSgT82HAdd--mn4KpMcJeURovfvFtfIJ3w== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | username: postgres 5 | password: password 6 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 7 | 8 | development: 9 | <<: *default 10 | host: database 11 | database: sokuseki_development 12 | 13 | test: 14 | <<: *default 15 | host: <%= ENV["CI"] ? "127.0.0.1" : "database" %> 16 | database: sokuseki_test 17 | 18 | production: 19 | <<: *default 20 | host: <%= ENV["DB_HOST"] || "127.0.0.1" %> 21 | database: <%= ENV["DB_NAME"] || "sokuseki_production" %> 22 | username: <%= ENV["DB_USER"] || "sokuseki" %> 23 | password: <%= ENV['SOKUSEKI_DATABASE_PASSWORD'] %> 24 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join("tmp", "caching-dev.txt").exist? 18 | config.action_controller.perform_caching = true 19 | 20 | config.cache_store = :memory_store 21 | config.public_file_server.headers = { 22 | "Cache-Control" => "public, max-age=#{2.days.to_i}", 23 | } 24 | else 25 | config.action_controller.perform_caching = false 26 | 27 | config.cache_store = :null_store 28 | end 29 | 30 | # Store uploaded files on the local file system (see config/storage.yml for options) 31 | config.active_storage.service = :local 32 | 33 | # Don't care if the mailer can't send. 34 | config.action_mailer.raise_delivery_errors = false 35 | 36 | config.action_mailer.perform_caching = false 37 | 38 | # Print deprecation notices to the Rails logger. 39 | config.active_support.deprecation = :log 40 | 41 | # Raise an error on page load if there are pending migrations. 42 | config.active_record.migration_error = :page_load 43 | 44 | # Highlight code that triggered database queries in logs. 45 | config.active_record.verbose_query_logs = true 46 | 47 | # Debug mode disables concatenation and preprocessing of assets. 48 | # This option may cause significant delays in view rendering with a large 49 | # number of complex assets. 50 | config.assets.debug = true 51 | 52 | # Suppress logger output for asset requests. 53 | config.assets.quiet = true 54 | 55 | # Raises error for missing translations 56 | # config.action_view.raise_on_missing_translations = true 57 | 58 | # Use an evented file watcher to asynchronously detect changes in source code, 59 | # routes, locales, etc. This feature depends on the listen gem. 60 | config.file_watcher = ActiveSupport::FileUpdateChecker 61 | end 62 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 33 | 34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 35 | # config.action_controller.asset_host = 'http://assets.example.com' 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 40 | 41 | # Store uploaded files on the local file system (see config/storage.yml for options) 42 | config.active_storage.service = :local 43 | 44 | # Mount Action Cable outside main process or domain 45 | # config.action_cable.mount_path = nil 46 | # config.action_cable.url = 'wss://example.com/cable' 47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 48 | 49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 50 | config.force_ssl = true 51 | 52 | # Use the lowest log level to ensure availability of diagnostic information 53 | # when problems arise. 54 | config.log_level = :info 55 | 56 | # Prepend all log lines with the following tags. 57 | config.log_tags = [:request_id] 58 | 59 | # Use a different cache store in production. 60 | # config.cache_store = :mem_cache_store 61 | 62 | # Use a real queuing backend for Active Job (and separate queues per environment) 63 | # config.active_job.queue_adapter = :resque 64 | # config.active_job.queue_name_prefix = "sokuseki_#{Rails.env}" 65 | 66 | config.action_mailer.perform_caching = false 67 | 68 | # Ignore bad email addresses and do not raise email delivery errors. 69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 70 | # config.action_mailer.raise_delivery_errors = false 71 | 72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 73 | # the I18n.default_locale when a translation cannot be found). 74 | config.i18n.fallbacks = true 75 | 76 | # Send deprecation notices to registered listeners. 77 | config.active_support.deprecation = :notify 78 | 79 | # Use default logging formatter so that PID and timestamp are not suppressed. 80 | config.log_formatter = Logger::Formatter.new 81 | 82 | # Use a different logger for distributed setups. 83 | # require 'syslog/logger' 84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 85 | 86 | if ENV["RAILS_LOG_TO_STDOUT"].present? 87 | logger = ActiveSupport::Logger.new($stdout) 88 | logger.formatter = config.log_formatter 89 | config.logger = ActiveSupport::TaggedLogging.new(logger) 90 | end 91 | 92 | # Do not dump schema after migrations. 93 | config.active_record.dump_schema_after_migration = false 94 | end 95 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = false 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | "Cache-Control" => "public, max-age=#{1.hour.to_i}", 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Store uploaded files on the local file system in a temporary directory 32 | config.active_storage.service = :test 33 | 34 | config.action_mailer.perform_caching = false 35 | 36 | # Tell Action Mailer not to deliver emails to the real world. 37 | # The :test delivery method accumulates sent emails in the 38 | # ActionMailer::Base.deliveries array. 39 | config.action_mailer.delivery_method = :test 40 | 41 | # Print deprecation notices to the stderr. 42 | config.active_support.deprecation = :stderr 43 | 44 | # Raises error for missing translations 45 | # config.action_view.raise_on_missing_translations = true 46 | end 47 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join("node_modules") 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Report CSP violations to a specified URI 23 | # For further information see the following documentation: 24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # Rails.application.config.content_security_policy_report_only = true 26 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/octokit.rb: -------------------------------------------------------------------------------- 1 | Octokit.configure do |c| 2 | ghe_host = ENV.fetch("GHE_HOST", nil) 3 | c.api_endpoint = "https://#{ghe_host}/api/v3/" 4 | end 5 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.use OmniAuth::Builder do 2 | ghe_host = ENV.fetch("GHE_HOST", nil) 3 | 4 | provider :github, ENV.fetch("GHE_APP_KEY", nil), ENV.fetch("GHE_APP_SECRET", nil), { 5 | client_options: { 6 | site: "https://#{ghe_host}/api/v3", 7 | authorize_url: "https://#{ghe_host}/login/oauth/authorize", 8 | token_url: "https://#{ghe_host}/login/oauth/access_token", 9 | }, 10 | scope: "user,repo", 11 | } 12 | end 13 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT", 3000) 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. 30 | # 31 | # preload_app! 32 | 33 | # Allow puma to be restarted by `rails restart` command. 34 | plugin :tmp_restart 35 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get "/auth/:provider/callback", to: "sessions#create" 3 | delete "/sessions", to: "sessions#destroy" 4 | resources :shared_links, only: [:show, :create, :destroy], param: :token 5 | get "shared_links/:token", to: "shared_links#show" 6 | post "shared_links", to: "shared_links#create" 7 | delete "shared_links/:token", to: "shared_links#destroy" 8 | 9 | get "/activities/on/:term_string", to: "activities/on_term#index", as: "activities_on_term" 10 | 11 | root to: "welcome#index" 12 | end 13 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /db/migrate/20180709154513_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :users do |t| 4 | t.string :name, null: false 5 | t.string :avatar_url, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :users, :name, unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20180709223556_create_authentications.rb: -------------------------------------------------------------------------------- 1 | class CreateAuthentications < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :authentications do |t| 4 | t.references :user, foreign_key: true 5 | t.string :provider, null: false 6 | t.string :uid, null: false 7 | t.string :name, null: false 8 | t.string :access_token, null: false 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20180714072854_create_activities.rb: -------------------------------------------------------------------------------- 1 | class CreateActivities < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :activities do |t| 4 | t.references :user, foreign_key: true 5 | t.references :authentication, foreign_key: true 6 | t.string :activity_id, null: false 7 | t.string :activity_type, null: false 8 | t.datetime :acted_at, null: false 9 | t.jsonb :original_data 10 | 11 | t.timestamps 12 | end 13 | 14 | add_index :activities, [:activity_id, :activity_type], unique: true 15 | add_index :activities, :acted_at 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20191026092654_create_shared_links.rb: -------------------------------------------------------------------------------- 1 | class CreateSharedLinks < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :shared_links do |t| 4 | t.references :user, null: false, foreign_key: true 5 | t.datetime :expired_at, null: false 6 | t.string :on, null: false 7 | t.string :token, null: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200107134431_add_index_to_shared_links_token.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToSharedLinksToken < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :shared_links, :token, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema[7.1].define(version: 2020_01_07_134431) do 14 | # These are extensions that must be enabled in order to support this database 15 | enable_extension "plpgsql" 16 | 17 | create_table "activities", force: :cascade do |t| 18 | t.bigint "user_id" 19 | t.bigint "authentication_id" 20 | t.string "activity_id", null: false 21 | t.string "activity_type", null: false 22 | t.datetime "acted_at", precision: nil, null: false 23 | t.jsonb "original_data" 24 | t.datetime "created_at", precision: nil, null: false 25 | t.datetime "updated_at", precision: nil, null: false 26 | t.index ["acted_at"], name: "index_activities_on_acted_at" 27 | t.index ["activity_id", "activity_type"], name: "index_activities_on_activity_id_and_activity_type", unique: true 28 | t.index ["authentication_id"], name: "index_activities_on_authentication_id" 29 | t.index ["user_id"], name: "index_activities_on_user_id" 30 | end 31 | 32 | create_table "authentications", force: :cascade do |t| 33 | t.bigint "user_id" 34 | t.string "provider", null: false 35 | t.string "uid", null: false 36 | t.string "name", null: false 37 | t.string "access_token", null: false 38 | t.datetime "created_at", precision: nil, null: false 39 | t.datetime "updated_at", precision: nil, null: false 40 | t.index ["user_id"], name: "index_authentications_on_user_id" 41 | end 42 | 43 | create_table "shared_links", force: :cascade do |t| 44 | t.bigint "user_id", null: false 45 | t.datetime "expired_at", precision: nil, null: false 46 | t.string "on", null: false 47 | t.string "token", null: false 48 | t.datetime "created_at", null: false 49 | t.datetime "updated_at", null: false 50 | t.index ["token"], name: "index_shared_links_on_token", unique: true 51 | t.index ["user_id"], name: "index_shared_links_on_user_id" 52 | end 53 | 54 | create_table "users", force: :cascade do |t| 55 | t.string "name", null: false 56 | t.string "avatar_url", null: false 57 | t.datetime "created_at", precision: nil, null: false 58 | t.datetime "updated_at", precision: nil, null: false 59 | t.index ["name"], name: "index_users_on_name", unique: true 60 | end 61 | 62 | add_foreign_key "activities", "authentications" 63 | add_foreign_key "activities", "users" 64 | add_foreign_key "authentications", "users" 65 | add_foreign_key "shared_links", "users" 66 | end 67 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | database: 4 | image: postgres:10.4 5 | volumes: 6 | - database-volume:/var/lib/postgresql/data 7 | web: 8 | build: . 9 | command: bash -c 'rm tmp/pids/server.pid 2>/dev/null; bundle exec rails server -b "0.0.0.0"' 10 | volumes: 11 | - bundle-volume:/usr/local/bundle 12 | - .:/myapp 13 | ports: 14 | - "3000:3000" 15 | environment: 16 | TZ: "Asia/Tokyo" 17 | ELASTIC_APM_ENABLED: "false" 18 | depends_on: 19 | - database 20 | tty: true 21 | stdin_open: true 22 | volumes: 23 | bundle-volume: 24 | driver: local 25 | database-volume: 26 | driver: local 27 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sokuseki", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title>The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /spec/factories/activities.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :activity do 3 | sequence(:activity_id, &:to_s) 4 | activity_type { "IssuesEvent" } 5 | acted_at { Time.current } 6 | 7 | user { nil } 8 | authentication { nil } 9 | 10 | trait :commit_comment_event do 11 | activity_type { "CommitCommentEvent" } 12 | end 13 | 14 | trait :issue_event do 15 | activity_type { "IssuesEvent" } 16 | end 17 | 18 | trait :pull_request_event do 19 | activity_type { "PullRequestEvent" } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/factories/authentications.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :authentication do 3 | provider { "github" } 4 | sequence(:uid, &:to_s) 5 | name { "yinm" } 6 | access_token { "abcde12345" } 7 | 8 | user { nil } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/shared_links.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :shared_link do 3 | user { nil } 4 | expired_at { "2019-10-26 18:26:58" } 5 | on { "MyString" } 6 | token { "MyString" } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | name { "yinm" } 4 | avatar_url { "https://example.com/avatars" } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/activity_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe Activity, type: :model do 4 | describe ".issue_and_pr" do 5 | let(:user) { create(:user) } 6 | let(:authentication) { create(:authentication, user:) } 7 | let!(:issue_event) { create(:activity, :issue_event, user:, authentication:) } 8 | let!(:pull_request_event) { create(:activity, :pull_request_event, user:, authentication:) } 9 | let!(:commit_comment_event) { create(:activity, :commit_comment_event, user:, authentication:) } 10 | 11 | it "IssuesEvent, PullRequestEventだけを取得する" do 12 | expect(Activity.issue_and_pr).to contain_exactly(issue_event, pull_request_event) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/models/shared_link_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe SharedLink, type: :model do 4 | let(:user) { create(:user) } 5 | let(:token) { SecureRandom.hex(32) } 6 | let(:expired_at) { 30.minutes.after } 7 | let(:on) { "2019-12" } 8 | 9 | subject do 10 | SharedLink.new(token:, expired_at:, on:, user:) 11 | end 12 | 13 | describe "validation" do 14 | it { is_expected.to be_valid } 15 | 16 | context "on is nil" do 17 | let(:on) { 18 | nil 19 | } 20 | it { is_expected.to be_invalid } 21 | end 22 | 23 | context "on is too long" do 24 | let(:on) { 25 | SecureRandom.hex(512) 26 | } 27 | it { is_expected.to be_invalid } 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | require "spec_helper" 3 | ENV["RAILS_ENV"] ||= "test" 4 | require File.expand_path("../config/environment", __dir__) 5 | # Prevent database truncation if the environment is production 6 | abort("The Rails environment is running in production mode!") if Rails.env.production? 7 | require "rspec/rails" 8 | # Add additional requires below this line. Rails is not loaded until this point! 9 | 10 | # Requires supporting ruby files with custom matchers and macros, etc, in 11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 12 | # run as spec files by default. This means that files in spec/support that end 13 | # in _spec.rb will both be required and run as specs, causing the specs to be 14 | # run twice. It is recommended that you do not name files matching this glob to 15 | # end with _spec.rb. You can configure this pattern with the --pattern 16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 17 | # 18 | # The following line is provided for convenience purposes. It has the downside 19 | # of increasing the boot-up time by auto-requiring all files in the support 20 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 21 | # require only the support files necessary. 22 | # 23 | Dir[Rails.root.join("spec", "support", "**", "*.rb")].each { |f| require f } 24 | 25 | require "support/factory_bot" 26 | 27 | # Checks for pending migrations and applies them before tests are run. 28 | # If you are not using ActiveRecord, you can remove these lines. 29 | begin 30 | ActiveRecord::Migration.maintain_test_schema! 31 | rescue ActiveRecord::PendingMigrationError => e 32 | puts e.to_s.strip 33 | exit 1 34 | end 35 | RSpec.configure do |config| 36 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 37 | config.fixture_path = "#{Rails.root}/spec/fixtures" 38 | 39 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 40 | # examples within a transaction, remove the following line or assign false 41 | # instead of true. 42 | config.use_transactional_fixtures = true 43 | 44 | # RSpec Rails can automatically mix in different behaviours to your tests 45 | # based on their file location, for example enabling you to call `get` and 46 | # `post` in specs under `spec/controllers`. 47 | # 48 | # You can disable this behaviour by removing the line below, and instead 49 | # explicitly tag your specs with their type, e.g.: 50 | # 51 | # RSpec.describe UsersController, :type => :controller do 52 | # # ... 53 | # end 54 | # 55 | # The different available types are documented in the features, such as in 56 | # https://relishapp.com/rspec/rspec-rails/docs 57 | config.infer_spec_type_from_file_location! 58 | 59 | # Filter lines from Rails gems in backtraces. 60 | config.filter_rails_from_backtrace! 61 | # arbitrary gems may also be filtered via: 62 | # config.filter_gems_from_backtrace("gem name") 63 | end 64 | -------------------------------------------------------------------------------- /spec/requests/sessions_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe "Sessions", type: :request do 4 | describe "GET /auth/github/callback" do 5 | context "登録済みのユーザーのとき" do 6 | let(:user) { create(:user) } 7 | let!(:authentication) { create(:authentication, user:, uid: "12345") } 8 | 9 | before do 10 | OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ 11 | provider: "github", 12 | uid: "12345", 13 | info: { 14 | nickname: "yinm", 15 | }, 16 | credentials: { 17 | token: "abcde12345", 18 | }, 19 | }) 20 | end 21 | 22 | it "ユーザーを作らない" do 23 | expect { get "/auth/github/callback" }.not_to change(User, :count) 24 | end 25 | 26 | it "GitHubのusernameに変更がない場合は、更新処理を行わない" do 27 | expect { get "/auth/github/callback" }.not_to change { authentication.reload.updated_at } 28 | end 29 | 30 | it "GitHubのusernameに変更がある場合は、変更に追従する" do 31 | OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ 32 | provider: "github", 33 | uid: "12345", 34 | info: { 35 | nickname: "yinm_updated", 36 | }, 37 | credentials: { 38 | token: "abcde12345", 39 | }, 40 | }) 41 | 42 | expect { get "/auth/github/callback" }.to change { authentication.reload.name }.from("yinm").to("yinm_updated") 43 | end 44 | 45 | it "GitHubのaccess tokenに変更がある場合は、変更に追従する" do 46 | OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ 47 | provider: "github", 48 | uid: "12345", 49 | info: { 50 | nickname: "yinm_updated", 51 | }, 52 | credentials: { 53 | token: "update_token", 54 | }, 55 | }) 56 | 57 | expect { get "/auth/github/callback" }.to change { authentication.reload.name }.from("yinm").to("yinm_updated") 58 | end 59 | 60 | it "root_pathにリダイレクトする" do 61 | get "/auth/github/callback" 62 | expect(response).to redirect_to(root_path) 63 | end 64 | end 65 | 66 | context "未登録のユーザーのとき" do 67 | before do 68 | OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ 69 | provider: "github", 70 | uid: "99999", 71 | info: { 72 | nickname: "new user", 73 | image: "https://example.com/avatar.png", 74 | }, 75 | credentials: { 76 | token: "abcd1234", 77 | }, 78 | }) 79 | end 80 | 81 | it "ユーザーを作る" do 82 | expect { get "/auth/github/callback" }.to change(User, :count).by(1) 83 | end 84 | 85 | it "root_pathにリダイレクトする" do 86 | get "/auth/github/callback" 87 | expect(response).to redirect_to(root_path) 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 16 | RSpec.configure do |config| 17 | # rspec-expectations config goes here. You can use an alternate 18 | # assertion/expectation library such as wrong or the stdlib/minitest 19 | # assertions if you prefer. 20 | config.expect_with :rspec do |expectations| 21 | # This option will default to `true` in RSpec 4. It makes the `description` 22 | # and `failure_message` of custom matchers include text for helper methods 23 | # defined using `chain`, e.g.: 24 | # be_bigger_than(2).and_smaller_than(4).description 25 | # # => "be bigger than 2 and smaller than 4" 26 | # ...rather than: 27 | # # => "be bigger than 2" 28 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 29 | end 30 | 31 | # rspec-mocks config goes here. You can use an alternate test double 32 | # library (such as bogus or mocha) by changing the `mock_with` option here. 33 | config.mock_with :rspec do |mocks| 34 | # Prevents you from mocking or stubbing a method that does not exist on 35 | # a real object. This is generally recommended, and will default to 36 | # `true` in RSpec 4. 37 | mocks.verify_partial_doubles = true 38 | end 39 | 40 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 41 | # have no way to turn it off -- the option exists only for backwards 42 | # compatibility in RSpec 3). It causes shared context metadata to be 43 | # inherited by the metadata hash of host groups and examples, rather than 44 | # triggering implicit auto-inclusion in groups with matching metadata. 45 | config.shared_context_metadata_behavior = :apply_to_host_groups 46 | 47 | # The settings below are suggested to provide a good initial experience 48 | # with RSpec, but feel free to customize to your heart's content. 49 | # # This allows you to limit a spec run to individual examples or groups 50 | # # you care about by tagging them with `:focus` metadata. When nothing 51 | # # is tagged with `:focus`, all examples get run. RSpec also provides 52 | # # aliases for `it`, `describe`, and `context` that include `:focus` 53 | # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 54 | # config.filter_run_when_matching :focus 55 | # 56 | # # Allows RSpec to persist some state between runs in order to support 57 | # # the `--only-failures` and `--next-failure` CLI options. We recommend 58 | # # you configure your source control system to ignore this file. 59 | # config.example_status_persistence_file_path = "spec/examples.txt" 60 | # 61 | # # Limits the available syntax to the non-monkey patched syntax that is 62 | # # recommended. For more details, see: 63 | # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 64 | # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 65 | # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 66 | # config.disable_monkey_patching! 67 | # 68 | # # Many RSpec users commonly either run the entire suite or an individual 69 | # # file, and it's useful to allow more verbose output when running an 70 | # # individual spec file. 71 | # if config.files_to_run.one? 72 | # # Use the documentation formatter for detailed output, 73 | # # unless a formatter has already been configured 74 | # # (e.g. via a command-line flag). 75 | # config.default_formatter = "doc" 76 | # end 77 | # 78 | # # Print the 10 slowest examples and example groups at the 79 | # # end of the spec run, to help surface which specs are running 80 | # # particularly slow. 81 | # config.profile_examples = 10 82 | # 83 | # # Run specs in random order to surface order dependencies. If you find an 84 | # # order dependency and want to debug it, you can fix the order by providing 85 | # # the seed, which is printed after each run. 86 | # # --seed 1234 87 | # config.order = :random 88 | # 89 | # # Seed global randomization in this process using the `--seed` CLI option. 90 | # # Setting this allows you to use `--seed` to deterministically reproduce 91 | # # test failures related to randomization by passing the same `--seed` value 92 | # # as the one that triggered the failure. 93 | # Kernel.srand config.seed 94 | end 95 | -------------------------------------------------------------------------------- /spec/support/factory_bot.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryBot::Syntax::Methods 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/omniauth.rb: -------------------------------------------------------------------------------- 1 | OmniAuth.config.test_mode = true 2 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/tmp/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sokusekiya/sokuseki/65c4ef950ebbdcb926355011723d2bcd0baf18ba/vendor/.keep --------------------------------------------------------------------------------