├── .browserslistrc
├── .github
├── dependabot.yml
└── workflows
│ └── rubyonrails.yml
├── .gitignore
├── .postcssrc.yml
├── .prettierrc
├── .rspec
├── .ruby-version
├── Gemfile
├── Gemfile.lock
├── Procfile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ ├── .keep
│ │ ├── gem.svg
│ │ └── orange-waves.svg
│ ├── javascripts
│ │ └── application.js
│ └── stylesheets
│ │ └── application.scss
├── controllers
│ ├── application_controller.rb
│ ├── comparison_controller.rb
│ ├── concerns
│ │ └── .keep
│ ├── home_controller.rb
│ └── versions_controller.rb
├── helpers
│ └── application_helper.rb
├── javascript
│ └── packs
│ │ └── application.js
├── jobs
│ └── application_job.rb
├── lib
│ └── date_range.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── application_record.rb
│ ├── concerns
│ │ └── .keep
│ ├── import_status.rb
│ └── stat.rb
├── views
│ ├── home
│ │ ├── _chart.js.erb
│ │ ├── _charts.html.erb
│ │ ├── _totals.js.erb
│ │ └── index.html.erb
│ ├── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
│ └── versions
│ │ └── show.js.erb
└── workers
│ ├── import_stats_day_worker.rb
│ ├── import_stats_days_worker.rb
│ ├── import_stats_file_worker.rb
│ └── import_stats_worker.rb
├── babel.config.js
├── bin
├── bundle
├── production
├── puma
├── rails
├── rake
├── setup
├── sidekiq
├── update
├── webpack
├── webpack-dev-server
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── credentials.yml.enc
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── aws_s3.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── filter_parameter_logging.rb
│ ├── generators.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── sidekiq.rb
│ └── wrap_parameters.rb
├── locales
│ └── en.yml
├── puma.rb
├── routes.rb
├── sidekiq.yml
├── webpack
│ ├── development.js
│ ├── environment.js
│ ├── production.js
│ └── test.js
└── webpacker.yml
├── db
├── migrate
│ ├── 20180513075234_enable_uuid.rb
│ ├── 20180513075308_create_stats.rb
│ ├── 20180929063650_create_import_statuses.rb
│ ├── 20190708173946_add_timestamps_to_stats.rb
│ └── 20190713153752_add_unique_index_to_stats.rb
├── schema.rb
├── seeds.rb
└── seeds
│ └── stats.csv
├── gems.locked
├── gems.rb
├── lib
├── assets
│ └── .keep
└── tasks
│ ├── .keep
│ └── temporary.rake
├── log
└── .keep
├── package.json
├── postcss.config.js
├── public
├── 404.html
├── 422.html
├── 500.html
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
├── readme_images
└── design.png
├── spec
├── models
│ ├── import_status_spec.rb
│ └── stat_spec.rb
├── rails_helper.rb
├── spec_helper.rb
├── support
│ ├── legacy_stats.json
│ └── new_stats.json
└── workers
│ ├── import_stats_day_worker_spec.rb
│ └── import_stats_worker_spec.rb
├── tmp
└── .keep
├── vendor
└── .keep
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | registries:
3 | rubygems-server-gems-contribsys-com:
4 | type: rubygems-server
5 | url: https://gems.contribsys.com
6 | username: "${{secrets.RUBYGEMS_SERVER_GEMS_CONTRIBSYS_COM_USERNAME}}"
7 | password: "${{secrets.RUBYGEMS_SERVER_GEMS_CONTRIBSYS_COM_PASSWORD}}"
8 |
9 | updates:
10 | - package-ecosystem: bundler
11 | directory: "/"
12 | schedule:
13 | interval: daily
14 | open-pull-requests-limit: 3
15 | registries:
16 | - rubygems-server-gems-contribsys-com
17 | - package-ecosystem: npm
18 | directory: "/"
19 | schedule:
20 | interval: daily
21 | open-pull-requests-limit: 3
22 |
--------------------------------------------------------------------------------
/.github/workflows/rubyonrails.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are
2 | # provided by a third-party and are governed by separate terms of service,
3 | # privacy policy, and support documentation.
4 | #
5 | # This workflow will install a prebuilt Ruby version, install dependencies, and
6 | # run tests and linters.
7 | name: "Ruby on Rails CI"
8 | on:
9 | push:
10 | branches: [ main ]
11 | pull_request:
12 | branches: [ main ]
13 | jobs:
14 | test:
15 | runs-on: ubuntu-latest
16 | services:
17 | postgres:
18 | image: postgres:11-alpine
19 | ports:
20 | - "5432:5432"
21 | env:
22 | POSTGRES_DB: rails_test
23 | POSTGRES_USER: rails
24 | POSTGRES_PASSWORD: password
25 | env:
26 | RAILS_ENV: test
27 | DATABASE_URL: "postgres://rails:password@localhost:5432/rails_test"
28 | BUNDLE_GEMS__CONTRIBSYS__COM: "${{ secrets.BUNDLE_GEMS__CONTRIBSYS__COM }}"
29 | steps:
30 | - name: Checkout code
31 | uses: actions/checkout@v2
32 | # Add or replace dependency steps here
33 | - name: Install Ruby and gems
34 | uses: ruby/setup-ruby@8f312efe1262fb463d906e9bf040319394c18d3e # v1.92
35 | with:
36 | bundler-cache: true
37 | # Add or replace database setup steps here
38 | - name: Set up database schema
39 | run: bin/rails db:schema:load
40 | # Add or replace test runners here
41 | - name: Run tests
42 | run: bin/rake
43 |
44 | # lint:
45 | # runs-on: ubuntu-latest
46 | # steps:
47 | # - name: Checkout code
48 | # uses: actions/checkout@v2
49 | # - name: Install Ruby and gems
50 | # uses: ruby/setup-ruby@8f312efe1262fb463d906e9bf040319394c18d3e # v1.92
51 | # with:
52 | # bundler-cache: true
53 | # # Add or replace any other lints here
54 | # - name: Security audit dependencies
55 | # run: bin/bundler-audit --update
56 | # - name: Security audit application code
57 | # run: bin/brakeman -q -w2
58 | # - name: Lint Ruby files
59 | # run: bin/rubocop --parallel
60 |
--------------------------------------------------------------------------------
/.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 |
10 | # Ignore all logfiles and tempfiles.
11 | /log/*
12 | /tmp/*
13 | !/log/.keep
14 | !/tmp/.keep
15 |
16 | /node_modules
17 | /yarn-error.log
18 |
19 | /public/assets
20 | .byebug_history
21 |
22 | # Ignore master key for decrypting credentials and more.
23 | /config/master.key
24 | /public/packs
25 | /public/packs-test
26 | yarn-debug.log*
27 | .yarn-integrity
28 |
--------------------------------------------------------------------------------
/.postcssrc.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | postcss-import: {}
3 | postcss-cssnext: {}
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": false,
3 | "preferSingleQuotes": false
4 | }
5 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.7.5
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | gems.rb
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | gems.locked
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bin/puma -C config/puma.rb
2 | worker: bin/sidekiq -C config/sidekiq.yml
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Ruby Ecosystem
2 |
3 | We collect metrics from Bundler and rubygems.org and graph them.
4 |
5 | ## What metrics, exactly?
6 |
7 | To start with, we will graph over time:
8 |
9 | * Request counts for Ruby and Bundler, segregated by version.
10 | * Request counts made from various platforms (eg. Linux, Windows etc)
11 | * Request counts made from various CI providers (eg. CircleCI, Jenkins etc)
12 |
13 | ## Why?
14 |
15 | Apart from being interesting to look at, this information has the potential to help:
16 |
17 | * Gem authors understand what versions of Ruby they should consider supporting
18 | * Developers make decisions on if and when they should upgrade their Ruby version
19 | * The Ruby community as a whole understand better how it is evolving and changing
20 |
21 |
22 | ## How does it work?
23 |
24 | Running `bundle install` or `gem install` makes requests to rubygems.org to download various gems. These requests also send information to rubygems.org about the current Ruby and/or Bundler version being used, the platform being used and a few other things. This infomation is logged and stored in S3. Kirby (https://github.com/rubytogether/kirby) parses this info (there's a lot of it!) and aggregates it into JSON files.
25 |
26 | The Ecosystem app retrieves this JSON information on a daily basis and graphs it.
27 |
28 |
29 | ## Where is it?
30 |
31 | Ecosystem is currently hosted on Heroku at: http://ecosystem.rubytogether.org
32 |
33 | ## WIP - Design
34 |
35 | Here's what Brendan Miller (designer at Cloud City Development) has come up with so far. We are currently working on building this page out:
36 |
37 | 
38 |
--------------------------------------------------------------------------------
/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 |
8 | namespace :stats do
9 | task :import => :environment do
10 | ImportStatsWorker.perform_async
11 | end
12 | end
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/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/images/gem.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/assets/images/orange-waves.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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_tree .
15 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap");
2 |
3 | body {
4 | font-family: "Roboto", sans-serif;
5 | background-color: #e5e5e5;
6 | color: #475972;
7 | margin: 0;
8 |
9 | a, a:visited {
10 | color: white;
11 | }
12 |
13 | header {
14 | background: url("../images/orange-waves.svg");
15 | color: white;
16 | padding-bottom: 85px;
17 | padding-top: 85px;
18 | text-align: center;
19 | h1 {
20 | font-weight: 600;
21 | font-size: 50px;
22 | line-height: 59px;
23 | }
24 |
25 | h2 {
26 | font-weight: 300;
27 | font-size: 32px;
28 | line-height: 40px;
29 | }
30 | }
31 |
32 | main {
33 | margin: 5% 10%;
34 |
35 | h3 {
36 | text-align: center;
37 | font-weight: 300;
38 | font-size: 30px;
39 | line-height: 35px;
40 | }
41 |
42 | p {
43 | max-width: 40em;
44 | margin: 0 auto 2em auto;
45 | }
46 |
47 | .chart-container {
48 | margin-bottom: 5%;
49 |
50 | .chart-heading {
51 | strong {
52 | font-size: 18px;
53 | line-height: 21px;
54 | color: black;
55 | }
56 |
57 | a {
58 | font-weight: 500;
59 | font-size: 14px;
60 | line-height: 16px;
61 | letter-spacing: 0.03em;
62 | text-decoration: none;
63 | margin-right: 30px;
64 | color: #acc0e6;
65 | &.active {
66 | color: #5f7195;
67 | }
68 | }
69 |
70 | display: flex;
71 | justify-content: space-between;
72 | width: 94%;
73 | padding: 0 2%;
74 | }
75 | .apexcharts-xaxis-label {
76 | fill: #8e9bb5;
77 | &._3months,
78 | &._1year {
79 | &:nth-child(even) {
80 | visibility: hidden;
81 | }
82 | }
83 | }
84 |
85 | .apexcharts-yaxis-label {
86 | fill: #8e9bb5;
87 | }
88 | }
89 | }
90 | }
91 |
92 | .hidden {
93 | display: none !important;
94 | }
95 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | rescue_from ActionController::InvalidAuthenticityToken, with: :invalid_token
3 |
4 | def invalid_token
5 | render file: Rails.public_path.join('500.html'),
6 | status: :unprocessable_entity, layout: false
7 | end
8 |
9 | def not_found
10 | render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/controllers/comparison_controller.rb:
--------------------------------------------------------------------------------
1 | class ComparisonController < ApplicationController
2 | def show
3 | key1 = params.fetch(:key1)
4 | key2 = params.fetch(:key2)
5 | @range = DateRange.new(params)
6 |
7 | data = Stat.send(@range.prefix + "comparison", key1, key2, @range.value)
8 |
9 | dates = data.map(&:first).sort.uniq
10 | count_map = Hash.new { |h, k| h[k] = {} }
11 | data.each { |date, key, count| count_map[date][key] = count }
12 |
13 | key1_points =
14 | dates.map do |date|
15 | first = count_map.dig(date, key1) || 0
16 | second = count_map.dig(date, key2) || 0
17 | count = first - second
18 | y = count.to_f / count_map.dig(date, key1) * 100
19 | { x: date.strftime("%m/%d"), y: y }
20 | end
21 |
22 | key1_without_unique = key1.delete_suffix("_unique")
23 | key2_without_unique = key2.delete_suffix("_unique")
24 |
25 | # TODO: Make this more general
26 | key1_name = key1_without_unique == "bundler" && key2_without_unique == "ci" ? "Non-CI" : key1_without_unique
27 | key1_series = { name: key1_name, data: key1_points }
28 |
29 | key2_points =
30 | dates.map do |date|
31 | count = count_map.dig(date, key2)
32 | y = count.to_f / count_map.dig(date, key1) * 100
33 | { x: date.strftime("%m/%d"), y: y }
34 | end
35 |
36 | key2_name = key2_without_unique == "ci" ? "CI" : key2_without_unique
37 | key2_series = { name: key2_name, data: key2_points }
38 |
39 | @series = [key1_series, key2_series]
40 |
41 | @ranges_container_selector = ".comparison-#{key1}-#{key2}-graph-ranges"
42 |
43 | respond_to do |format|
44 | format.json { render json: @series }
45 | format.js { render "versions/show" }
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def index
3 | return render plain: "No data!" if Stat.count.zero?
4 |
5 | suffix = params[:unique] ? "_unique" : ""
6 |
7 | @version_charts = {
8 | "Ruby" => "/versions/ruby#{suffix}",
9 | "Bundler" => "/versions/bundler#{suffix}",
10 | "RubyGems" => "/versions/rubygems#{suffix}",
11 | "Platform" => "/versions/platform#{suffix}",
12 | "CI Provider" => "/versions/ci#{suffix}"
13 | }
14 |
15 | @comparison_charts = {
16 | "Rubygems vs Bundler" => "/comparison/rubygems#{suffix}/bundler#{suffix}",
17 | "CI vs non-CI" => "/comparison/bundler#{suffix}/ci#{suffix}"
18 | }
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/controllers/versions_controller.rb:
--------------------------------------------------------------------------------
1 | class VersionsController < ApplicationController
2 | def show
3 | key = params.fetch(:key)
4 | limit = Integer(params.fetch(:limit) { DEFAULT_LIMITS[key] }).clamp(0, MAX_LIMIT)
5 | @range = DateRange.new(params)
6 |
7 | data = Stat.send(@range.prefix + "data", key, @range.value)
8 | date_totals = Stat.send(@range.prefix + "totals", key, @range.value)
9 |
10 | if params[:total]
11 | points =
12 | date_totals.map do |date, count|
13 | { x: date.strftime("%m/%d"), y: count }
14 | end
15 | @series = [{ name: "total", data: points }]
16 | @total = true
17 | @ranges_container_selector = ".ruby-total-ranges"
18 | else
19 | dates = data.map(&:first).sort.uniq
20 | count_map = count(data)
21 |
22 | # Build one series for each version, with an entry for every date
23 | #
24 | # [ {name: "2.2.2", data: [{x: "2018-07-13", y: 3932}, ...] }, ... ]
25 | versions = count_map.values.flat_map(&:keys).compact.sort.uniq
26 | top =
27 | versions.max_by(limit) do |v|
28 | count_map[count_map.keys.last][v] || 0
29 | end
30 |
31 | top_series =
32 | top.map do |version|
33 | points =
34 | dates.map do |date|
35 | count = count_map.dig(date, version) || 0
36 | y = (count.to_f / date_totals[date]) * 100
37 | { x: date.strftime("%m/%d"), y: y }
38 | end
39 |
40 | { name: "#{version}", data: points }
41 | end
42 |
43 | if top.length < versions.length
44 | the_rest = {
45 | name: "Others",
46 | data:
47 | dates.map do |date|
48 | count = count_map[date].reject { |v| v.in?(top) }.sum { |v, c| c }
49 | y = (count.to_f / date_totals[date]) * 100
50 | # TODO: Move the formatting to JS
51 | { x: date.strftime("%m/%d"), y: y }
52 | end
53 | }
54 | end
55 |
56 | @series = the_rest ? top_series.push(the_rest) : top_series
57 | @ranges_container_selector = ".versions-#{key}-graph-ranges"
58 | end
59 |
60 | respond_to do |format|
61 | format.json { render json: @series }
62 | format.js
63 | end
64 | end
65 |
66 | private
67 |
68 | DEFAULT_LIMITS = { "ruby_unique" => 7, "bundler_unique" => 7, "rubygems_unique" => 7 }
69 | DEFAULT_LIMITS.default = 5
70 | MAX_LIMIT = 25
71 |
72 | def count(data)
73 | # Build a hash of hashes we can use to look up values for a specific date
74 | count_map = Hash.new { |h, k| h[k] = {} }
75 | data.each { |date, version, count| count_map[date][version] = count }
76 | count_map
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper; end
2 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console:0 */
2 | import "stylesheets/application.scss";
3 |
4 | import jQuery from "jquery";
5 | import ApexCharts from "apexcharts";
6 | import Rails from "rails-ujs";
7 | require.context("../../assets/images", false, /\.(png|jpe?g|svg)$/);
8 |
9 | Rails.start();
10 |
11 | window.jQuery = jQuery;
12 | window.ApexCharts = ApexCharts;
13 | window.Rails = Rails;
14 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base; end
2 |
--------------------------------------------------------------------------------
/app/lib/date_range.rb:
--------------------------------------------------------------------------------
1 | class DateRange
2 | RANGES = {
3 | "1year" => Date.today - 1.year,
4 | "3months" => Date.today - 90.days,
5 | "1month" => Date.today - 30.days,
6 | "2weeks" => Date.today - 2.weeks
7 | }
8 |
9 | RANGES.default = Date.today - 90.days
10 |
11 | attr_reader :value
12 |
13 | def initialize(params)
14 | @value = RANGES[params[:range]]
15 | end
16 |
17 | def prefix
18 | @value > RANGES["1year"] ? "daily_" : "weekly_"
19 | end
20 |
21 | def key
22 | RANGES.key(@value)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: "from@example.com"
3 | layout "mailer"
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/import_status.rb:
--------------------------------------------------------------------------------
1 | class ImportStatus < ApplicationRecord
2 | def self.import(key, json_body)
3 | json = JSON.parse(json_body)
4 |
5 | json.each do |date, data|
6 | create!(key: key, date: date, data: data)
7 | end
8 | end
9 |
10 | def self.fetched?(key)
11 | where(key: key).count > 0
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/stat.rb:
--------------------------------------------------------------------------------
1 | class Stat < ApplicationRecord
2 | def self.weekly_data(key, range)
3 | if key.in?(BUCKETS.keys)
4 | base_query(key, range).group(:week, BUCKETS[key]).pluck(
5 | week,
6 | send(BUCKETS[key]),
7 | sum
8 | )
9 | else
10 | base_query(key, range).group(:week, :value).pluck(week, :value, sum)
11 | end
12 | end
13 |
14 | def self.weekly_totals(key, range)
15 | base_query(key, range).group(:week).pluck(week, sum).reduce(
16 | {}
17 | ) do |totals, row|
18 | totals[row[0]] = row[1]
19 | totals
20 | end
21 | end
22 |
23 | def self.daily_totals(key, range)
24 | base_query(key, range).group(:date).order(:date).sum(:count)
25 | end
26 |
27 | def self.daily_data(key, range)
28 | if key.in?(BUCKETS.keys)
29 | base_query(key, range).group(:date, BUCKETS[key]).pluck(
30 | :date,
31 | send(BUCKETS[key]),
32 | sum
33 | )
34 | else
35 | base_query(key, range).pluck(:date, :value, :count)
36 | end
37 | end
38 |
39 | def self.daily_comparison(key1, key2, range)
40 | Stat.where("(key = ? OR key = ?) AND date > ?", key1, key2, range).where
41 | .not(value: "")
42 | .group(:date, :key)
43 | .order(:date)
44 | .pluck(:date, :key, sum)
45 | end
46 |
47 | def self.weekly_comparison(key1, key2, range)
48 | Stat.where("(key = ? OR key = ?) AND date > ?", key1, key2, range).where
49 | .not(value: "")
50 | .group(:week, :key)
51 | .pluck(week, :key, sum)
52 | end
53 |
54 | private
55 |
56 | BUCKETS = {
57 | "ruby" => :minor,
58 | "bundler" => :minor,
59 | "rubygems" => :minor,
60 | "ci" => :ci,
61 | "platform" => :platform
62 | }
63 | BUCKETS.merge!(BUCKETS.transform_keys { |k| "#{k}_unique" })
64 |
65 | PLATFORMS = %w[linux mingw32 darwin java]
66 |
67 | def self.week
68 | Arel.sql("date_trunc('week', date) AS week")
69 | end
70 |
71 | def self.minor
72 | Arel.sql("substring(value from '\\d.\\d*') AS minor")
73 | end
74 |
75 | def self.sum
76 | Arel.sql("sum(count) AS sum")
77 | end
78 |
79 | def self.ci
80 | # values for ci are a comma separated string corresponding to
81 | # various CI related ENV vars that are set on the source system.
82 | # Here, we have a somewhat ugly case statement in SQL to figure out
83 | # which one of the values to use
84 | Arel.sql(
85 | "CASE array_length(string_to_array(value, ','), 1)
86 | WHEN 1 THEN CASE value
87 | WHEN 'ci' THEN 'unknown'
88 | ELSE value
89 | END
90 | WHEN 2 THEN CASE (string_to_array(value, ','))[1]
91 | WHEN '' THEN (string_to_array(value, ','))[2]
92 | WHEN 'ci' THEN (string_to_array(value, ','))[2]
93 | ELSE (string_to_array(value, ','))[1]
94 | END
95 | ELSE 'unknown'
96 | END
97 | AS ci"
98 | )
99 | end
100 |
101 | def self.platform
102 | Arel.sql(
103 | "COALESCE(" +
104 | PLATFORMS.map { |p| "substring(value from '#{p}')" }.join(",") +
105 | ", 'other') AS platform"
106 | )
107 | end
108 |
109 | def self.base_query(key, range)
110 | Stat.where("key = ? AND date > ?", key, range).where.not(value: "")
111 | end
112 | end
113 |
--------------------------------------------------------------------------------
/app/views/home/_chart.js.erb:
--------------------------------------------------------------------------------
1 | Rails.ajax({
2 | url: "<%= path %>",
3 | type: "get",
4 | success: series => {
5 | const options = {
6 | stroke: {
7 | width: 2
8 | },
9 | grid: {
10 | show: true,
11 | yaxis: {
12 | lines: {
13 | show: true
14 | }
15 | }
16 | },
17 | legend: {
18 | formatter: value => value[0].toUpperCase() + value.slice(1),
19 | itemMargin: {
20 | horizontal: 20,
21 | vertical: 25
22 | }
23 | },
24 | chart: {
25 | id: "chart-<%= path.parameterize %>",
26 | height: 400,
27 | animations: {
28 | enabled: false
29 | },
30 | toolbar: { show: false }
31 | },
32 | series,
33 | yaxis: {
34 | labels: {
35 | formatter: value => `${Math.round(Number(value) * 100) / 100}%`,
36 | style: {
37 | fontSize: "14px",
38 | lineHeight: "16px",
39 | fontFamily: "Roboto",
40 | fontWeight: 300
41 | }
42 | }
43 | },
44 | xaxis: {
45 | labels: {
46 | style: {
47 | fontSize: "10px",
48 | lineHeight: "16px",
49 | fontFamily: "Roboto",
50 | fontWeight: 300,
51 | cssClass: "_3months"
52 | }
53 | }
54 | },
55 | colors: [
56 | "#1ABC9C",
57 | "#F1C40F",
58 | "#3498DB",
59 | "#E74C3C",
60 | "#9B59B6",
61 | "#34495E"
62 | ],
63 | dataLabels: { enabled: false },
64 | tooltip: {
65 | custom: ({
66 | series,
67 | seriesIndex,
68 | w: {
69 | config: { colors },
70 | globals: { seriesNames }
71 | },
72 | dataPointIndex
73 | }) => {
74 | return [...series]
75 | .sort((a, b) => b[dataPointIndex] - a[dataPointIndex])
76 | .map((s, i) => {
77 | const sIndex = series.indexOf(s);
78 | return `
79 |
`;
94 | })
95 | .join("\n");
96 | }
97 | }
98 | };
99 | new ApexCharts(
100 | document.querySelector("#<%= path.parameterize %>-graph"),
101 | options
102 | ).render();
103 | document
104 | .querySelector(".<%= path.parameterize %>-graph-ranges")
105 | .parentElement.classList.toggle("hidden");
106 | }
107 | });
108 |
--------------------------------------------------------------------------------
/app/views/home/_charts.html.erb:
--------------------------------------------------------------------------------
1 | <% charts.each do | name, path | %>
2 |
3 |
4 | <%= h name %>
5 | <%= content_tag :div, class: "#{path.parameterize}-graph-ranges" do %>
6 | <%= link_to "2w", "#{path}?range=2weeks", remote: true, tabindex: 0, class: "2weeks" %>
7 | <%= link_to "1m", "#{path}?range=1month", remote: true, tabindex: 0, class: "1month" %>
8 | <%= link_to "3m", "#{path}?range=3months", remote: true, tabindex: 0, class: "3months active" %>
9 | <%= link_to "12m", "#{path}?range=1year", remote: true, tabindex: 0, class: "1year" %>
10 | <%end %>
11 |
12 | <%= content_tag :div, id: "#{path.parameterize}-graph" do %>
13 | <% end %>
14 |
15 |
16 | <% end %>
17 |
18 |
--------------------------------------------------------------------------------
/app/views/home/_totals.js.erb:
--------------------------------------------------------------------------------
1 | Rails.ajax({
2 | url: "/versions/<%= CGI.escape(key) %>?total=true",
3 | type: "get",
4 | success: series => {
5 | const options = {
6 | stroke: {
7 | width: 2
8 | },
9 | chart: {
10 | id: "totals-chart-versions-<%= CGI.escape(key) %>",
11 | height: 400,
12 | type: "bar",
13 | animations: { enabled: false }
14 | },
15 | series,
16 | xaxis: {
17 | labels: {
18 | style: {
19 | fontSize: "10px",
20 | lineHeight: "16px",
21 | fontFamily: "Roboto",
22 | fontWeight: 300,
23 | cssClass: "_3months"
24 | }
25 | }
26 | },
27 | colors: [
28 | "#3498DB",
29 | ],
30 | dataLabels: { enabled: false }
31 | };
32 | new ApexCharts(
33 | document.querySelector("#<%= key %>-total"),
34 | options
35 | ).render();
36 | document
37 | .querySelector(".ruby-total-ranges")
38 | .parentElement.classList.toggle("hidden");
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | Version breakdowns
2 |
3 | The version graphs try to count each run of gem install
or bundle install
exactly one time. However, since each of those commands can trigger dozens or even hundreds of HTTP requests, these results are not necessarily accurate. After trying to keep only one entry per install command, we group each minor version together because graphing every patch version just makes a mess.
4 |
5 | <%= render "charts", charts: @version_charts %>
6 |
7 | Traffic Comparisons
8 |
9 | Group all traffic for relative comparison. In these graphs, "RubyGems" means gem install
while "Bundler" means bundle install
, and "Unknown" means Bundler found a value in the CI
environment variable, but no other variable indicating the specific CI environment.
10 |
11 | <%= render "charts", charts: @comparison_charts %>
12 |
13 | Traffic Totals
14 |
15 | The count of requests whose logs were fully processed and counted on a given day. All other charts are percentages relative to the total, while this chart shows the absolute number of data points available on a given day.
16 |
17 |
18 |
19 | Total Requests
20 | <%= content_tag :div, class: "ruby-total-ranges" do %>
21 | <%= link_to "2w", "/versions/ruby?total=true&range=2weeks", remote: true, tabindex: 0, class: "2weeks" %>
22 | <%= link_to "1m", "/versions/ruby?total=true&range=1month", remote: true, tabindex: 0, class: "1month" %>
23 | <%= link_to "3m", "/versions/ruby?total=true&range=3months", remote: true, tabindex: 0, class: "3months active" %>
24 | <%= link_to "12m", "/versions/ruby?total=true&range=1year", remote: true, tabindex: 0, class: "1year" %>
25 | <%end %>
26 |
27 | <%= content_tag :div, id: "ruby-total" do %>
28 | <% end %>
29 |
30 |
31 |
37 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RubyGems.org stats
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_pack_tag 'application', media: 'all' %>
9 | <%= javascript_pack_tag 'application' %>
10 |
11 |
12 |
13 |
29 |
30 | <%= yield %>
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/views/versions/show.js.erb:
--------------------------------------------------------------------------------
1 | ApexCharts.exec(
2 | '<%= @total ? "totals-" : "" %>chart-<%= request.path.parameterize %>',
3 | "updateSeries",
4 | JSON.parse(`<%= raw(@series.to_json) %>`),
5 | true
6 | );
7 | ApexCharts.exec(
8 | '<%= @total ? "totals-" : "" %>chart-<%= request.path.parameterize %>',
9 | "updateOptions",
10 | {
11 | xaxis: {
12 | labels: {
13 | style: {
14 | cssClass: "_<%= @range.key %>"
15 | }
16 | }
17 | }
18 | }
19 | );
20 |
21 | Array
22 | .from(document.querySelectorAll("<%= @ranges_container_selector %> a"))
23 | .forEach(link => link.classList.remove("active"))
24 | document
25 | .querySelector("<%= @ranges_container_selector %>")
26 | .querySelector("[class=\"<%= @range.key %>\"]")
27 | .classList.add("active")
28 |
--------------------------------------------------------------------------------
/app/workers/import_stats_day_worker.rb:
--------------------------------------------------------------------------------
1 | class ImportStatsDayWorker
2 | include Sidekiq::Worker
3 |
4 | def perform(date)
5 | import_status_ids = []
6 | stats = Hash.new { |h, k| h[k] = Hash.new(0) }
7 |
8 | ImportStatus.where(date: date).find_each do |is|
9 | import_status_ids.push(is.id)
10 |
11 | is.data.each do |name, vv|
12 | vv.each do |version, count|
13 | if count.is_a?(Hash)
14 | stats[name][version] += count["total"]
15 | stats["#{name}_unique"][version] += count["unique"]
16 | else
17 | stats[name][version] += count
18 | end
19 | end
20 | end
21 | end
22 |
23 | ImportStatus.transaction do
24 | ImportStatus.where(id: import_status_ids).update_all(
25 | imported_at: Time.now
26 | )
27 |
28 | Stat.bulk_insert(
29 | update_duplicates: %w[date key value], ignore: false
30 | ) do |t|
31 | stats.each do |name, value_map|
32 | value_map.each do |value, count|
33 | t.add(date: date, key: name, value: value, count: count)
34 | end
35 | end
36 | end
37 | end
38 |
39 | Rails.logger.info "Finished importing stats for #{date}"
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/workers/import_stats_days_worker.rb:
--------------------------------------------------------------------------------
1 | class ImportStatsDaysWorker
2 | include Sidekiq::Worker
3 |
4 | def perform
5 | dates = ImportStatus.where("imported_at IS NULL").pluck(:date).uniq
6 | dates -= [Time.now.utc.to_date]
7 |
8 | Sidekiq::Client.push_bulk(
9 | "class" => "ImportStatsDayWorker", "args" => dates.map { |d| [d] }
10 | )
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/workers/import_stats_file_worker.rb:
--------------------------------------------------------------------------------
1 | class ImportStatsFileWorker
2 | include Sidekiq::Worker
3 |
4 | def perform(key)
5 | return if ImportStatus.fetched?(key)
6 |
7 | s3 = Aws::S3::Client.new
8 | bucket = Rails.application.config.stats.bucket_name
9 |
10 | body = s3.get_object(bucket: bucket, key: key).body.read
11 | ImportStatus.import(key, body)
12 | Rails.logger.info("Downloaded #{key}")
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/workers/import_stats_worker.rb:
--------------------------------------------------------------------------------
1 | class ImportStatsWorker
2 | include Sidekiq::Worker
3 |
4 | def perform
5 | s3 = Aws::S3::Client.new
6 | bucket_name = Rails.application.config.stats.bucket_name
7 | prefix = Rails.application.config.stats.prefix
8 |
9 | last_key = ImportStatus.order("key COLLATE \"C\" DESC").
10 | limit(1).pluck(:key).first
11 | keys = s3.list_objects_v2(
12 | bucket: bucket_name, prefix: prefix, start_after: last_key
13 | ).contents.map(&:key)
14 |
15 | Rails.logger.info "Found #{keys.size} pending stats files"
16 |
17 | Sidekiq::Client.push_bulk(
18 | "class" => "ImportStatsFileWorker", "args" => keys.map { |k| [k] }
19 | )
20 |
21 | if keys.size == 1000
22 | Rails.logger.info "Re-running #{self.class} in 5 minutes"
23 | self.class.perform_in(5.minutes)
24 | else
25 | Rails.logger.info "Running ImportStatsDaysWorker in 5 minutes"
26 | ImportStatsDaysWorker.perform_in(5.minutes)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | var validEnv = ["development", "test", "production"];
3 | var currentEnv = api.env();
4 | var isDevelopmentEnv = api.env("development");
5 | var isProductionEnv = api.env("production");
6 | var isTestEnv = api.env("test");
7 |
8 | if (!validEnv.includes(currentEnv)) {
9 | throw new Error(
10 | "Please specify a valid `NODE_ENV` or " +
11 | '`BABEL_ENV` environment variables. Valid values are "development", ' +
12 | '"test", and "production". Instead, received: ' +
13 | JSON.stringify(currentEnv) +
14 | "."
15 | );
16 | }
17 |
18 | return {
19 | presets: [
20 | isTestEnv && [
21 | require("@babel/preset-env").default,
22 | {
23 | targets: {
24 | node: "current"
25 | }
26 | }
27 | ],
28 | (isProductionEnv || isDevelopmentEnv) && [
29 | require("@babel/preset-env").default,
30 | {
31 | forceAllTransforms: true,
32 | useBuiltIns: "entry",
33 | corejs: 3,
34 | modules: false,
35 | exclude: ["transform-typeof-symbol"]
36 | }
37 | ]
38 | ].filter(Boolean),
39 | plugins: [
40 | require("babel-plugin-macros"),
41 | require("@babel/plugin-syntax-dynamic-import").default,
42 | isTestEnv && require("babel-plugin-dynamic-import-node"),
43 | require("@babel/plugin-transform-destructuring").default,
44 | [
45 | require("@babel/plugin-proposal-class-properties").default,
46 | {
47 | loose: true
48 | }
49 | ],
50 | [
51 | require("@babel/plugin-proposal-object-rest-spread").default,
52 | {
53 | useBuiltIns: true
54 | }
55 | ],
56 | [
57 | require("@babel/plugin-transform-runtime").default,
58 | {
59 | helpers: false,
60 | regenerator: true,
61 | corejs: false
62 | }
63 | ],
64 | [
65 | require("@babel/plugin-transform-regenerator").default,
66 | {
67 | async: false
68 | }
69 | ]
70 | ].filter(Boolean)
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1 || ">= 0.a"
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../gems.rb", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_version
64 | @bundler_version ||= begin
65 | env_var_version || cli_arg_version ||
66 | lockfile_version || "#{Gem::Requirement.default}.a"
67 | end
68 | end
69 |
70 | def load_bundler!
71 | ENV["BUNDLE_GEMFILE"] ||= gemfile
72 |
73 | # must dup string for RG < 1.8 compatibility
74 | activate_bundler(bundler_version.dup)
75 | end
76 |
77 | def activate_bundler(bundler_version)
78 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
79 | bundler_version = "< 2"
80 | end
81 | gem_error = activation_error_handling do
82 | gem "bundler", bundler_version
83 | end
84 | return if gem_error.nil?
85 | require_error = activation_error_handling do
86 | require "bundler/version"
87 | end
88 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
89 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
90 | exit 42
91 | end
92 |
93 | def activation_error_handling
94 | yield
95 | nil
96 | rescue StandardError, LoadError => e
97 | e
98 | end
99 | end
100 |
101 | m.load_bundler!
102 |
103 | if m.invoked_as_script?
104 | load Gem.bin_path("bundler", "bundle")
105 | end
106 |
--------------------------------------------------------------------------------
/bin/production:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | HEROKU_APP=rt-ecosystem exec "${HEROKU_COMMAND:-heroku}" "$@"
3 |
--------------------------------------------------------------------------------
/bin/puma:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'puma' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("puma", "puma")
30 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/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/sidekiq:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'sidekiq' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("sidekiq", "sidekiq")
30 |
--------------------------------------------------------------------------------
/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/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/webpack_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::WebpackRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/dev_server_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::DevServerRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/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 | module Ecosystem
21 | class Application < Rails::Application
22 | config.load_defaults 5.2
23 |
24 | # Settings in config/environments/* take precedence over those specified here.
25 | # Application configuration can go into files in config/initializers
26 | # -- all .rb files in that directory are automatically loaded after loading
27 | # the framework and any gems in your application.
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/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/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | hqjgPubl7eseaj/twsGO2pIw8U0Z24mQcutC6KrAeyXIu6KbeCUrgvKcRQVnEbbDSNSsIWTcMrkAWuIlv/YM8wxYQz/Zxv3/ICV7PYOc14iN1Fnxle+MoBEIZ16scPEHgg3L520+27uEjrOzul9tiA3aW/HcrhuSoMJMSd3i3FlpdbjhwakpFQLBM5MNTftlKzF/kpapUAULjkze6FcPrHm5u3tkxtgAZnPWYJ7I5JbIQSLq5cPgzj8jcSW99zCHt8V19+/qi99D7pjCUJFx6RD6IMv0isiuZEfyPuiy1yUMYnzT+mqIqnD8Fr97VR+jIefItpNSTMFAYRxvBppNXxZFQvwCWnXRi3Lfxjmy7OnkuMD7K9Oez0KMPYpnTdCEfq2z7iaUoc96ipMbDWGtwRzEg0NzP5i8BdDf--Q18ccBuAiTlZYAoi--RLFLKBnSuweW/iGSpTor6g==
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | encoding: unicode
4 | pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5).to_i + 1 %>
5 |
6 | development:
7 | <<: *default
8 | database: ecosystem
9 |
10 | test:
11 | <<: *default
12 | database: ecosystem_test
13 |
--------------------------------------------------------------------------------
/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 | # Verifies that versions and hashed value of the package contents in the project's package.json
3 | config.webpacker.check_yarn_integrity = true
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded on
7 | # every request. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join("tmp", "caching-dev.txt").exist?
20 | config.action_controller.perform_caching = true
21 |
22 | config.cache_store = :memory_store
23 | config.public_file_server.headers = {
24 | "Cache-Control" => "public, max-age=#{2.days.to_i}"
25 | }
26 | else
27 | config.action_controller.perform_caching = false
28 |
29 | config.cache_store = :null_store
30 | end
31 |
32 | # Don't care if the mailer can't send.
33 | config.action_mailer.raise_delivery_errors = false
34 |
35 | config.action_mailer.perform_caching = false
36 |
37 | # Print deprecation notices to the Rails logger.
38 | config.active_support.deprecation = :log
39 |
40 | # Raise an error on page load if there are pending migrations.
41 | config.active_record.migration_error = :page_load
42 |
43 | # Highlight code that triggered database queries in logs.
44 | config.active_record.verbose_query_logs = true
45 |
46 | # Raises error for missing translations
47 | # config.action_view.raise_on_missing_translations = true
48 |
49 | # Use an evented file watcher to asynchronously detect changes in source code,
50 | # routes, locales, etc. This feature depends on the listen gem.
51 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
52 | end
53 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Verifies that versions and hashed value of the package contents in the project's package.json
3 | config.webpacker.check_yarn_integrity = false
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
26 |
27 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
28 | # config.action_controller.asset_host = 'http://assets.example.com'
29 |
30 | # Specifies the header that your server uses for sending files.
31 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
32 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
33 |
34 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
35 | # config.force_ssl = true
36 |
37 | # Use the lowest log level to ensure availability of diagnostic information
38 | # when problems arise.
39 | config.log_level = :info
40 |
41 | # Prepend all log lines with the following tags.
42 | config.log_tags = %i[request_id]
43 |
44 | # Use a different cache store in production.
45 | # config.cache_store = :mem_cache_store
46 |
47 | # Use a real queuing backend for Active Job (and separate queues per environment)
48 | # config.active_job.queue_adapter = :resque
49 | # config.active_job.queue_name_prefix = "ecosystem_#{Rails.env}"
50 |
51 | config.action_mailer.perform_caching = false
52 |
53 | # Ignore bad email addresses and do not raise email delivery errors.
54 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
55 | # config.action_mailer.raise_delivery_errors = false
56 |
57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 | # the I18n.default_locale when a translation cannot be found).
59 | config.i18n.fallbacks = true
60 |
61 | # Send deprecation notices to registered listeners.
62 | config.active_support.deprecation = :notify
63 |
64 | # Use default logging formatter so that PID and timestamp are not suppressed.
65 | config.log_formatter = ::Logger::Formatter.new
66 |
67 | # Use a different logger for distributed setups.
68 | # require 'syslog/logger'
69 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
70 |
71 | if ENV["RAILS_LOG_TO_STDOUT"].present?
72 | logger = ActiveSupport::Logger.new(STDOUT)
73 | logger.formatter = config.log_formatter
74 | config.logger = ActiveSupport::TaggedLogging.new(logger)
75 | config.lograge.enabled = true
76 | end
77 |
78 | # Do not dump schema after migrations.
79 | config.active_record.dump_schema_after_migration = false
80 | end
81 |
--------------------------------------------------------------------------------
/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 = true
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 | config.action_mailer.perform_caching = false
32 |
33 | # Tell Action Mailer not to deliver emails to the real world.
34 | # The :test delivery method accumulates sent emails in the
35 | # ActionMailer::Base.deliveries array.
36 | config.action_mailer.delivery_method = :test
37 |
38 | # Print deprecation notices to the stderr.
39 | config.active_support.deprecation = :stderr
40 |
41 | # Raises error for missing translations
42 | # config.action_view.raise_on_missing_translations = true
43 | end
44 |
--------------------------------------------------------------------------------
/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/aws_s3.rb:
--------------------------------------------------------------------------------
1 | require "aws-sdk-core"
2 |
3 | Aws.config.update(logger: ::Rails.logger)
4 |
5 | Rails.application.config.stats =
6 | OpenStruct.new(
7 | bucket_name:
8 | ENV.fetch("STATS_BUCKET_NAME", "rubygems-logs-staging.rubytogether"),
9 | prefix: ENV.fetch("STATS_BUCKET_PREFIX", "fastly_stats/")
10 | )
11 |
--------------------------------------------------------------------------------
/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, "'unsafe-inline'"
13 | policy.style_src :self, :https, "'unsafe-inline' fonts.googleapis.com"
14 |
15 | # Specify URI for violation reports
16 | # policy.report_uri "/csp-violation-report-endpoint"
17 |
18 | if Rails.env.development?
19 | policy.connect_src :self,
20 | :https,
21 | "http://localhost:3036",
22 | "ws://localhost:3035"
23 | policy.style_src :self, :blob, "'unsafe-inline' fonts.googleapis.com"
24 | end
25 | end
26 |
27 | # If you are using UJS then enable automatic nonce generation
28 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
29 |
30 | # Report CSP violations to a specified URI
31 | # For further information see the following documentation:
32 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
33 | # Rails.application.config.content_security_policy_report_only = true
34 |
--------------------------------------------------------------------------------
/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 += %i[password]
5 |
--------------------------------------------------------------------------------
/config/initializers/generators.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.generators do |g|
2 | g.orm :active_record, primary_key_type: :uuid
3 | end
4 |
--------------------------------------------------------------------------------
/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/sidekiq.rb:
--------------------------------------------------------------------------------
1 | Sidekiq.configure_server do |config|
2 | config.redis = {
3 | url: ENV["REDIS_URL"],
4 | ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
5 | }
6 | end
7 |
8 | Sidekiq.configure_client do |config|
9 | config.redis = {
10 | url: ENV["REDIS_URL"],
11 | ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
12 | }
13 | end
14 |
--------------------------------------------------------------------------------
/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) { wrap_parameters format: %i[json] }
8 |
9 | # To enable root element in JSON for ActiveRecord objects.
10 | # ActiveSupport.on_load(:active_record) do
11 | # self.include_root_in_json = true
12 | # end
13 |
--------------------------------------------------------------------------------
/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") { 3_000 }
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 "versions/:key" => "versions#show"
3 | get "comparison/:key1/:key2" => "comparison#show"
4 |
5 | if Rails.env.development?
6 | require "sidekiq/pro/web"
7 | mount Sidekiq::Web => "/sidekiq"
8 | end
9 |
10 | root "home#index"
11 | match "*missing", to: "application#not_found", via: :all
12 | end
13 |
--------------------------------------------------------------------------------
/config/sidekiq.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :concurrency: 5
3 | :timeout: 8
4 |
5 | :queues:
6 | - critical
7 | - default
8 | - low
9 |
10 | production:
11 | :concurrency: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
12 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || "development";
2 |
3 | const environment = require("./environment");
4 |
5 | module.exports = environment.toWebpackConfig();
6 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require("@rails/webpacker");
2 |
3 | module.exports = environment;
4 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || "production";
2 |
3 | const environment = require("./environment");
4 |
5 | module.exports = environment.toWebpackConfig();
6 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || "development";
2 |
3 | const environment = require("./environment");
4 |
5 | module.exports = environment.toWebpackConfig();
6 |
--------------------------------------------------------------------------------
/config/webpacker.yml:
--------------------------------------------------------------------------------
1 | # Note: You must restart bin/webpack-dev-server for changes to take effect
2 |
3 | default: &default
4 | source_path: app/javascript
5 | source_entry_path: packs
6 | public_root_path: public
7 | public_output_path: packs
8 | cache_path: tmp/cache/webpacker
9 | check_yarn_integrity: false
10 | webpack_compile_output: false
11 |
12 | # Additional paths webpack should lookup modules
13 | # ['app/assets', 'engine/foo/app/assets']
14 | resolved_paths: ['app/assets']
15 |
16 | # Reload manifest.json on all requests so we reload latest compiled packs
17 | cache_manifest: false
18 |
19 | # Extract and emit a css file
20 | extract_css: false
21 |
22 | static_assets_extensions:
23 | - .jpg
24 | - .jpeg
25 | - .png
26 | - .gif
27 | - .tiff
28 | - .ico
29 | - .svg
30 | - .eot
31 | - .otf
32 | - .ttf
33 | - .woff
34 | - .woff2
35 |
36 | extensions:
37 | - .mjs
38 | - .js
39 | - .sass
40 | - .scss
41 | - .css
42 | - .module.sass
43 | - .module.scss
44 | - .module.css
45 | - .png
46 | - .svg
47 | - .gif
48 | - .jpeg
49 | - .jpg
50 |
51 | development:
52 | <<: *default
53 | compile: true
54 |
55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
56 | check_yarn_integrity: true
57 |
58 | # Reference: https://webpack.js.org/configuration/dev-server/
59 | dev_server:
60 | https: false
61 | host: localhost
62 | port: 3035
63 | public: localhost:3035
64 | hmr: false
65 | # Inline should be set to true if using HMR
66 | inline: true
67 | overlay: true
68 | compress: true
69 | disable_host_check: true
70 | use_local_ip: false
71 | quiet: false
72 | headers:
73 | 'Access-Control-Allow-Origin': '*'
74 | watch_options:
75 | ignored: '**/node_modules/**'
76 |
77 |
78 | test:
79 | <<: *default
80 | compile: true
81 |
82 | # Compile test packs to a separate directory
83 | public_output_path: packs-test
84 |
85 | production:
86 | <<: *default
87 |
88 | # Production depends on precompilation of packs prior to booting for performance.
89 | compile: false
90 |
91 | # Extract and emit a css file
92 | extract_css: true
93 |
94 | # Cache manifest.json for performance
95 | cache_manifest: true
96 |
--------------------------------------------------------------------------------
/db/migrate/20180513075234_enable_uuid.rb:
--------------------------------------------------------------------------------
1 | class EnableUuid < ActiveRecord::Migration[5.2]
2 | def change
3 | enable_extension "pgcrypto"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20180513075308_create_stats.rb:
--------------------------------------------------------------------------------
1 | class CreateStats < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :stats, id: :uuid do |t|
4 | t.date :date, null: false
5 | t.string :key, null: false
6 | t.string :value, null: false
7 | t.integer :count, null: false, default: 0
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20180929063650_create_import_statuses.rb:
--------------------------------------------------------------------------------
1 | class CreateImportStatuses < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :import_statuses, id: :uuid do |t|
4 | t.string :key, unique: true, null: false
5 | t.date :date, null: false
6 | t.timestamp :imported_at
7 | t.jsonb :data
8 | t.timestamps
9 |
10 | t.index :key
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20190708173946_add_timestamps_to_stats.rb:
--------------------------------------------------------------------------------
1 | class AddTimestampsToStats < ActiveRecord::Migration[5.2]
2 | def change
3 | add_timestamps :stats, null: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20190713153752_add_unique_index_to_stats.rb:
--------------------------------------------------------------------------------
1 | class AddUniqueIndexToStats < ActiveRecord::Migration[5.2]
2 | def change
3 | add_index :stats, %i[date key value], 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 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_07_13_153752) do
14 | # These are extensions that must be enabled in order to support this database
15 | enable_extension "pgcrypto"
16 | enable_extension "plpgsql"
17 |
18 | create_table "import_statuses",
19 | id: :uuid,
20 | default: -> { "gen_random_uuid()" },
21 | force: :cascade do |t|
22 | t.string "key", null: false
23 | t.date "date", null: false
24 | t.datetime "imported_at"
25 | t.jsonb "data"
26 | t.datetime "created_at", null: false
27 | t.datetime "updated_at", null: false
28 | t.index %w[key], name: "index_import_statuses_on_key"
29 | end
30 |
31 | create_table "stats",
32 | id: :uuid,
33 | default: -> { "gen_random_uuid()" },
34 | force: :cascade do |t|
35 | t.date "date", null: false
36 | t.string "key", null: false
37 | t.string "value", null: false
38 | t.integer "count", default: 0, null: false
39 | t.datetime "created_at"
40 | t.datetime "updated_at"
41 | t.index %w[date key value],
42 | name: "index_stats_on_date_and_key_and_value", unique: true
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | require "csv"
2 |
3 | data_file = File.expand_path("seeds/stats.csv", __dir__)
4 |
5 | created_at = Time.now
6 |
7 | stats = []
8 | CSV.foreach(data_file) { |row| stats.push([[created_at.year, *row[1..2]].join("-"), *row[3..-1]]) }
9 | stats.shift
10 |
11 | stat_values =
12 | stats.map do |s|
13 | s.push(created_at, created_at)
14 | "(" + s.map { |v| "'#{v}'" }.join(", ") + ")"
15 | end.join(", ")
16 |
17 | insert =
18 | "INSERT INTO stats
19 | (date, value, count, key, created_at, updated_at)
20 | VALUES #{
21 | stat_values
22 | }"
23 | Stat.connection.execute(insert) # remove header row
24 |
--------------------------------------------------------------------------------
/gems.locked:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: https://github.com/jamis/bulk_insert.git
3 | revision: 3ded6b059af8d239ca0f5fbecca0de6138d49592
4 | specs:
5 | bulk_insert (1.7.0)
6 | activerecord (>= 3.2.0)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | remote: https://gems.contribsys.com/
11 | specs:
12 | actioncable (5.2.3)
13 | actionpack (= 5.2.3)
14 | nio4r (~> 2.0)
15 | websocket-driver (>= 0.6.1)
16 | actionmailer (5.2.3)
17 | actionpack (= 5.2.3)
18 | actionview (= 5.2.3)
19 | activejob (= 5.2.3)
20 | mail (~> 2.5, >= 2.5.4)
21 | rails-dom-testing (~> 2.0)
22 | actionpack (5.2.3)
23 | actionview (= 5.2.3)
24 | activesupport (= 5.2.3)
25 | rack (~> 2.0)
26 | rack-test (>= 0.6.3)
27 | rails-dom-testing (~> 2.0)
28 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
29 | actionview (5.2.3)
30 | activesupport (= 5.2.3)
31 | builder (~> 3.1)
32 | erubi (~> 1.4)
33 | rails-dom-testing (~> 2.0)
34 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
35 | activejob (5.2.3)
36 | activesupport (= 5.2.3)
37 | globalid (>= 0.3.6)
38 | activemodel (5.2.3)
39 | activesupport (= 5.2.3)
40 | activerecord (5.2.3)
41 | activemodel (= 5.2.3)
42 | activesupport (= 5.2.3)
43 | arel (>= 9.0)
44 | activestorage (5.2.3)
45 | actionpack (= 5.2.3)
46 | activerecord (= 5.2.3)
47 | marcel (~> 0.3.1)
48 | activesupport (5.2.3)
49 | concurrent-ruby (~> 1.0, >= 1.0.2)
50 | i18n (>= 0.7, < 2)
51 | minitest (~> 5.1)
52 | tzinfo (~> 1.1)
53 | apexcharts (0.1.5)
54 | smart_kv (= 0.2.7)
55 | arel (9.0.0)
56 | aws-eventstream (1.0.3)
57 | aws-partitions (1.193.0)
58 | aws-sdk-core (3.61.1)
59 | aws-eventstream (~> 1.0, >= 1.0.2)
60 | aws-partitions (~> 1.0)
61 | aws-sigv4 (~> 1.1)
62 | jmespath (~> 1.0)
63 | aws-sdk-kms (1.24.0)
64 | aws-sdk-core (~> 3, >= 3.61.1)
65 | aws-sigv4 (~> 1.1)
66 | aws-sdk-s3 (1.46.0)
67 | aws-sdk-core (~> 3, >= 3.61.1)
68 | aws-sdk-kms (~> 1)
69 | aws-sigv4 (~> 1.1)
70 | aws-sigv4 (1.1.0)
71 | aws-eventstream (~> 1.0, >= 1.0.2)
72 | bindex (0.5.0)
73 | bootsnap (1.4.6)
74 | msgpack (~> 1.0)
75 | builder (3.2.3)
76 | byebug (11.0.0)
77 | coderay (1.1.2)
78 | concurrent-ruby (1.1.5)
79 | connection_pool (2.2.2)
80 | crass (1.0.4)
81 | diff-lcs (1.3)
82 | erubi (1.8.0)
83 | ffi (1.15.4)
84 | globalid (0.4.2)
85 | activesupport (>= 4.2.0)
86 | honeybadger (4.11.0)
87 | i18n (1.6.0)
88 | concurrent-ruby (~> 1.0)
89 | jmespath (1.4.0)
90 | listen (3.1.5)
91 | rb-fsevent (~> 0.9, >= 0.9.4)
92 | rb-inotify (~> 0.9, >= 0.9.7)
93 | ruby_dep (~> 1.2)
94 | lograge (0.11.2)
95 | actionpack (>= 4)
96 | activesupport (>= 4)
97 | railties (>= 4)
98 | request_store (~> 1.0)
99 | loofah (2.2.3)
100 | crass (~> 1.0.2)
101 | nokogiri (>= 1.5.9)
102 | mail (2.7.1)
103 | mini_mime (>= 0.1.1)
104 | marcel (0.3.3)
105 | mimemagic (~> 0.3.2)
106 | method_source (0.9.2)
107 | mimemagic (0.3.10)
108 | nokogiri (~> 1)
109 | rake
110 | mini_mime (1.0.2)
111 | mini_portile2 (2.6.1)
112 | minitest (5.11.3)
113 | msgpack (1.3.3)
114 | nio4r (2.5.4)
115 | nokogiri (1.12.5)
116 | mini_portile2 (~> 2.6.1)
117 | racc (~> 1.4)
118 | nokogiri (1.12.5-x86_64-darwin)
119 | racc (~> 1.4)
120 | pg (1.1.4)
121 | pry (0.12.2)
122 | coderay (~> 1.1.0)
123 | method_source (~> 0.9.0)
124 | pry-byebug (3.7.0)
125 | byebug (~> 11.0)
126 | pry (~> 0.10)
127 | pry-rails (0.3.9)
128 | pry (>= 0.10.4)
129 | puma (4.3.6)
130 | nio4r (~> 2.0)
131 | racc (1.6.0)
132 | rack (2.0.8)
133 | rack-protection (2.0.7)
134 | rack
135 | rack-proxy (0.6.5)
136 | rack
137 | rack-test (1.1.0)
138 | rack (>= 1.0, < 3)
139 | rails (5.2.3)
140 | actioncable (= 5.2.3)
141 | actionmailer (= 5.2.3)
142 | actionpack (= 5.2.3)
143 | actionview (= 5.2.3)
144 | activejob (= 5.2.3)
145 | activemodel (= 5.2.3)
146 | activerecord (= 5.2.3)
147 | activestorage (= 5.2.3)
148 | activesupport (= 5.2.3)
149 | bundler (>= 1.3.0)
150 | railties (= 5.2.3)
151 | sprockets-rails (>= 2.0.0)
152 | rails-dom-testing (2.0.3)
153 | activesupport (>= 4.2.0)
154 | nokogiri (>= 1.6)
155 | rails-html-sanitizer (1.2.0)
156 | loofah (~> 2.2, >= 2.2.2)
157 | railties (5.2.3)
158 | actionpack (= 5.2.3)
159 | activesupport (= 5.2.3)
160 | method_source
161 | rake (>= 0.8.7)
162 | thor (>= 0.19.0, < 2.0)
163 | rake (12.3.3)
164 | rb-fsevent (0.10.3)
165 | rb-inotify (0.9.10)
166 | ffi (>= 0.5.0, < 2)
167 | redis (4.1.2)
168 | request_store (1.5.0)
169 | rack (>= 1.4)
170 | rspec-core (3.8.2)
171 | rspec-support (~> 3.8.0)
172 | rspec-expectations (3.8.4)
173 | diff-lcs (>= 1.2.0, < 2.0)
174 | rspec-support (~> 3.8.0)
175 | rspec-mocks (3.8.1)
176 | diff-lcs (>= 1.2.0, < 2.0)
177 | rspec-support (~> 3.8.0)
178 | rspec-rails (3.8.2)
179 | actionpack (>= 3.0)
180 | activesupport (>= 3.0)
181 | railties (>= 3.0)
182 | rspec-core (~> 3.8.0)
183 | rspec-expectations (~> 3.8.0)
184 | rspec-mocks (~> 3.8.0)
185 | rspec-support (~> 3.8.0)
186 | rspec-support (3.8.2)
187 | ruby_dep (1.5.0)
188 | sidekiq (5.2.7)
189 | connection_pool (~> 2.2, >= 2.2.2)
190 | rack (>= 1.5.0)
191 | rack-protection (>= 1.5.0)
192 | redis (>= 3.3.5, < 5)
193 | sidekiq-pro (4.0.5)
194 | concurrent-ruby (>= 1.0.5)
195 | sidekiq (>= 5.2.1)
196 | smart_kv (0.2.7)
197 | sprockets (3.7.2)
198 | concurrent-ruby (~> 1.0)
199 | rack (> 1, < 3)
200 | sprockets-rails (3.2.1)
201 | actionpack (>= 4.0)
202 | activesupport (>= 4.0)
203 | sprockets (>= 3.0.0)
204 | thor (0.20.3)
205 | thread_safe (0.3.6)
206 | tzinfo (1.2.5)
207 | thread_safe (~> 0.1)
208 | web-console (3.7.0)
209 | actionview (>= 5.0)
210 | activemodel (>= 5.0)
211 | bindex (>= 0.4.0)
212 | railties (>= 5.0)
213 | webpacker (4.0.7)
214 | activesupport (>= 4.2)
215 | rack-proxy (>= 0.6.1)
216 | railties (>= 4.2)
217 | websocket-driver (0.7.1)
218 | websocket-extensions (>= 0.1.0)
219 | websocket-extensions (0.1.4)
220 |
221 | PLATFORMS
222 | ruby
223 | x86_64-darwin-17
224 |
225 | DEPENDENCIES
226 | apexcharts
227 | aws-sdk-s3 (~> 1.46)
228 | bootsnap (>= 1.1.0)
229 | bulk_insert!
230 | concurrent-ruby
231 | connection_pool
232 | honeybadger (~> 4.11)
233 | listen (>= 3.0.5, < 3.2)
234 | lograge (~> 0.11.2)
235 | pg (>= 0.18, < 2.0)
236 | pry-byebug
237 | pry-rails (~> 0.3.9)
238 | puma (~> 4.1)
239 | rack
240 | rack-protection
241 | rails (~> 5.2.3)
242 | redis
243 | rspec-rails (~> 3.8)
244 | sidekiq (~> 5.2)
245 | sidekiq-pro!
246 | web-console (>= 3.3.0)
247 | webpacker (~> 4.0)
248 |
249 | RUBY VERSION
250 | ruby 2.7.5p203
251 |
252 | BUNDLED WITH
253 | 2.1.4
254 |
--------------------------------------------------------------------------------
/gems.rb:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby Pathname.new(".ruby-version").expand_path(__dir__).read
5 |
6 | gem "rails", "~> 5.2.3"
7 |
8 | gem "apexcharts"
9 | gem "aws-sdk-s3", "~> 1.46"
10 | gem "bootsnap", ">= 1.1.0", require: false
11 | gem "bulk_insert", git: "https://github.com/jamis/bulk_insert.git"
12 | gem "honeybadger", "~> 4.11"
13 | gem "lograge", "~> 0.11.2"
14 | gem "pg", ">= 0.18", "< 2.0"
15 | gem "puma", "~> 4.1"
16 | gem "sidekiq", "~> 5.2"
17 | gem "webpacker", "~> 4.0"
18 |
19 | # Gems in both rubygems.org and gems.contribsys.com
20 | gem "redis"
21 | gem "connection_pool"
22 | gem "rack-protection"
23 | gem "concurrent-ruby"
24 | gem "rack"
25 |
26 | group :development, :test do
27 | gem "pry-byebug", require: false
28 | gem "pry-rails", "~> 0.3.9"
29 | gem "rspec-rails", "~> 3.8"
30 | end
31 |
32 | group :development do
33 | gem "web-console", ">= 3.3.0"
34 | gem "listen", ">= 3.0.5", "< 3.2"
35 | end
36 |
37 | source "https://gems.contribsys.com/" do
38 | gem "sidekiq-pro"
39 | end
40 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/temporary.rake:
--------------------------------------------------------------------------------
1 | namespace :temporary do
2 | desc "one-off tasks"
3 | task destroy_duplicate_stats: :environment do
4 | # delete all duplicate rows preserving the row with the maximum count
5 | Stat.where.not(id: Stat.order(:date, :key, :value, "count desc").select("distinct on (date, key, value) id")).delete_all
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/log/.keep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecosystem",
3 | "private": true,
4 | "dependencies": {
5 | "@rails/webpacker": "^4.0.7",
6 | "apexcharts": "^3.8.4",
7 | "cssnext": "^1.8.4",
8 | "jquery": "^3.3.1",
9 | "postcss-cssnext": "^3.1.0",
10 | "rails-ujs": "^5.2.3"
11 | },
12 | "devDependencies": {
13 | "@prettier/plugin-ruby": "^0.15.0",
14 | "eslint": "^6.4.0",
15 | "eslint-config-prettier": "^6.0.0",
16 | "eslint-config-standard": "^14.1.0",
17 | "eslint-plugin-import": "^2.18.2",
18 | "eslint-plugin-node": "^9.2.0",
19 | "eslint-plugin-prettier": "^3.1.1",
20 | "eslint-plugin-promise": "^4.2.1",
21 | "eslint-plugin-standard": "^4.0.1",
22 | "prettier": "^1.18.2",
23 | "webpack-dev-server": "^3.8.1"
24 | },
25 | "engines": {
26 | "node": "14.x"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require("postcss-import"),
4 | require("postcss-flexbugs-fixes"),
5 | require("postcss-preset-env")({
6 | autoprefixer: {
7 | flexbox: "no-2009"
8 | },
9 | stage: 3
10 | })
11 | ]
12 | };
13 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 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/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/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 |
--------------------------------------------------------------------------------
/readme_images/design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/readme_images/design.png
--------------------------------------------------------------------------------
/spec/models/import_status_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe ImportStatus, type: :model do
4 | pending "add some examples to (or delete) #{__FILE__}"
5 | end
6 |
--------------------------------------------------------------------------------
/spec/models/stat_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Stat, type: :model do
4 | pending "add some examples to (or delete) #{__FILE__}"
5 | end
6 |
--------------------------------------------------------------------------------
/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', __FILE__)
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 | # Checks for pending migrations and applies them before tests are run.
26 | # If you are not using ActiveRecord, you can remove these lines.
27 | begin
28 | ActiveRecord::Migration.maintain_test_schema!
29 | rescue ActiveRecord::PendingMigrationError => e
30 | puts e.to_s.strip
31 | exit 1
32 | end
33 | RSpec.configure do |config|
34 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
35 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
36 |
37 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
38 | # examples within a transaction, remove the following line or assign false
39 | # instead of true.
40 | config.use_transactional_fixtures = true
41 |
42 | # RSpec Rails can automatically mix in different behaviours to your tests
43 | # based on their file location, for example enabling you to call `get` and
44 | # `post` in specs under `spec/controllers`.
45 | #
46 | # You can disable this behaviour by removing the line below, and instead
47 | # explicitly tag your specs with their type, e.g.:
48 | #
49 | # RSpec.describe UsersController, :type => :controller do
50 | # # ...
51 | # end
52 | #
53 | # The different available types are documented in the features, such as in
54 | # https://relishapp.com/rspec/rspec-rails/docs
55 | config.infer_spec_type_from_file_location!
56 |
57 | # Filter lines from Rails gems in backtraces.
58 | config.filter_rails_from_backtrace!
59 | # arbitrary gems may also be filtered via:
60 | # config.filter_gems_from_backtrace("gem name")
61 | end
62 |
--------------------------------------------------------------------------------
/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 | =begin
50 | # This allows you to limit a spec run to individual examples or groups
51 | # you care about by tagging them with `:focus` metadata. When nothing
52 | # is tagged with `:focus`, all examples get run. RSpec also provides
53 | # aliases for `it`, `describe`, and `context` that include `:focus`
54 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55 | config.filter_run_when_matching :focus
56 |
57 | # Allows RSpec to persist some state between runs in order to support
58 | # the `--only-failures` and `--next-failure` CLI options. We recommend
59 | # you configure your source control system to ignore this file.
60 | config.example_status_persistence_file_path = "spec/examples.txt"
61 |
62 | # Limits the available syntax to the non-monkey patched syntax that is
63 | # recommended. For more details, see:
64 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67 | config.disable_monkey_patching!
68 |
69 | # Many RSpec users commonly either run the entire suite or an individual
70 | # file, and it's useful to allow more verbose output when running an
71 | # individual spec file.
72 | if config.files_to_run.one?
73 | # Use the documentation formatter for detailed output,
74 | # unless a formatter has already been configured
75 | # (e.g. via a command-line flag).
76 | config.default_formatter = "doc"
77 | end
78 |
79 | # Print the 10 slowest examples and example groups at the
80 | # end of the spec run, to help surface which specs are running
81 | # particularly slow.
82 | config.profile_examples = 10
83 |
84 | # Run specs in random order to surface order dependencies. If you find an
85 | # order dependency and want to debug it, you can fix the order by providing
86 | # the seed, which is printed after each run.
87 | # --seed 1234
88 | config.order = :random
89 |
90 | # Seed global randomization in this process using the `--seed` CLI option.
91 | # Setting this allows you to use `--seed` to deterministically reproduce
92 | # test failures related to randomization by passing the same `--seed` value
93 | # as the one that triggered the failure.
94 | Kernel.srand config.seed
95 | =end
96 | end
97 |
--------------------------------------------------------------------------------
/spec/support/legacy_stats.json:
--------------------------------------------------------------------------------
1 | {
2 | "2022-01-31":{
3 | "bundler":{
4 | "1.12.5":120,
5 | "1.15.4":5,
6 | "1.16.1":33,
7 | "1.16.2":1,
8 | "1.16.6":8,
9 | "1.17.2":4,
10 | "1.17.3":7,
11 | "1.9.10":1,
12 | "2.1.4":13,
13 | "2.2.15":6,
14 | "2.2.16":2,
15 | "2.2.22":3,
16 | "2.2.33":1,
17 | "2.2.7":1,
18 | "2.3.6":3
19 | },
20 | "ci":{
21 | "ci":3,
22 | "circle,ci":1,
23 | "gitlab,ci":2
24 | },
25 | "gemstash":{
26 |
27 | },
28 | "platform":{
29 | "aarch64-linux":2,
30 | "universal-darwin-20":4,
31 | "universal-java-1.8":1,
32 | "universal-java-11":4,
33 | "x86_64-Oracle Corporation-linux":2,
34 | "x86_64-darwin-19":3,
35 | "x86_64-darwin-21":1,
36 | "x86_64-linux":152,
37 | "x86_64-pc-linux":14,
38 | "x86_64-pc-linux-gnu":185,
39 | "x86_64-w64-mingw32":7
40 | },
41 | "ruby":{
42 | "1.8.7":1,
43 | "1.9.3":4,
44 | "2.0.0":9,
45 | "2.1.10":1,
46 | "2.1.6":7,
47 | "2.3.0":8,
48 | "2.3.1":21,
49 | "2.3.3":12,
50 | "2.3.5":13,
51 | "2.3.6":97,
52 | "2.4.10":3,
53 | "2.4.3":8,
54 | "2.4.4":5,
55 | "2.4.5":11,
56 | "2.4.7":5,
57 | "2.4.9":1,
58 | "2.5.1":12,
59 | "2.5.3":42,
60 | "2.5.5":16,
61 | "2.5.7":14,
62 | "2.5.8":2,
63 | "2.5.9":2,
64 | "2.6.2":1,
65 | "2.6.3":8,
66 | "2.6.4":1,
67 | "2.6.5":17,
68 | "2.6.6":5,
69 | "2.6.7":3,
70 | "2.6.8":3,
71 | "2.7.0":4,
72 | "2.7.1":5,
73 | "2.7.2":11,
74 | "2.7.3":3,
75 | "2.7.4":2,
76 | "2.7.5":9,
77 | "3.0.1":5,
78 | "3.0.2":3,
79 | "3.0.3":1
80 | },
81 | "rubygems":{
82 | "1.3.7":1,
83 | "1.8.23":1,
84 | "2.0.14.1":9,
85 | "2.4.8":7,
86 | "2.5.2":2,
87 | "2.5.2.1":13,
88 | "2.6.10":4,
89 | "2.6.11":3,
90 | "2.6.13":7,
91 | "2.6.14":116,
92 | "2.6.14.4":9,
93 | "2.6.6":11,
94 | "2.6.7":1,
95 | "2.6.8":8,
96 | "2.7.10":1,
97 | "2.7.11":1,
98 | "2.7.6":52,
99 | "2.7.6.2":9,
100 | "2.7.6.3":2,
101 | "2.7.7":2,
102 | "2.7.9":24,
103 | "3.0.3":37,
104 | "3.0.3.1":5,
105 | "3.0.6":4,
106 | "3.0.8":1,
107 | "3.1.2":9,
108 | "3.1.4":10,
109 | "3.1.6":13,
110 | "3.2.15":5,
111 | "3.2.20":1,
112 | "3.2.22":3,
113 | "3.2.29":1,
114 | "3.2.32":1,
115 | "3.2.7":1,
116 | "3.3.6":1
117 | },
118 | "server_region":{
119 |
120 | },
121 | "tls_cipher":{
122 | "":2,
123 | "AES128-GCM":3,
124 | "AES256-GCM":54,
125 | "ECDHE-RSA-AES128-GCM-SHA256":321,
126 | "ECDHE-RSA-AES128-SHA256":1
127 | }
128 | },
129 | "2022-02-01":{
130 | "bundler":{
131 | "1.10.6":10,
132 | "1.11.2":79,
133 | "1.12.5":29054,
134 | "1.13.5":11,
135 | "1.13.6":18,
136 | "1.13.7":41,
137 | "1.14.3":1,
138 | "1.14.4":1,
139 | "1.14.5":1,
140 | "1.14.6":68,
141 | "1.15.0":6,
142 | "1.15.1":61,
143 | "1.15.3":3,
144 | "1.15.4":1890,
145 | "1.16.0":15,
146 | "1.16.1":5638,
147 | "1.16.2":149,
148 | "1.16.3":8,
149 | "1.16.5":39,
150 | "1.16.6":2506,
151 | "1.17.1":16,
152 | "1.17.2":2148,
153 | "1.17.3":2863,
154 | "1.17.3.ol.0.1.0":1,
155 | "1.5.3":5,
156 | "1.7.3":2,
157 | "1.7.4":3,
158 | "1.7.5":1,
159 | "1.9.10":10,
160 | "1.9.4":1,
161 | "2.0.1":16,
162 | "2.0.2":21,
163 | "2.1.1":1,
164 | "2.1.2":17,
165 | "2.1.4":2763,
166 | "2.2.0":2,
167 | "2.2.10":1,
168 | "2.2.11":10,
169 | "2.2.12":1,
170 | "2.2.13":1,
171 | "2.2.15":573,
172 | "2.2.16":33,
173 | "2.2.17":17,
174 | "2.2.18":1,
175 | "2.2.19":8,
176 | "2.2.2":3,
177 | "2.2.20":64,
178 | "2.2.21":2,
179 | "2.2.22":755,
180 | "2.2.23":2,
181 | "2.2.24":14,
182 | "2.2.25":11,
183 | "2.2.26":45,
184 | "2.2.27":57,
185 | "2.2.28":37,
186 | "2.2.29":48,
187 | "2.2.3":15,
188 | "2.2.30":19,
189 | "2.2.31":116,
190 | "2.2.32":408,
191 | "2.2.33":389,
192 | "2.2.4":3,
193 | "2.2.5":4,
194 | "2.2.6":5,
195 | "2.2.7":2,
196 | "2.2.8":34,
197 | "2.2.9":10,
198 | "2.3.0":3,
199 | "2.3.1":1,
200 | "2.3.2":2,
201 | "2.3.3":23,
202 | "2.3.4":52,
203 | "2.3.5":33,
204 | "2.3.6":204,
205 | "2.4.0.dev":2
206 | },
207 | "ci":{
208 | "aws-code-build,ci":3,
209 | "ci":479,
210 | "circle,ci":206,
211 | "circle-ci":1,
212 | "gitlab,ci":47,
213 | "jenkins":72,
214 | "jenkins,ci":84,
215 | "semaphore,ci":24,
216 | "travis,ci":10
217 | },
218 | "gemstash":{
219 |
220 | },
221 | "platform":{
222 | "aarch64-apple-darwin21.2.0":1,
223 | "aarch64-linux":305,
224 | "aarch64-linux-android":2,
225 | "aarch64-unknown-linux":16,
226 | "aarch64-unknown-linux-gnu":135,
227 | "aarch64-unknown-linux-musl":3,
228 | "amd64-freebsd-12":1,
229 | "arm-apple-darwin20.2.0":1,
230 | "arm-apple-darwin20.6.0":3,
231 | "arm-apple-darwin21.2.0":2,
232 | "arm-apple-darwin21.3.0":1,
233 | "arm-linux":11,
234 | "arm-linux-androideabi":1,
235 | "arm64-apple-darwin20":27,
236 | "arm64-apple-darwin21":16,
237 | "arm64-darwin-20":5,
238 | "arm64-darwin-21":11,
239 | "armv7l-linux":3,
240 | "armv7l-linux-eabihf":4,
241 | "armv7l-unknown-linux-eabihf":2,
242 | "armv7l-unknown-linux-gnueabihf":3,
243 | "i686-pc-linux-gnu":1,
244 | "i686-w64-mingw32":14,
245 | "powerpc-ibm-aix6.1.0.0":8,
246 | "powerpc-ibm-aix7.1.5.0":5,
247 | "powerpc64le-unknown-linux":1,
248 | "sparc-sun-solaris2.11":9,
249 | "universal-darwin-16":6,
250 | "universal-darwin-17":15,
251 | "universal-darwin-18":1,
252 | "universal-darwin-19":52,
253 | "universal-darwin-20":325,
254 | "universal-darwin-21":44,
255 | "universal-java-1.8":167,
256 | "universal-java-11":751,
257 | "x64-mingw32":358,
258 | "x64-w64-mingw32":252,
259 | "x86-linux":6,
260 | "x86-mingw32":258,
261 | "x86-solaris-2.10":6,
262 | "x86_64-AdoptOpenJDK-linux":42,
263 | "x86_64-Amazon.com Inc.-linux":1,
264 | "x86_64-Azul Systems, Inc.-linux":4,
265 | "x86_64-Eclipse Adoptium-linux":17,
266 | "x86_64-Oracle Corporation-linux":37,
267 | "x86_64-Private Build-linux":3,
268 | "x86_64-Red Hat, Inc.-linux":8,
269 | "x86_64-Temurin-linux":3,
270 | "x86_64-Ubuntu-linux":10,
271 | "x86_64-alpine-linux-musl":17,
272 | "x86_64-apple-darwin13.4.0":1,
273 | "x86_64-apple-darwin14.5.0":12,
274 | "x86_64-apple-darwin15.6.0":8,
275 | "x86_64-apple-darwin16.7.0":5,
276 | "x86_64-apple-darwin17.7.0":5,
277 | "x86_64-apple-darwin18":3,
278 | "x86_64-apple-darwin18.6.0":5,
279 | "x86_64-apple-darwin18.7.0":2,
280 | "x86_64-apple-darwin19":65,
281 | "x86_64-apple-darwin19.3.0":1,
282 | "x86_64-apple-darwin19.4.0":2,
283 | "x86_64-apple-darwin19.5.0":1,
284 | "x86_64-apple-darwin19.6.0":18,
285 | "x86_64-apple-darwin20":69,
286 | "x86_64-apple-darwin20.1.0":1,
287 | "x86_64-apple-darwin20.5.0":3,
288 | "x86_64-apple-darwin20.6.0":31,
289 | "x86_64-apple-darwin21":9,
290 | "x86_64-apple-darwin21.1.0":9,
291 | "x86_64-apple-darwin21.2.0":5,
292 | "x86_64-apple-darwin21.3.0":1,
293 | "x86_64-cygwin":5,
294 | "x86_64-darwin-13":15,
295 | "x86_64-darwin-14":4,
296 | "x86_64-darwin-15":10,
297 | "x86_64-darwin-17":1,
298 | "x86_64-darwin-18":16,
299 | "x86_64-darwin-19":164,
300 | "x86_64-darwin-20":109,
301 | "x86_64-darwin-21":21,
302 | "x86_64-koji-linux-gnu":15,
303 | "x86_64-linux":29383,
304 | "x86_64-linux-musl":57,
305 | "x86_64-pc-linux":2471,
306 | "x86_64-pc-linux-gnu":43658,
307 | "x86_64-pc-linux-musl":61,
308 | "x86_64-redhat-linux":4,
309 | "x86_64-redhat-linux-gnu":6,
310 | "x86_64-solaris-2.11":4,
311 | "x86_64-unknown-freebsd10.3":43,
312 | "x86_64-unknown-freebsd11.3":1,
313 | "x86_64-unknown-freebsd11.4":2,
314 | "x86_64-unknown-linux":1,
315 | "x86_64-unknown-linux-gnu":89,
316 | "x86_64-w64-mingw32":3228
317 | },
318 | "ruby":{
319 | "1.8.7":52,
320 | "1.9.3":379,
321 | "2.0.0":1456,
322 | "2.1.0":4,
323 | "2.1.1":2,
324 | "2.1.10":266,
325 | "2.1.2":4,
326 | "2.1.3":11,
327 | "2.1.4":52,
328 | "2.1.5":34,
329 | "2.1.6":1254,
330 | "2.1.7":2,
331 | "2.1.8":292,
332 | "2.1.9":140,
333 | "2.2.0":21,
334 | "2.2.10":15,
335 | "2.2.2":50,
336 | "2.2.3":18,
337 | "2.2.4":46,
338 | "2.2.5":2,
339 | "2.2.6":13,
340 | "2.2.7":39,
341 | "2.2.8":1,
342 | "2.2.9":3,
343 | "2.3.0":972,
344 | "2.3.1":7730,
345 | "2.3.3":2588,
346 | "2.3.4":118,
347 | "2.3.5":3484,
348 | "2.3.6":21210,
349 | "2.3.7":72,
350 | "2.3.8":199,
351 | "2.4.0":25,
352 | "2.4.1":204,
353 | "2.4.10":398,
354 | "2.4.2":736,
355 | "2.4.3":1045,
356 | "2.4.4":926,
357 | "2.4.5":1503,
358 | "2.4.6":11,
359 | "2.4.7":1023,
360 | "2.4.9":85,
361 | "2.5.0":52,
362 | "2.5.1":3621,
363 | "2.5.2":2,
364 | "2.5.3":6836,
365 | "2.5.4":9,
366 | "2.5.5":2378,
367 | "2.5.7":2390,
368 | "2.5.8":1072,
369 | "2.5.9":264,
370 | "2.6.0":16,
371 | "2.6.1":236,
372 | "2.6.2":59,
373 | "2.6.3":1524,
374 | "2.6.4":96,
375 | "2.6.5":5671,
376 | "2.6.6":1358,
377 | "2.6.7":651,
378 | "2.6.8":844,
379 | "2.6.9":179,
380 | "2.7.0":518,
381 | "2.7.1":1082,
382 | "2.7.2":1827,
383 | "2.7.3":788,
384 | "2.7.4":845,
385 | "2.7.5":2089,
386 | "3.0.0":18,
387 | "3.0.1":560,
388 | "3.0.2":768,
389 | "3.0.3":308,
390 | "3.1.0":50,
391 | "3.2.0":2
392 | },
393 | "rubygems":{
394 | "1.3.7":46,
395 | "1.6.2":2,
396 | "1.8.11":3,
397 | "1.8.23":85,
398 | "1.8.23.2":1,
399 | "1.8.24":15,
400 | "1.8.28":1,
401 | "1.8.29":17,
402 | "1.8.30":1,
403 | "2.0.14":27,
404 | "2.0.14.1":1280,
405 | "2.1.11":36,
406 | "2.2.0":1,
407 | "2.2.2":104,
408 | "2.2.5":86,
409 | "2.4.3":2,
410 | "2.4.4":241,
411 | "2.4.5":3,
412 | "2.4.5.1":41,
413 | "2.4.5.2":1,
414 | "2.4.5.3":3,
415 | "2.4.5.4":3,
416 | "2.4.5.5":6,
417 | "2.4.7":45,
418 | "2.4.8":954,
419 | "2.4.8.1.acquia":9,
420 | "2.5.1":248,
421 | "2.5.2":473,
422 | "2.5.2.1":2666,
423 | "2.5.2.2":2,
424 | "2.5.2.3":140,
425 | "2.6.0":1,
426 | "2.6.10":1300,
427 | "2.6.11":732,
428 | "2.6.12":188,
429 | "2.6.13":2844,
430 | "2.6.14":24933,
431 | "2.6.14.1":37,
432 | "2.6.14.3":4,
433 | "2.6.14.4":1248,
434 | "2.6.2":1,
435 | "2.6.3":46,
436 | "2.6.4":83,
437 | "2.6.6":1454,
438 | "2.6.7":622,
439 | "2.6.8":4030,
440 | "2.7.0":202,
441 | "2.7.10":106,
442 | "2.7.11":197,
443 | "2.7.3":222,
444 | "2.7.4":4,
445 | "2.7.5":2,
446 | "2.7.6":9481,
447 | "2.7.6.1":1,
448 | "2.7.6.2":1341,
449 | "2.7.6.3":211,
450 | "2.7.7":267,
451 | "2.7.8":499,
452 | "2.7.9":5552,
453 | "3.0.0":86,
454 | "3.0.1":56,
455 | "3.0.2":9,
456 | "3.0.3":7521,
457 | "3.0.3.1":1172,
458 | "3.0.4":715,
459 | "3.0.6":793,
460 | "3.0.8":335,
461 | "3.0.9":78,
462 | "3.1.1":1,
463 | "3.1.2":1573,
464 | "3.1.3":144,
465 | "3.1.4":1831,
466 | "3.1.6":3173,
467 | "3.2.11":11,
468 | "3.2.12":1,
469 | "3.2.15":565,
470 | "3.2.16":16,
471 | "3.2.17":13,
472 | "3.2.18":1,
473 | "3.2.19":1,
474 | "3.2.2":15,
475 | "3.2.20":441,
476 | "3.2.21":1,
477 | "3.2.22":777,
478 | "3.2.24":5,
479 | "3.2.25":2,
480 | "3.2.26":4,
481 | "3.2.27":11,
482 | "3.2.28":9,
483 | "3.2.29":6,
484 | "3.2.3":21,
485 | "3.2.30":10,
486 | "3.2.31":100,
487 | "3.2.32":495,
488 | "3.2.33":20,
489 | "3.2.4":1,
490 | "3.2.5":51,
491 | "3.2.6":1,
492 | "3.2.7":9,
493 | "3.2.8":29,
494 | "3.3.0":2,
495 | "3.3.1":1,
496 | "3.3.3":47,
497 | "3.3.4":63,
498 | "3.3.5":53,
499 | "3.3.6":177,
500 | "3.4.0.dev":2
501 | },
502 | "server_region":{
503 |
504 | },
505 | "tls_cipher":{
506 | "":464,
507 | "AES128-GCM":1625,
508 | "AES256-GCM":10072,
509 | "ECDHE-RSA-AES128-GCM-SHA256":72325,
510 | "ECDHE-RSA-AES128-SHA":101,
511 | "ECDHE-RSA-AES128-SHA256":93
512 | }
513 | }
514 | }
--------------------------------------------------------------------------------
/spec/support/new_stats.json:
--------------------------------------------------------------------------------
1 | {
2 | "2022-01-31": {
3 | "bundler": {
4 | "1.12.5": {
5 | "total": 120,
6 | "unique": 53
7 | },
8 | "1.15.4": {
9 | "total": 5,
10 | "unique": 2
11 | },
12 | "1.16.1": {
13 | "total": 33,
14 | "unique": 4
15 | },
16 | "1.16.2": {
17 | "total": 1,
18 | "unique": 1
19 | },
20 | "1.16.6": {
21 | "total": 8,
22 | "unique": 4
23 | },
24 | "1.17.2": {
25 | "total": 4,
26 | "unique": 1
27 | },
28 | "1.17.3": {
29 | "total": 7,
30 | "unique": 3
31 | },
32 | "1.9.10": {
33 | "total": 1,
34 | "unique": 1
35 | },
36 | "2.1.4": {
37 | "total": 13,
38 | "unique": 5
39 | },
40 | "2.2.15": {
41 | "total": 6,
42 | "unique": 2
43 | },
44 | "2.2.16": {
45 | "total": 2,
46 | "unique": 1
47 | },
48 | "2.2.22": {
49 | "total": 3,
50 | "unique": 1
51 | },
52 | "2.2.33": {
53 | "total": 1,
54 | "unique": 1
55 | },
56 | "2.2.7": {
57 | "total": 1,
58 | "unique": 1
59 | },
60 | "2.3.6": {
61 | "total": 3,
62 | "unique": 1
63 | }
64 | },
65 | "ci": {
66 | "ci": {
67 | "total": 3,
68 | "unique": 1
69 | },
70 | "circle,ci": {
71 | "total": 1,
72 | "unique": 1
73 | },
74 | "gitlab,ci": {
75 | "total": 2,
76 | "unique": 1
77 | }
78 | },
79 | "gemstash": {
80 | },
81 | "platform": {
82 | "aarch64-linux": {
83 | "total": 2,
84 | "unique": 1
85 | },
86 | "universal-darwin-20": {
87 | "total": 4,
88 | "unique": 2
89 | },
90 | "universal-java-1.8": {
91 | "total": 1,
92 | "unique": 1
93 | },
94 | "universal-java-11": {
95 | "total": 4,
96 | "unique": 2
97 | },
98 | "x86_64-Oracle Corporation-linux": {
99 | "total": 2,
100 | "unique": 1
101 | },
102 | "x86_64-darwin-19": {
103 | "total": 3,
104 | "unique": 1
105 | },
106 | "x86_64-darwin-21": {
107 | "total": 1,
108 | "unique": 1
109 | },
110 | "x86_64-linux": {
111 | "total": 152,
112 | "unique": 28
113 | },
114 | "x86_64-pc-linux": {
115 | "total": 14,
116 | "unique": 6
117 | },
118 | "x86_64-pc-linux-gnu": {
119 | "total": 185,
120 | "unique": 33
121 | },
122 | "x86_64-w64-mingw32": {
123 | "total": 7,
124 | "unique": 3
125 | }
126 | },
127 | "ruby": {
128 | "1.8.7": {
129 | "total": 1,
130 | "unique": 1
131 | },
132 | "1.9.3": {
133 | "total": 4,
134 | "unique": 2
135 | },
136 | "2.0.0": {
137 | "total": 9,
138 | "unique": 3
139 | },
140 | "2.1.10": {
141 | "total": 1,
142 | "unique": 1
143 | },
144 | "2.1.6": {
145 | "total": 7,
146 | "unique": 2
147 | },
148 | "2.3.0": {
149 | "total": 8,
150 | "unique": 2
151 | },
152 | "2.3.1": {
153 | "total": 21,
154 | "unique": 3
155 | },
156 | "2.3.3": {
157 | "total": 12,
158 | "unique": 4
159 | },
160 | "2.3.5": {
161 | "total": 13,
162 | "unique": 7
163 | },
164 | "2.3.6": {
165 | "total": 97,
166 | "unique": 16
167 | },
168 | "2.4.10": {
169 | "total": 3,
170 | "unique": 1
171 | },
172 | "2.4.3": {
173 | "total": 8,
174 | "unique": 3
175 | },
176 | "2.4.4": {
177 | "total": 5,
178 | "unique": 2
179 | },
180 | "2.4.5": {
181 | "total": 11,
182 | "unique": 2
183 | },
184 | "2.4.7": {
185 | "total": 5,
186 | "unique": 3
187 | },
188 | "2.4.9": {
189 | "total": 1,
190 | "unique": 1
191 | },
192 | "2.5.1": {
193 | "total": 12,
194 | "unique": 5
195 | },
196 | "2.5.3": {
197 | "total": 42,
198 | "unique": 12
199 | },
200 | "2.5.5": {
201 | "total": 16,
202 | "unique": 7
203 | },
204 | "2.5.7": {
205 | "total": 14,
206 | "unique": 5
207 | },
208 | "2.5.8": {
209 | "total": 2,
210 | "unique": 1
211 | },
212 | "2.5.9": {
213 | "total": 2,
214 | "unique": 1
215 | },
216 | "2.6.2": {
217 | "total": 1,
218 | "unique": 1
219 | },
220 | "2.6.3": {
221 | "total": 8,
222 | "unique": 2
223 | },
224 | "2.6.4": {
225 | "total": 1,
226 | "unique": 1
227 | },
228 | "2.6.5": {
229 | "total": 17,
230 | "unique": 7
231 | },
232 | "2.6.6": {
233 | "total": 5,
234 | "unique": 3
235 | },
236 | "2.6.7": {
237 | "total": 3,
238 | "unique": 1
239 | },
240 | "2.6.8": {
241 | "total": 3,
242 | "unique": 2
243 | },
244 | "2.7.0": {
245 | "total": 4,
246 | "unique": 1
247 | },
248 | "2.7.1": {
249 | "total": 5,
250 | "unique": 1
251 | },
252 | "2.7.2": {
253 | "total": 11,
254 | "unique": 4
255 | },
256 | "2.7.3": {
257 | "total": 3,
258 | "unique": 2
259 | },
260 | "2.7.4": {
261 | "total": 2,
262 | "unique": 1
263 | },
264 | "2.7.5": {
265 | "total": 9,
266 | "unique": 1
267 | },
268 | "3.0.1": {
269 | "total": 5,
270 | "unique": 2
271 | },
272 | "3.0.2": {
273 | "total": 3,
274 | "unique": 2
275 | },
276 | "3.0.3": {
277 | "total": 1,
278 | "unique": 1
279 | }
280 | },
281 | "rubygems": {
282 | "1.3.7": {
283 | "total": 1,
284 | "unique": 1
285 | },
286 | "1.8.23": {
287 | "total": 1,
288 | "unique": 1
289 | },
290 | "2.0.14.1": {
291 | "total": 9,
292 | "unique": 2
293 | },
294 | "2.4.8": {
295 | "total": 7,
296 | "unique": 3
297 | },
298 | "2.5.2": {
299 | "total": 2,
300 | "unique": 1
301 | },
302 | "2.5.2.1": {
303 | "total": 13,
304 | "unique": 4
305 | },
306 | "2.6.10": {
307 | "total": 4,
308 | "unique": 2
309 | },
310 | "2.6.11": {
311 | "total": 3,
312 | "unique": 1
313 | },
314 | "2.6.13": {
315 | "total": 7,
316 | "unique": 3
317 | },
318 | "2.6.14": {
319 | "total": 116,
320 | "unique": 37
321 | },
322 | "2.6.14.4": {
323 | "total": 9,
324 | "unique": 4
325 | },
326 | "2.6.6": {
327 | "total": 11,
328 | "unique": 5
329 | },
330 | "2.6.7": {
331 | "total": 1,
332 | "unique": 1
333 | },
334 | "2.6.8": {
335 | "total": 8,
336 | "unique": 3
337 | },
338 | "2.7.10": {
339 | "total": 1,
340 | "unique": 1
341 | },
342 | "2.7.11": {
343 | "total": 1,
344 | "unique": 1
345 | },
346 | "2.7.6": {
347 | "total": 52,
348 | "unique": 13
349 | },
350 | "2.7.6.2": {
351 | "total": 9,
352 | "unique": 4
353 | },
354 | "2.7.6.3": {
355 | "total": 2,
356 | "unique": 1
357 | },
358 | "2.7.7": {
359 | "total": 2,
360 | "unique": 1
361 | },
362 | "2.7.9": {
363 | "total": 24,
364 | "unique": 3
365 | },
366 | "3.0.3": {
367 | "total": 37,
368 | "unique": 13
369 | },
370 | "3.0.3.1": {
371 | "total": 5,
372 | "unique": 2
373 | },
374 | "3.0.6": {
375 | "total": 4,
376 | "unique": 1
377 | },
378 | "3.0.8": {
379 | "total": 1,
380 | "unique": 1
381 | },
382 | "3.1.2": {
383 | "total": 9,
384 | "unique": 4
385 | },
386 | "3.1.4": {
387 | "total": 10,
388 | "unique": 5
389 | },
390 | "3.1.6": {
391 | "total": 13,
392 | "unique": 5
393 | },
394 | "3.2.15": {
395 | "total": 5,
396 | "unique": 2
397 | },
398 | "3.2.20": {
399 | "total": 1,
400 | "unique": 1
401 | },
402 | "3.2.22": {
403 | "total": 3,
404 | "unique": 1
405 | },
406 | "3.2.29": {
407 | "total": 1,
408 | "unique": 1
409 | },
410 | "3.2.32": {
411 | "total": 1,
412 | "unique": 1
413 | },
414 | "3.2.7": {
415 | "total": 1,
416 | "unique": 1
417 | },
418 | "3.3.6": {
419 | "total": 1,
420 | "unique": 1
421 | }
422 | },
423 | "server_region": {
424 | },
425 | "tls_cipher": {
426 | "": {
427 | "total": 2,
428 | "unique": 1
429 | },
430 | "AES128-GCM": {
431 | "total": 3,
432 | "unique": 1
433 | },
434 | "AES256-GCM": {
435 | "total": 54,
436 | "unique": 9
437 | },
438 | "ECDHE-RSA-AES128-GCM-SHA256": {
439 | "total": 321,
440 | "unique": 142
441 | },
442 | "ECDHE-RSA-AES128-SHA256": {
443 | "total": 1,
444 | "unique": 1
445 | }
446 | }
447 | },
448 | "2022-02-01": {
449 | "bundler": {
450 | "1.10.6": {
451 | "total": 10,
452 | "unique": 4
453 | },
454 | "1.11.2": {
455 | "total": 79,
456 | "unique": 14
457 | },
458 | "1.12.5": {
459 | "total": 29054,
460 | "unique": 5240
461 | },
462 | "1.13.5": {
463 | "total": 11,
464 | "unique": 4
465 | },
466 | "1.13.6": {
467 | "total": 18,
468 | "unique": 7
469 | },
470 | "1.13.7": {
471 | "total": 41,
472 | "unique": 6
473 | },
474 | "1.14.3": {
475 | "total": 1,
476 | "unique": 1
477 | },
478 | "1.14.4": {
479 | "total": 1,
480 | "unique": 1
481 | },
482 | "1.14.5": {
483 | "total": 1,
484 | "unique": 1
485 | },
486 | "1.14.6": {
487 | "total": 68,
488 | "unique": 10
489 | },
490 | "1.15.0": {
491 | "total": 6,
492 | "unique": 3
493 | },
494 | "1.15.1": {
495 | "total": 61,
496 | "unique": 20
497 | },
498 | "1.15.3": {
499 | "total": 3,
500 | "unique": 1
501 | },
502 | "1.15.4": {
503 | "total": 1890,
504 | "unique": 413
505 | },
506 | "1.16.0": {
507 | "total": 15,
508 | "unique": 6
509 | },
510 | "1.16.1": {
511 | "total": 5638,
512 | "unique": 1911
513 | },
514 | "1.16.2": {
515 | "total": 149,
516 | "unique": 72
517 | },
518 | "1.16.3": {
519 | "total": 8,
520 | "unique": 4
521 | },
522 | "1.16.5": {
523 | "total": 39,
524 | "unique": 19
525 | },
526 | "1.16.6": {
527 | "total": 2506,
528 | "unique": 773
529 | },
530 | "1.17.1": {
531 | "total": 16,
532 | "unique": 3
533 | },
534 | "1.17.2": {
535 | "total": 2148,
536 | "unique": 948
537 | },
538 | "1.17.3": {
539 | "total": 2863,
540 | "unique": 1337
541 | },
542 | "1.17.3.ol.0.1.0": {
543 | "total": 1,
544 | "unique": 1
545 | },
546 | "1.5.3": {
547 | "total": 5,
548 | "unique": 1
549 | },
550 | "1.7.3": {
551 | "total": 2,
552 | "unique": 1
553 | },
554 | "1.7.4": {
555 | "total": 3,
556 | "unique": 2
557 | },
558 | "1.7.5": {
559 | "total": 1,
560 | "unique": 1
561 | },
562 | "1.9.10": {
563 | "total": 10,
564 | "unique": 2
565 | },
566 | "1.9.4": {
567 | "total": 1,
568 | "unique": 1
569 | },
570 | "2.0.1": {
571 | "total": 16,
572 | "unique": 6
573 | },
574 | "2.0.2": {
575 | "total": 21,
576 | "unique": 11
577 | },
578 | "2.1.1": {
579 | "total": 1,
580 | "unique": 1
581 | },
582 | "2.1.2": {
583 | "total": 17,
584 | "unique": 9
585 | },
586 | "2.1.4": {
587 | "total": 2763,
588 | "unique": 367
589 | },
590 | "2.2.0": {
591 | "total": 2,
592 | "unique": 1
593 | },
594 | "2.2.10": {
595 | "total": 1,
596 | "unique": 1
597 | },
598 | "2.2.11": {
599 | "total": 10,
600 | "unique": 3
601 | },
602 | "2.2.12": {
603 | "total": 1,
604 | "unique": 1
605 | },
606 | "2.2.13": {
607 | "total": 1,
608 | "unique": 1
609 | },
610 | "2.2.15": {
611 | "total": 573,
612 | "unique": 209
613 | },
614 | "2.2.16": {
615 | "total": 33,
616 | "unique": 15
617 | },
618 | "2.2.17": {
619 | "total": 17,
620 | "unique": 9
621 | },
622 | "2.2.18": {
623 | "total": 1,
624 | "unique": 1
625 | },
626 | "2.2.19": {
627 | "total": 8,
628 | "unique": 4
629 | },
630 | "2.2.2": {
631 | "total": 3,
632 | "unique": 1
633 | },
634 | "2.2.20": {
635 | "total": 64,
636 | "unique": 13
637 | },
638 | "2.2.21": {
639 | "total": 2,
640 | "unique": 1
641 | },
642 | "2.2.22": {
643 | "total": 755,
644 | "unique": 101
645 | },
646 | "2.2.23": {
647 | "total": 2,
648 | "unique": 1
649 | },
650 | "2.2.24": {
651 | "total": 14,
652 | "unique": 4
653 | },
654 | "2.2.25": {
655 | "total": 11,
656 | "unique": 4
657 | },
658 | "2.2.26": {
659 | "total": 45,
660 | "unique": 11
661 | },
662 | "2.2.27": {
663 | "total": 57,
664 | "unique": 24
665 | },
666 | "2.2.28": {
667 | "total": 37,
668 | "unique": 8
669 | },
670 | "2.2.29": {
671 | "total": 48,
672 | "unique": 22
673 | },
674 | "2.2.3": {
675 | "total": 15,
676 | "unique": 3
677 | },
678 | "2.2.30": {
679 | "total": 19,
680 | "unique": 8
681 | },
682 | "2.2.31": {
683 | "total": 116,
684 | "unique": 19
685 | },
686 | "2.2.32": {
687 | "total": 408,
688 | "unique": 193
689 | },
690 | "2.2.33": {
691 | "total": 389,
692 | "unique": 167
693 | },
694 | "2.2.4": {
695 | "total": 3,
696 | "unique": 2
697 | },
698 | "2.2.5": {
699 | "total": 4,
700 | "unique": 2
701 | },
702 | "2.2.6": {
703 | "total": 5,
704 | "unique": 2
705 | },
706 | "2.2.7": {
707 | "total": 2,
708 | "unique": 1
709 | },
710 | "2.2.8": {
711 | "total": 34,
712 | "unique": 14
713 | },
714 | "2.2.9": {
715 | "total": 10,
716 | "unique": 2
717 | },
718 | "2.3.0": {
719 | "total": 3,
720 | "unique": 1
721 | },
722 | "2.3.1": {
723 | "total": 1,
724 | "unique": 1
725 | },
726 | "2.3.2": {
727 | "total": 2,
728 | "unique": 1
729 | },
730 | "2.3.3": {
731 | "total": 23,
732 | "unique": 6
733 | },
734 | "2.3.4": {
735 | "total": 52,
736 | "unique": 26
737 | },
738 | "2.3.5": {
739 | "total": 33,
740 | "unique": 14
741 | },
742 | "2.3.6": {
743 | "total": 204,
744 | "unique": 72
745 | },
746 | "2.4.0.dev": {
747 | "total": 2,
748 | "unique": 1
749 | }
750 | },
751 | "ci": {
752 | "aws-code-build,ci": {
753 | "total": 3,
754 | "unique": 1
755 | },
756 | "ci": {
757 | "total": 479,
758 | "unique": 152
759 | },
760 | "circle,ci": {
761 | "total": 206,
762 | "unique": 67
763 | },
764 | "circle-ci": {
765 | "total": 1,
766 | "unique": 1
767 | },
768 | "gitlab,ci": {
769 | "total": 47,
770 | "unique": 8
771 | },
772 | "jenkins": {
773 | "total": 72,
774 | "unique": 36
775 | },
776 | "jenkins,ci": {
777 | "total": 84,
778 | "unique": 36
779 | },
780 | "semaphore,ci": {
781 | "total": 24,
782 | "unique": 5
783 | },
784 | "travis,ci": {
785 | "total": 10,
786 | "unique": 3
787 | }
788 | },
789 | "gemstash": {
790 | },
791 | "platform": {
792 | "aarch64-apple-darwin21.2.0": {
793 | "total": 1,
794 | "unique": 1
795 | },
796 | "aarch64-linux": {
797 | "total": 305,
798 | "unique": 124
799 | },
800 | "aarch64-linux-android": {
801 | "total": 2,
802 | "unique": 1
803 | },
804 | "aarch64-unknown-linux": {
805 | "total": 16,
806 | "unique": 4
807 | },
808 | "aarch64-unknown-linux-gnu": {
809 | "total": 135,
810 | "unique": 16
811 | },
812 | "aarch64-unknown-linux-musl": {
813 | "total": 3,
814 | "unique": 2
815 | },
816 | "amd64-freebsd-12": {
817 | "total": 1,
818 | "unique": 1
819 | },
820 | "arm-apple-darwin20.2.0": {
821 | "total": 1,
822 | "unique": 1
823 | },
824 | "arm-apple-darwin20.6.0": {
825 | "total": 3,
826 | "unique": 2
827 | },
828 | "arm-apple-darwin21.2.0": {
829 | "total": 2,
830 | "unique": 1
831 | },
832 | "arm-apple-darwin21.3.0": {
833 | "total": 1,
834 | "unique": 1
835 | },
836 | "arm-linux": {
837 | "total": 11,
838 | "unique": 2
839 | },
840 | "arm-linux-androideabi": {
841 | "total": 1,
842 | "unique": 1
843 | },
844 | "arm64-apple-darwin20": {
845 | "total": 27,
846 | "unique": 10
847 | },
848 | "arm64-apple-darwin21": {
849 | "total": 16,
850 | "unique": 3
851 | },
852 | "arm64-darwin-20": {
853 | "total": 5,
854 | "unique": 3
855 | },
856 | "arm64-darwin-21": {
857 | "total": 11,
858 | "unique": 3
859 | },
860 | "armv7l-linux": {
861 | "total": 3,
862 | "unique": 2
863 | },
864 | "armv7l-linux-eabihf": {
865 | "total": 4,
866 | "unique": 2
867 | },
868 | "armv7l-unknown-linux-eabihf": {
869 | "total": 2,
870 | "unique": 1
871 | },
872 | "armv7l-unknown-linux-gnueabihf": {
873 | "total": 3,
874 | "unique": 2
875 | },
876 | "i686-pc-linux-gnu": {
877 | "total": 1,
878 | "unique": 1
879 | },
880 | "i686-w64-mingw32": {
881 | "total": 14,
882 | "unique": 2
883 | },
884 | "powerpc-ibm-aix6.1.0.0": {
885 | "total": 8,
886 | "unique": 2
887 | },
888 | "powerpc-ibm-aix7.1.5.0": {
889 | "total": 5,
890 | "unique": 1
891 | },
892 | "powerpc64le-unknown-linux": {
893 | "total": 1,
894 | "unique": 1
895 | },
896 | "sparc-sun-solaris2.11": {
897 | "total": 9,
898 | "unique": 4
899 | },
900 | "universal-darwin-16": {
901 | "total": 6,
902 | "unique": 2
903 | },
904 | "universal-darwin-17": {
905 | "total": 15,
906 | "unique": 6
907 | },
908 | "universal-darwin-18": {
909 | "total": 1,
910 | "unique": 1
911 | },
912 | "universal-darwin-19": {
913 | "total": 52,
914 | "unique": 10
915 | },
916 | "universal-darwin-20": {
917 | "total": 325,
918 | "unique": 66
919 | },
920 | "universal-darwin-21": {
921 | "total": 44,
922 | "unique": 11
923 | },
924 | "universal-java-1.8": {
925 | "total": 167,
926 | "unique": 67
927 | },
928 | "universal-java-11": {
929 | "total": 751,
930 | "unique": 227
931 | },
932 | "x64-mingw32": {
933 | "total": 358,
934 | "unique": 78
935 | },
936 | "x64-w64-mingw32": {
937 | "total": 252,
938 | "unique": 102
939 | },
940 | "x86-linux": {
941 | "total": 6,
942 | "unique": 2
943 | },
944 | "x86-mingw32": {
945 | "total": 258,
946 | "unique": 106
947 | },
948 | "x86-solaris-2.10": {
949 | "total": 6,
950 | "unique": 3
951 | },
952 | "x86_64-AdoptOpenJDK-linux": {
953 | "total": 42,
954 | "unique": 13
955 | },
956 | "x86_64-Amazon.com Inc.-linux": {
957 | "total": 1,
958 | "unique": 1
959 | },
960 | "x86_64-Azul Systems, Inc.-linux": {
961 | "total": 4,
962 | "unique": 2
963 | },
964 | "x86_64-Eclipse Adoptium-linux": {
965 | "total": 17,
966 | "unique": 9
967 | },
968 | "x86_64-Oracle Corporation-linux": {
969 | "total": 37,
970 | "unique": 9
971 | },
972 | "x86_64-Private Build-linux": {
973 | "total": 3,
974 | "unique": 1
975 | },
976 | "x86_64-Red Hat, Inc.-linux": {
977 | "total": 8,
978 | "unique": 4
979 | },
980 | "x86_64-Temurin-linux": {
981 | "total": 3,
982 | "unique": 1
983 | },
984 | "x86_64-Ubuntu-linux": {
985 | "total": 10,
986 | "unique": 3
987 | },
988 | "x86_64-alpine-linux-musl": {
989 | "total": 17,
990 | "unique": 4
991 | },
992 | "x86_64-apple-darwin13.4.0": {
993 | "total": 1,
994 | "unique": 1
995 | },
996 | "x86_64-apple-darwin14.5.0": {
997 | "total": 12,
998 | "unique": 2
999 | },
1000 | "x86_64-apple-darwin15.6.0": {
1001 | "total": 8,
1002 | "unique": 3
1003 | },
1004 | "x86_64-apple-darwin16.7.0": {
1005 | "total": 5,
1006 | "unique": 3
1007 | },
1008 | "x86_64-apple-darwin17.7.0": {
1009 | "total": 5,
1010 | "unique": 1
1011 | },
1012 | "x86_64-apple-darwin18": {
1013 | "total": 3,
1014 | "unique": 2
1015 | },
1016 | "x86_64-apple-darwin18.6.0": {
1017 | "total": 5,
1018 | "unique": 2
1019 | },
1020 | "x86_64-apple-darwin18.7.0": {
1021 | "total": 2,
1022 | "unique": 1
1023 | },
1024 | "x86_64-apple-darwin19": {
1025 | "total": 65,
1026 | "unique": 15
1027 | },
1028 | "x86_64-apple-darwin19.3.0": {
1029 | "total": 1,
1030 | "unique": 1
1031 | },
1032 | "x86_64-apple-darwin19.4.0": {
1033 | "total": 2,
1034 | "unique": 1
1035 | },
1036 | "x86_64-apple-darwin19.5.0": {
1037 | "total": 1,
1038 | "unique": 1
1039 | },
1040 | "x86_64-apple-darwin19.6.0": {
1041 | "total": 18,
1042 | "unique": 4
1043 | },
1044 | "x86_64-apple-darwin20": {
1045 | "total": 69,
1046 | "unique": 25
1047 | },
1048 | "x86_64-apple-darwin20.1.0": {
1049 | "total": 1,
1050 | "unique": 1
1051 | },
1052 | "x86_64-apple-darwin20.5.0": {
1053 | "total": 3,
1054 | "unique": 1
1055 | },
1056 | "x86_64-apple-darwin20.6.0": {
1057 | "total": 31,
1058 | "unique": 15
1059 | },
1060 | "x86_64-apple-darwin21": {
1061 | "total": 9,
1062 | "unique": 5
1063 | },
1064 | "x86_64-apple-darwin21.1.0": {
1065 | "total": 9,
1066 | "unique": 2
1067 | },
1068 | "x86_64-apple-darwin21.2.0": {
1069 | "total": 5,
1070 | "unique": 2
1071 | },
1072 | "x86_64-apple-darwin21.3.0": {
1073 | "total": 1,
1074 | "unique": 1
1075 | },
1076 | "x86_64-cygwin": {
1077 | "total": 5,
1078 | "unique": 2
1079 | },
1080 | "x86_64-darwin-13": {
1081 | "total": 15,
1082 | "unique": 5
1083 | },
1084 | "x86_64-darwin-14": {
1085 | "total": 4,
1086 | "unique": 2
1087 | },
1088 | "x86_64-darwin-15": {
1089 | "total": 10,
1090 | "unique": 3
1091 | },
1092 | "x86_64-darwin-17": {
1093 | "total": 1,
1094 | "unique": 1
1095 | },
1096 | "x86_64-darwin-18": {
1097 | "total": 16,
1098 | "unique": 8
1099 | },
1100 | "x86_64-darwin-19": {
1101 | "total": 164,
1102 | "unique": 28
1103 | },
1104 | "x86_64-darwin-20": {
1105 | "total": 109,
1106 | "unique": 52
1107 | },
1108 | "x86_64-darwin-21": {
1109 | "total": 21,
1110 | "unique": 6
1111 | },
1112 | "x86_64-koji-linux-gnu": {
1113 | "total": 15,
1114 | "unique": 8
1115 | },
1116 | "x86_64-linux": {
1117 | "total": 29383,
1118 | "unique": 8338
1119 | },
1120 | "x86_64-linux-musl": {
1121 | "total": 57,
1122 | "unique": 7
1123 | },
1124 | "x86_64-pc-linux": {
1125 | "total": 2471,
1126 | "unique": 516
1127 | },
1128 | "x86_64-pc-linux-gnu": {
1129 | "total": 43658,
1130 | "unique": 16716
1131 | },
1132 | "x86_64-pc-linux-musl": {
1133 | "total": 61,
1134 | "unique": 31
1135 | },
1136 | "x86_64-redhat-linux": {
1137 | "total": 4,
1138 | "unique": 2
1139 | },
1140 | "x86_64-redhat-linux-gnu": {
1141 | "total": 6,
1142 | "unique": 2
1143 | },
1144 | "x86_64-solaris-2.11": {
1145 | "total": 4,
1146 | "unique": 2
1147 | },
1148 | "x86_64-unknown-freebsd10.3": {
1149 | "total": 43,
1150 | "unique": 8
1151 | },
1152 | "x86_64-unknown-freebsd11.3": {
1153 | "total": 1,
1154 | "unique": 1
1155 | },
1156 | "x86_64-unknown-freebsd11.4": {
1157 | "total": 2,
1158 | "unique": 1
1159 | },
1160 | "x86_64-unknown-linux": {
1161 | "total": 1,
1162 | "unique": 1
1163 | },
1164 | "x86_64-unknown-linux-gnu": {
1165 | "total": 89,
1166 | "unique": 42
1167 | },
1168 | "x86_64-w64-mingw32": {
1169 | "total": 3228,
1170 | "unique": 1402
1171 | }
1172 | },
1173 | "ruby": {
1174 | "1.8.7": {
1175 | "total": 52,
1176 | "unique": 26
1177 | },
1178 | "1.9.3": {
1179 | "total": 379,
1180 | "unique": 182
1181 | },
1182 | "2.0.0": {
1183 | "total": 1456,
1184 | "unique": 441
1185 | },
1186 | "2.1.0": {
1187 | "total": 4,
1188 | "unique": 2
1189 | },
1190 | "2.1.1": {
1191 | "total": 2,
1192 | "unique": 1
1193 | },
1194 | "2.1.10": {
1195 | "total": 266,
1196 | "unique": 77
1197 | },
1198 | "2.1.2": {
1199 | "total": 4,
1200 | "unique": 1
1201 | },
1202 | "2.1.3": {
1203 | "total": 11,
1204 | "unique": 2
1205 | },
1206 | "2.1.4": {
1207 | "total": 52,
1208 | "unique": 18
1209 | },
1210 | "2.1.5": {
1211 | "total": 34,
1212 | "unique": 4
1213 | },
1214 | "2.1.6": {
1215 | "total": 1254,
1216 | "unique": 420
1217 | },
1218 | "2.1.7": {
1219 | "total": 2,
1220 | "unique": 1
1221 | },
1222 | "2.1.8": {
1223 | "total": 292,
1224 | "unique": 38
1225 | },
1226 | "2.1.9": {
1227 | "total": 140,
1228 | "unique": 64
1229 | },
1230 | "2.2.0": {
1231 | "total": 21,
1232 | "unique": 4
1233 | },
1234 | "2.2.10": {
1235 | "total": 15,
1236 | "unique": 3
1237 | },
1238 | "2.2.2": {
1239 | "total": 50,
1240 | "unique": 21
1241 | },
1242 | "2.2.3": {
1243 | "total": 18,
1244 | "unique": 7
1245 | },
1246 | "2.2.4": {
1247 | "total": 46,
1248 | "unique": 14
1249 | },
1250 | "2.2.5": {
1251 | "total": 2,
1252 | "unique": 1
1253 | },
1254 | "2.2.6": {
1255 | "total": 13,
1256 | "unique": 4
1257 | },
1258 | "2.2.7": {
1259 | "total": 39,
1260 | "unique": 19
1261 | },
1262 | "2.2.8": {
1263 | "total": 1,
1264 | "unique": 1
1265 | },
1266 | "2.2.9": {
1267 | "total": 3,
1268 | "unique": 1
1269 | },
1270 | "2.3.0": {
1271 | "total": 972,
1272 | "unique": 421
1273 | },
1274 | "2.3.1": {
1275 | "total": 7730,
1276 | "unique": 3157
1277 | },
1278 | "2.3.3": {
1279 | "total": 2588,
1280 | "unique": 663
1281 | },
1282 | "2.3.4": {
1283 | "total": 118,
1284 | "unique": 55
1285 | },
1286 | "2.3.5": {
1287 | "total": 3484,
1288 | "unique": 1136
1289 | },
1290 | "2.3.6": {
1291 | "total": 21210,
1292 | "unique": 5113
1293 | },
1294 | "2.3.7": {
1295 | "total": 72,
1296 | "unique": 27
1297 | },
1298 | "2.3.8": {
1299 | "total": 199,
1300 | "unique": 46
1301 | },
1302 | "2.4.0": {
1303 | "total": 25,
1304 | "unique": 12
1305 | },
1306 | "2.4.1": {
1307 | "total": 204,
1308 | "unique": 47
1309 | },
1310 | "2.4.10": {
1311 | "total": 398,
1312 | "unique": 54
1313 | },
1314 | "2.4.2": {
1315 | "total": 736,
1316 | "unique": 297
1317 | },
1318 | "2.4.3": {
1319 | "total": 1045,
1320 | "unique": 401
1321 | },
1322 | "2.4.4": {
1323 | "total": 926,
1324 | "unique": 256
1325 | },
1326 | "2.4.5": {
1327 | "total": 1503,
1328 | "unique": 678
1329 | },
1330 | "2.4.6": {
1331 | "total": 11,
1332 | "unique": 2
1333 | },
1334 | "2.4.7": {
1335 | "total": 1023,
1336 | "unique": 120
1337 | },
1338 | "2.4.9": {
1339 | "total": 85,
1340 | "unique": 32
1341 | },
1342 | "2.5.0": {
1343 | "total": 52,
1344 | "unique": 19
1345 | },
1346 | "2.5.1": {
1347 | "total": 3621,
1348 | "unique": 1343
1349 | },
1350 | "2.5.2": {
1351 | "total": 2,
1352 | "unique": 1
1353 | },
1354 | "2.5.3": {
1355 | "total": 6836,
1356 | "unique": 1935
1357 | },
1358 | "2.5.4": {
1359 | "total": 9,
1360 | "unique": 3
1361 | },
1362 | "2.5.5": {
1363 | "total": 2378,
1364 | "unique": 784
1365 | },
1366 | "2.5.7": {
1367 | "total": 2390,
1368 | "unique": 852
1369 | },
1370 | "2.5.8": {
1371 | "total": 1072,
1372 | "unique": 321
1373 | },
1374 | "2.5.9": {
1375 | "total": 264,
1376 | "unique": 132
1377 | },
1378 | "2.6.0": {
1379 | "total": 16,
1380 | "unique": 4
1381 | },
1382 | "2.6.1": {
1383 | "total": 236,
1384 | "unique": 112
1385 | },
1386 | "2.6.2": {
1387 | "total": 59,
1388 | "unique": 24
1389 | },
1390 | "2.6.3": {
1391 | "total": 1524,
1392 | "unique": 324
1393 | },
1394 | "2.6.4": {
1395 | "total": 96,
1396 | "unique": 13
1397 | },
1398 | "2.6.5": {
1399 | "total": 5671,
1400 | "unique": 2036
1401 | },
1402 | "2.6.6": {
1403 | "total": 1358,
1404 | "unique": 497
1405 | },
1406 | "2.6.7": {
1407 | "total": 651,
1408 | "unique": 287
1409 | },
1410 | "2.6.8": {
1411 | "total": 844,
1412 | "unique": 139
1413 | },
1414 | "2.6.9": {
1415 | "total": 179,
1416 | "unique": 36
1417 | },
1418 | "2.7.0": {
1419 | "total": 518,
1420 | "unique": 201
1421 | },
1422 | "2.7.1": {
1423 | "total": 1082,
1424 | "unique": 450
1425 | },
1426 | "2.7.2": {
1427 | "total": 1827,
1428 | "unique": 756
1429 | },
1430 | "2.7.3": {
1431 | "total": 788,
1432 | "unique": 167
1433 | },
1434 | "2.7.4": {
1435 | "total": 845,
1436 | "unique": 353
1437 | },
1438 | "2.7.5": {
1439 | "total": 2089,
1440 | "unique": 418
1441 | },
1442 | "3.0.0": {
1443 | "total": 18,
1444 | "unique": 9
1445 | },
1446 | "3.0.1": {
1447 | "total": 560,
1448 | "unique": 250
1449 | },
1450 | "3.0.2": {
1451 | "total": 768,
1452 | "unique": 325
1453 | },
1454 | "3.0.3": {
1455 | "total": 308,
1456 | "unique": 79
1457 | },
1458 | "3.1.0": {
1459 | "total": 50,
1460 | "unique": 12
1461 | },
1462 | "3.2.0": {
1463 | "total": 2,
1464 | "unique": 1
1465 | }
1466 | },
1467 | "rubygems": {
1468 | "1.3.7": {
1469 | "total": 46,
1470 | "unique": 23
1471 | },
1472 | "1.6.2": {
1473 | "total": 2,
1474 | "unique": 1
1475 | },
1476 | "1.8.11": {
1477 | "total": 3,
1478 | "unique": 1
1479 | },
1480 | "1.8.23": {
1481 | "total": 85,
1482 | "unique": 42
1483 | },
1484 | "1.8.23.2": {
1485 | "total": 1,
1486 | "unique": 1
1487 | },
1488 | "1.8.24": {
1489 | "total": 15,
1490 | "unique": 6
1491 | },
1492 | "1.8.28": {
1493 | "total": 1,
1494 | "unique": 1
1495 | },
1496 | "1.8.29": {
1497 | "total": 17,
1498 | "unique": 7
1499 | },
1500 | "1.8.30": {
1501 | "total": 1,
1502 | "unique": 1
1503 | },
1504 | "2.0.14": {
1505 | "total": 27,
1506 | "unique": 4
1507 | },
1508 | "2.0.14.1": {
1509 | "total": 1280,
1510 | "unique": 152
1511 | },
1512 | "2.1.11": {
1513 | "total": 36,
1514 | "unique": 16
1515 | },
1516 | "2.2.0": {
1517 | "total": 1,
1518 | "unique": 1
1519 | },
1520 | "2.2.2": {
1521 | "total": 104,
1522 | "unique": 19
1523 | },
1524 | "2.2.5": {
1525 | "total": 86,
1526 | "unique": 26
1527 | },
1528 | "2.4.3": {
1529 | "total": 2,
1530 | "unique": 1
1531 | },
1532 | "2.4.4": {
1533 | "total": 241,
1534 | "unique": 108
1535 | },
1536 | "2.4.5": {
1537 | "total": 3,
1538 | "unique": 1
1539 | },
1540 | "2.4.5.1": {
1541 | "total": 41,
1542 | "unique": 7
1543 | },
1544 | "2.4.5.2": {
1545 | "total": 1,
1546 | "unique": 1
1547 | },
1548 | "2.4.5.3": {
1549 | "total": 3,
1550 | "unique": 1
1551 | },
1552 | "2.4.5.4": {
1553 | "total": 3,
1554 | "unique": 1
1555 | },
1556 | "2.4.5.5": {
1557 | "total": 6,
1558 | "unique": 3
1559 | },
1560 | "2.4.7": {
1561 | "total": 45,
1562 | "unique": 6
1563 | },
1564 | "2.4.8": {
1565 | "total": 954,
1566 | "unique": 119
1567 | },
1568 | "2.4.8.1.acquia": {
1569 | "total": 9,
1570 | "unique": 3
1571 | },
1572 | "2.5.1": {
1573 | "total": 248,
1574 | "unique": 103
1575 | },
1576 | "2.5.2": {
1577 | "total": 473,
1578 | "unique": 157
1579 | },
1580 | "2.5.2.1": {
1581 | "total": 2666,
1582 | "unique": 1262
1583 | },
1584 | "2.5.2.2": {
1585 | "total": 2,
1586 | "unique": 1
1587 | },
1588 | "2.5.2.3": {
1589 | "total": 140,
1590 | "unique": 39
1591 | },
1592 | "2.6.0": {
1593 | "total": 1,
1594 | "unique": 1
1595 | },
1596 | "2.6.10": {
1597 | "total": 1300,
1598 | "unique": 638
1599 | },
1600 | "2.6.11": {
1601 | "total": 732,
1602 | "unique": 265
1603 | },
1604 | "2.6.12": {
1605 | "total": 188,
1606 | "unique": 68
1607 | },
1608 | "2.6.13": {
1609 | "total": 2844,
1610 | "unique": 767
1611 | },
1612 | "2.6.14": {
1613 | "total": 24933,
1614 | "unique": 11294
1615 | },
1616 | "2.6.14.1": {
1617 | "total": 37,
1618 | "unique": 9
1619 | },
1620 | "2.6.14.3": {
1621 | "total": 4,
1622 | "unique": 1
1623 | },
1624 | "2.6.14.4": {
1625 | "total": 1248,
1626 | "unique": 541
1627 | },
1628 | "2.6.2": {
1629 | "total": 1,
1630 | "unique": 1
1631 | },
1632 | "2.6.3": {
1633 | "total": 46,
1634 | "unique": 19
1635 | },
1636 | "2.6.4": {
1637 | "total": 83,
1638 | "unique": 36
1639 | },
1640 | "2.6.6": {
1641 | "total": 1454,
1642 | "unique": 426
1643 | },
1644 | "2.6.7": {
1645 | "total": 622,
1646 | "unique": 239
1647 | },
1648 | "2.6.8": {
1649 | "total": 4030,
1650 | "unique": 1039
1651 | },
1652 | "2.7.0": {
1653 | "total": 202,
1654 | "unique": 73
1655 | },
1656 | "2.7.10": {
1657 | "total": 106,
1658 | "unique": 45
1659 | },
1660 | "2.7.11": {
1661 | "total": 197,
1662 | "unique": 30
1663 | },
1664 | "2.7.3": {
1665 | "total": 222,
1666 | "unique": 45
1667 | },
1668 | "2.7.4": {
1669 | "total": 4,
1670 | "unique": 2
1671 | },
1672 | "2.7.5": {
1673 | "total": 2,
1674 | "unique": 1
1675 | },
1676 | "2.7.6": {
1677 | "total": 9481,
1678 | "unique": 4295
1679 | },
1680 | "2.7.6.1": {
1681 | "total": 1,
1682 | "unique": 1
1683 | },
1684 | "2.7.6.2": {
1685 | "total": 1341,
1686 | "unique": 456
1687 | },
1688 | "2.7.6.3": {
1689 | "total": 211,
1690 | "unique": 47
1691 | },
1692 | "2.7.7": {
1693 | "total": 267,
1694 | "unique": 58
1695 | },
1696 | "2.7.8": {
1697 | "total": 499,
1698 | "unique": 188
1699 | },
1700 | "2.7.9": {
1701 | "total": 5552,
1702 | "unique": 2647
1703 | },
1704 | "3.0.0": {
1705 | "total": 86,
1706 | "unique": 41
1707 | },
1708 | "3.0.1": {
1709 | "total": 56,
1710 | "unique": 22
1711 | },
1712 | "3.0.2": {
1713 | "total": 9,
1714 | "unique": 2
1715 | },
1716 | "3.0.3": {
1717 | "total": 7521,
1718 | "unique": 2813
1719 | },
1720 | "3.0.3.1": {
1721 | "total": 1172,
1722 | "unique": 560
1723 | },
1724 | "3.0.4": {
1725 | "total": 715,
1726 | "unique": 286
1727 | },
1728 | "3.0.6": {
1729 | "total": 793,
1730 | "unique": 248
1731 | },
1732 | "3.0.8": {
1733 | "total": 335,
1734 | "unique": 40
1735 | },
1736 | "3.0.9": {
1737 | "total": 78,
1738 | "unique": 37
1739 | },
1740 | "3.1.1": {
1741 | "total": 1,
1742 | "unique": 1
1743 | },
1744 | "3.1.2": {
1745 | "total": 1573,
1746 | "unique": 581
1747 | },
1748 | "3.1.3": {
1749 | "total": 144,
1750 | "unique": 17
1751 | },
1752 | "3.1.4": {
1753 | "total": 1831,
1754 | "unique": 911
1755 | },
1756 | "3.1.6": {
1757 | "total": 3173,
1758 | "unique": 879
1759 | },
1760 | "3.2.11": {
1761 | "total": 11,
1762 | "unique": 2
1763 | },
1764 | "3.2.12": {
1765 | "total": 1,
1766 | "unique": 1
1767 | },
1768 | "3.2.15": {
1769 | "total": 565,
1770 | "unique": 199
1771 | },
1772 | "3.2.16": {
1773 | "total": 16,
1774 | "unique": 8
1775 | },
1776 | "3.2.17": {
1777 | "total": 13,
1778 | "unique": 3
1779 | },
1780 | "3.2.18": {
1781 | "total": 1,
1782 | "unique": 1
1783 | },
1784 | "3.2.19": {
1785 | "total": 1,
1786 | "unique": 1
1787 | },
1788 | "3.2.2": {
1789 | "total": 15,
1790 | "unique": 4
1791 | },
1792 | "3.2.20": {
1793 | "total": 441,
1794 | "unique": 203
1795 | },
1796 | "3.2.21": {
1797 | "total": 1,
1798 | "unique": 1
1799 | },
1800 | "3.2.22": {
1801 | "total": 777,
1802 | "unique": 191
1803 | },
1804 | "3.2.24": {
1805 | "total": 5,
1806 | "unique": 1
1807 | },
1808 | "3.2.25": {
1809 | "total": 2,
1810 | "unique": 1
1811 | },
1812 | "3.2.26": {
1813 | "total": 4,
1814 | "unique": 1
1815 | },
1816 | "3.2.27": {
1817 | "total": 11,
1818 | "unique": 2
1819 | },
1820 | "3.2.28": {
1821 | "total": 9,
1822 | "unique": 2
1823 | },
1824 | "3.2.29": {
1825 | "total": 6,
1826 | "unique": 3
1827 | },
1828 | "3.2.3": {
1829 | "total": 21,
1830 | "unique": 8
1831 | },
1832 | "3.2.30": {
1833 | "total": 10,
1834 | "unique": 5
1835 | },
1836 | "3.2.31": {
1837 | "total": 100,
1838 | "unique": 41
1839 | },
1840 | "3.2.32": {
1841 | "total": 495,
1842 | "unique": 140
1843 | },
1844 | "3.2.33": {
1845 | "total": 20,
1846 | "unique": 10
1847 | },
1848 | "3.2.4": {
1849 | "total": 1,
1850 | "unique": 1
1851 | },
1852 | "3.2.5": {
1853 | "total": 51,
1854 | "unique": 14
1855 | },
1856 | "3.2.6": {
1857 | "total": 1,
1858 | "unique": 1
1859 | },
1860 | "3.2.7": {
1861 | "total": 9,
1862 | "unique": 5
1863 | },
1864 | "3.2.8": {
1865 | "total": 29,
1866 | "unique": 5
1867 | },
1868 | "3.3.0": {
1869 | "total": 2,
1870 | "unique": 1
1871 | },
1872 | "3.3.1": {
1873 | "total": 1,
1874 | "unique": 1
1875 | },
1876 | "3.3.3": {
1877 | "total": 47,
1878 | "unique": 8
1879 | },
1880 | "3.3.4": {
1881 | "total": 63,
1882 | "unique": 16
1883 | },
1884 | "3.3.5": {
1885 | "total": 53,
1886 | "unique": 15
1887 | },
1888 | "3.3.6": {
1889 | "total": 177,
1890 | "unique": 37
1891 | },
1892 | "3.4.0.dev": {
1893 | "total": 2,
1894 | "unique": 1
1895 | }
1896 | },
1897 | "server_region": {
1898 | },
1899 | "tls_cipher": {
1900 | "": {
1901 | "total": 464,
1902 | "unique": 116
1903 | },
1904 | "AES128-GCM": {
1905 | "total": 1625,
1906 | "unique": 202
1907 | },
1908 | "AES256-GCM": {
1909 | "total": 10072,
1910 | "unique": 1447
1911 | },
1912 | "ECDHE-RSA-AES128-GCM-SHA256": {
1913 | "total": 72325,
1914 | "unique": 10737
1915 | },
1916 | "ECDHE-RSA-AES128-SHA": {
1917 | "total": 101,
1918 | "unique": 40
1919 | },
1920 | "ECDHE-RSA-AES128-SHA256": {
1921 | "total": 93,
1922 | "unique": 11
1923 | }
1924 | }
1925 | }
1926 | }
1927 |
--------------------------------------------------------------------------------
/spec/workers/import_stats_day_worker_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe ImportStatsDayWorker, type: :worker do
4 | let(:start_date) { Date.new(2022, 1, 31) }
5 | let(:end_date) { Date.new(2022, 2, 1) }
6 |
7 | it "handle old format" do
8 | Stat.delete_all
9 | ImportStatus.delete_all
10 | ImportStatus.import("test", Rails.root.join("spec/support/legacy_stats.json").read)
11 |
12 | worker = ImportStatsDayWorker.new
13 |
14 | expect do
15 | worker.perform(end_date)
16 | end.to change { Stat.count }
17 |
18 | expect(Stat.daily_totals("rubygems", start_date)).to be == { end_date => 82598 }
19 | end
20 |
21 | it "handle new format" do
22 | Stat.delete_all
23 | ImportStatus.delete_all
24 |
25 | ImportStatus.import("test", Rails.root.join("spec/support/new_stats.json").read)
26 | worker = ImportStatsDayWorker.new
27 |
28 | expect do
29 | worker.perform(end_date)
30 | end.to change { Stat.count }
31 |
32 | expect(Stat.daily_totals("rubygems", start_date)).to be == { end_date => 82598 }
33 | expect(Stat.daily_totals("rubygems_unique", start_date)).to be == { end_date => 32717 }
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/workers/import_stats_worker_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 | RSpec.describe ImportStatsWorker, type: :worker do
3 | pending "add some examples to (or delete) #{__FILE__}"
4 | end
5 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/tmp/.keep
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubytogether/ecosystem/b86ae695fe264445bbcf2e3c3979e007d0d4b130/vendor/.keep
--------------------------------------------------------------------------------