├── .pdd ├── renovate.json ├── .gitignore ├── .0pdd.yml ├── lib ├── total │ ├── version.rb │ ├── linux.rb │ ├── osx.rb │ ├── freebsd.rb │ └── windows.rb └── total.rb ├── .github └── workflows │ ├── reuse.yml │ ├── typos.yml │ ├── pdd.yml │ ├── yamllint.yml │ ├── copyrights.yml │ ├── markdown-lint.yml │ ├── xcop.yml │ ├── rake.yml │ ├── actionlint.yml │ └── codecov.yml ├── test ├── test_total.rb ├── total │ ├── test_osx.rb │ ├── test_linux.rb │ ├── test_freebsd.rb │ └── test_windows.rb └── test__helper.rb ├── .rultor.yml ├── Gemfile ├── .rubocop.yml ├── REUSE.toml ├── total.gemspec ├── Rakefile ├── LICENSE.txt ├── LICENSES └── MIT.txt ├── README.md ├── Gemfile.lock └── logo.svg /.pdd: -------------------------------------------------------------------------------- 1 | --source=. 2 | --verbose 3 | --rule min-words:20 4 | --rule min-estimate:15 5 | --rule max-estimate:90 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle/ 3 | .DS_Store 4 | .idea/ 5 | .yardoc/ 6 | coverage/ 7 | doc/ 8 | node_modules/ 9 | NUL 10 | rdoc/ 11 | vendor/ 12 | -------------------------------------------------------------------------------- /.0pdd.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | errors: 5 | - yegor256@gmail.com 6 | # alerts: 7 | # github: 8 | # - yegor256 9 | 10 | tags: 11 | - pdd 12 | - bug 13 | -------------------------------------------------------------------------------- /lib/total/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | # Version of it. 7 | # 8 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 9 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 10 | # License:: MIT 11 | module Total 12 | # Current version of the library. 13 | VERSION = '0.0.0' 14 | end 15 | -------------------------------------------------------------------------------- /.github/workflows/reuse.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: reuse 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | reuse: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: fsfe/reuse-action@v6 20 | -------------------------------------------------------------------------------- /.github/workflows/typos.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: typos 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | typos: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: crate-ci/typos@v1.40.0 20 | -------------------------------------------------------------------------------- /.github/workflows/pdd.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: pdd 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | pdd: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: volodya-lombrozo/pdd-action@master 20 | -------------------------------------------------------------------------------- /.github/workflows/yamllint.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: yamllint 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | yamllint: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: ibiqlik/action-yamllint@v3 20 | -------------------------------------------------------------------------------- /.github/workflows/copyrights.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: copyrights 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | copyrights: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: yegor256/copyrights-action@0.0.12 20 | -------------------------------------------------------------------------------- /.github/workflows/markdown-lint.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: markdown-lint 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | markdown-lint: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: DavidAnson/markdownlint-cli2-action@v21.0.0 20 | -------------------------------------------------------------------------------- /.github/workflows/xcop.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: xcop 6 | "on": 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | xcop: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - uses: g4s8/xcop-action@master 20 | with: 21 | files: '**/*.xml' 22 | -------------------------------------------------------------------------------- /test/test_total.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'minitest/autorun' 7 | require_relative '../lib/total' 8 | 9 | # Total test. 10 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 11 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 12 | # License:: MIT 13 | class TotalTest < Minitest::Test 14 | def test_simple_fetch 15 | mem = Total::Mem.new 16 | refute_nil(mem.bytes) 17 | assert_operator(mem.bytes, :>, 1024 * 1024) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/total/linux.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | # Linux specific. 7 | # 8 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 9 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 10 | # License:: MIT 11 | module Total 12 | # Linux specifics. 13 | class Linux 14 | # Get the total in bytes 15 | def memory 16 | raise CantDetect unless File.exist?('/proc/meminfo') 17 | File.readlines('/proc/meminfo').each do |t| 18 | return t.split(/ +/)[1].to_i * 1024 if t.start_with?('MemTotal:') 19 | end 20 | raise CantDetect, 'Can\'t detect memory size at /proc/meminfo' 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.rultor.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | docker: 6 | image: yegor256/ruby 7 | assets: 8 | rubygems.yml: yegor256/home#assets/rubygems.yml 9 | install: | 10 | pdd -f /dev/null 11 | sudo bundle install --no-color "--gemfile=$(pwd)/Gemfile" 12 | release: 13 | pre: false 14 | script: |- 15 | bundle exec rake 16 | sed -i "s/0\.0\.0/${tag}/g" lib/total/version.rb 17 | git add lib/total/version.rb 18 | git commit -m "version set to ${tag}" 19 | gem build total.gemspec 20 | chmod 0600 ../rubygems.yml 21 | gem push *.gem --config-file ../rubygems.yml 22 | merge: 23 | script: |- 24 | bundle exec rake 25 | -------------------------------------------------------------------------------- /lib/total/osx.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | # OSX specific. 7 | # 8 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 9 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 10 | # License:: MIT 11 | module Total 12 | # OSX specifics. 13 | class OSX 14 | # Get the total in bytes 15 | def memory 16 | begin 17 | `sysctl -a`.split("\n").each do |t| 18 | return t.split[1].to_i if t.start_with?('hw.memsize:') 19 | end 20 | rescue Errno::ENOENT => e 21 | raise CantDetect, e.message 22 | end 23 | raise CantDetect, 'Can\'t detect memory size via sysctl' 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | source 'https://rubygems.org' 7 | gemspec 8 | 9 | gem 'minitest', '~>6.0', require: false 10 | gem 'minitest-reporters', '~>1.7', require: false 11 | gem 'mutex_m', '~>0.3', require: false 12 | gem 'ostruct', '~>0.6', require: false 13 | gem 'rake', '~>13.0.6', require: false 14 | gem 'rdoc', '~>7.0.0', require: false 15 | gem 'rubocop', '~>1.82.0', require: false 16 | gem 'rubocop-minitest', '~>0.38', require: false 17 | gem 'rubocop-performance', '~>1.25', require: false 18 | gem 'rubocop-rake', '~>0.7', require: false 19 | gem 'simplecov', '~>0.22', require: false 20 | gem 'simplecov-cobertura', '~>3.0', require: false 21 | -------------------------------------------------------------------------------- /lib/total/freebsd.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | # FreeBSD specific. 7 | # 8 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 9 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 10 | # License:: MIT 11 | module Total 12 | # FreeBSD specifics. 13 | class FreeBSD 14 | # Get the total in bytes 15 | def memory 16 | begin 17 | `sysctl -a`.split("\n").each do |t| 18 | return t.split[1].to_i if t.start_with?('hw.physmem:') 19 | end 20 | rescue Errno::ENOENT => e 21 | raise CantDetect, e.message 22 | end 23 | raise CantDetect, 'Can\'t detect memory size via sysctl' 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | AllCops: 5 | NewCops: enable 6 | DisplayCopNames: true 7 | TargetRubyVersion: 2.3 8 | SuggestExtensions: false 9 | Minitest/EmptyLineBeforeAssertionMethods: 10 | Enabled: false 11 | Layout/EmptyLineAfterGuardClause: 12 | Enabled: false 13 | Layout/MultilineMethodCallIndentation: 14 | Enabled: false 15 | Metrics/AbcSize: 16 | Max: 50 17 | Metrics/MethodLength: 18 | Max: 30 19 | Metrics/CyclomaticComplexity: 20 | Max: 10 21 | Metrics/PerceivedComplexity: 22 | Max: 10 23 | Metrics/ParameterLists: 24 | Max: 10 25 | Layout/ParameterAlignment: 26 | Enabled: false 27 | plugins: 28 | - rubocop-rake 29 | - rubocop-minitest 30 | - rubocop-performance 31 | -------------------------------------------------------------------------------- /.github/workflows/rake.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: rake 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | rake: 15 | strategy: 16 | matrix: 17 | os: [ubuntu-24.04] 18 | ruby: [3.3] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v6 22 | - uses: ruby/setup-ruby@v1 23 | with: 24 | ruby-version: ${{ matrix.ruby }} 25 | bundler-cache: true 26 | - run: bundle config set --global path "$(pwd)/vendor/bundle" 27 | - run: bundle install --no-color 28 | - run: bundle exec rake 29 | -------------------------------------------------------------------------------- /.github/workflows/actionlint.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: actionlint 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | actionlint: 15 | timeout-minutes: 15 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - uses: actions/checkout@v6 19 | - name: Download actionlint 20 | id: get_actionlint 21 | run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 22 | shell: bash 23 | - name: Check workflow files 24 | run: ${{ steps.get_actionlint.outputs.executable }} -color 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | --- 4 | # yamllint disable rule:line-length 5 | name: codecov 6 | 'on': 7 | push: 8 | branches: 9 | - master 10 | jobs: 11 | codecov: 12 | timeout-minutes: 15 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: actions/checkout@v6 16 | - uses: actions/setup-ruby@v1 17 | with: 18 | ruby-version: 3.3 19 | bundler-cache: true 20 | - run: bundle config set --global path "$(pwd)/vendor/bundle" 21 | - run: bundle install --no-color 22 | - run: bundle exec rake 23 | - uses: codecov/codecov-action@v5 24 | with: 25 | files: coverage/.resultset.json 26 | fail_ci_if_error: true 27 | -------------------------------------------------------------------------------- /test/total/test_osx.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'minitest/autorun' 7 | require_relative '../../lib/total/osx' 8 | 9 | # OSX test. 10 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 11 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 12 | # License:: MIT 13 | class OSXTest < Minitest::Test 14 | def test_fetch_memory_size 15 | skip unless RUBY_PLATFORM.include?('darwin') 16 | linux = Total::OSX.new 17 | refute_nil(linux.memory) 18 | assert_operator(linux.memory, :>, 1024 * 1024) 19 | end 20 | 21 | def test_crashes_when_cant_detect 22 | skip if RUBY_PLATFORM.include?('darwin') 23 | assert_raises Total::CantDetect do 24 | Total::OSX.new.memory 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko 2 | # SPDX-License-Identifier: MIT 3 | 4 | version = 1 5 | [[annotations]] 6 | path = [ 7 | ".DS_Store", 8 | ".gitattributes", 9 | ".gitignore", 10 | ".pdd", 11 | "**.json", 12 | "**.md", 13 | "**.png", 14 | "**.svg", 15 | "**.txt", 16 | "**/.DS_Store", 17 | "**/.gitignore", 18 | "**/.pdd", 19 | "**/*.csv", 20 | "**/*.jpg", 21 | "**/*.json", 22 | "**/*.md", 23 | "**/*.pdf", 24 | "**/*.png", 25 | "**/*.svg", 26 | "**/*.txt", 27 | "**/*.vm", 28 | "**/CNAME", 29 | "**/Gemfile.lock", 30 | "Gemfile.lock", 31 | "README.md", 32 | "renovate.json", 33 | ] 34 | precedence = "override" 35 | SPDX-FileCopyrightText = "Copyright (c) 2025 Yegor Bugayenko" 36 | SPDX-License-Identifier = "MIT" 37 | -------------------------------------------------------------------------------- /test/total/test_linux.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'minitest/autorun' 7 | require_relative '../../lib/total/linux' 8 | 9 | # Linux test. 10 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 11 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 12 | # License:: MIT 13 | class LinuxTest < Minitest::Test 14 | def test_fetch_memory_size 15 | skip unless RUBY_PLATFORM.include?('linux') 16 | linux = Total::Linux.new 17 | refute_nil(linux.memory) 18 | assert_operator(linux.memory, :>, 1024 * 1024) 19 | end 20 | 21 | def test_crashes_when_cant_detect 22 | skip if RUBY_PLATFORM.include?('linux') 23 | assert_raises Total::CantDetect do 24 | Total::Linux.new.memory 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/total/test_freebsd.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'minitest/autorun' 7 | require_relative '../../lib/total/freebsd' 8 | 9 | # FreeBSD test. 10 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 11 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 12 | # License:: MIT 13 | class FreeBSDTest < Minitest::Test 14 | def test_fetch_memory_size 15 | skip unless RUBY_PLATFORM.include?('freebsd') 16 | freebsd = Total::FreeBSD.new 17 | refute_nil(freebsd.memory) 18 | assert_operator(freebsd.memory, :>, 1024 * 1024) 19 | end 20 | 21 | def test_crashes_when_cant_detect 22 | skip if RUBY_PLATFORM.include?('freebsd') 23 | assert_raises Total::CantDetect do 24 | Total::FreeBSD.new.memory 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/total/test_windows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'minitest/autorun' 7 | require_relative '../../lib/total' 8 | 9 | # Windows test. 10 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 11 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 12 | # License:: MIT 13 | class WindowsTest < Minitest::Test 14 | def test_fetch_memory_size 15 | skip unless windows_platform? 16 | windows = Total::Windows.new 17 | refute_nil(windows.memory) 18 | assert_operator(windows.memory, :>, 1024 * 1024) 19 | end 20 | 21 | def test_crashes_when_cant_detect 22 | skip if windows_platform? 23 | assert_raises Total::CantDetect do 24 | Total::Windows.new.memory 25 | end 26 | end 27 | 28 | private 29 | 30 | def windows_platform? 31 | RUBY_PLATFORM =~ /mingw|mswin|cygwin|ucrt/ 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/test__helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | $stdout.sync = true 7 | 8 | require 'simplecov' 9 | require 'simplecov-cobertura' 10 | unless SimpleCov.running || ENV['PICKS'] 11 | SimpleCov.command_name('test') 12 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( 13 | [ 14 | SimpleCov::Formatter::HTMLFormatter, 15 | SimpleCov::Formatter::CoberturaFormatter 16 | ] 17 | ) 18 | SimpleCov.minimum_coverage 60 19 | SimpleCov.minimum_coverage_by_file 50 20 | SimpleCov.start do 21 | add_filter 'test/' 22 | add_filter 'vendor/' 23 | add_filter 'target/' 24 | track_files 'lib/**/*.rb' 25 | track_files '*.rb' 26 | end 27 | end 28 | 29 | require 'minitest/autorun' 30 | require 'minitest/reporters' 31 | Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new] 32 | Minitest.load :minitest_reporter 33 | -------------------------------------------------------------------------------- /total.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'English' 7 | lib = File.expand_path('lib', __dir__) 8 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 9 | require_relative 'lib/total/version' 10 | Gem::Specification.new do |s| 11 | s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version= 12 | s.required_ruby_version = '>= 2.3' 13 | s.name = 'total' 14 | s.version = Total::VERSION 15 | s.license = 'MIT' 16 | s.summary = 'Total memory calculator in Ruby' 17 | s.description = 'Get total memory size of the system' 18 | s.authors = ['Yegor Bugayenko'] 19 | s.email = 'yegor256@gmail.com' 20 | s.homepage = 'https://github.com/yegor256/total' 21 | s.files = `git ls-files | grep -v -E '^(test/|\\.|renovate)'`.split($RS) 22 | s.rdoc_options = ['--charset=UTF-8'] 23 | s.extra_rdoc_files = ['README.md'] 24 | s.metadata['rubygems_mfa_required'] = 'true' 25 | end 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require 'rubygems' 7 | require 'rake' 8 | require 'rake/clean' 9 | 10 | CLEAN = FileList['coverage'] 11 | 12 | def name 13 | @name ||= File.basename(Dir['*.gemspec'].first, '.*') 14 | end 15 | 16 | def version 17 | Gem::Specification.load(Dir['*.gemspec'].first).version 18 | end 19 | 20 | task default: %i[clean test rubocop] 21 | 22 | require 'rake/testtask' 23 | desc 'Run all unit tests' 24 | Rake::TestTask.new(:test) do |test| 25 | test.libs << 'lib' << 'test' 26 | test.pattern = 'test/**/test_*.rb' 27 | test.verbose = false 28 | end 29 | 30 | require 'rdoc/task' 31 | RDoc::Task.new do |rdoc| 32 | rdoc.main = 'README.md' 33 | rdoc.rdoc_dir = 'rdoc' 34 | rdoc.rdoc_files.include('README.md', 'lib/**/*.rb') 35 | end 36 | 37 | require 'rubocop/rake_task' 38 | desc 'Run RuboCop on all directories' 39 | RuboCop::RakeTask.new(:rubocop) do |task| 40 | task.fail_on_error = true 41 | end 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018-2025 Yegor Bugayenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the 'Software'), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018-2025 Yegor Bugayenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the 'Software'), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/total.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | require_relative 'total/linux' 7 | require_relative 'total/freebsd' 8 | require_relative 'total/osx' 9 | require_relative 'total/windows' 10 | 11 | # Total is a simple class to detect the total amount of memory in the system. 12 | # 13 | # require 'total' 14 | # bytes = Total::Mem.new.bytes 15 | # 16 | # For more information read 17 | # {README}[https://github.com/yegor256/total/blob/master/README.md] file. 18 | # 19 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 20 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 21 | # License:: MIT 22 | module Total 23 | # When it's impossible to detect something. 24 | class CantDetect < StandardError; end 25 | 26 | # Memory specifics. 27 | class Mem 28 | # Get it in bytes. 29 | def bytes 30 | target.memory 31 | end 32 | 33 | private 34 | 35 | # Target object to calculate memory size. 36 | def target 37 | return Total::OSX.new if RUBY_PLATFORM.include?('darwin') 38 | return Total::Linux.new if RUBY_PLATFORM.include?('linux') 39 | return Total::FreeBSD.new if RUBY_PLATFORM.include?('freebsd') 40 | return Total::Windows.new if RUBY_PLATFORM =~ /mingw|mswin|cygwin|ucrt/ 41 | raise CantDetect, "Can't detect operating system: #{RUBY_PLATFORM}" 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Get Total Memory Size 2 | 3 | [![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org) 4 | [![DevOps By Rultor.com](https://www.rultor.com/b/yegor256/total)](https://www.rultor.com/p/yegor256/total) 5 | [![We recommend RubyMine](https://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/) 6 | 7 | [![rake](https://github.com/yegor256/total/actions/workflows/rake.yml/badge.svg)](https://github.com/yegor256/total/actions/workflows/rake.yml) 8 | [![Gem Version](https://badge.fury.io/rb/total.svg)](https://badge.fury.io/rb/total) 9 | [![Maintainability](https://api.codeclimate.com/v1/badges/6e08ce63e597f241ccc7/maintainability)](https://codeclimate.com/github/yegor256/total/maintainability) 10 | [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://rubydoc.info/github/yegor256/total/master/frames) 11 | [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/total/sibit/master/LICENSE.txt) 12 | [![Test Coverage](https://img.shields.io/codecov/c/github/yegor256/total.svg)](https://codecov.io/github/yegor256/total?branch=master) 13 | [![Hits-of-Code](https://hitsofcode.com/github/yegor256/total)](https://hitsofcode.com/view/github/yegor256/total) 14 | 15 | This Ruby gem helps you detect the total amount of memory in the system. 16 | 17 | First, install it: 18 | 19 | ```bash 20 | gem install total 21 | ``` 22 | 23 | Then, use it like this: 24 | 25 | ```ruby 26 | require 'total' 27 | puts Total::Mem.new.bytes 28 | ``` 29 | 30 | The following platforms are supported: 31 | 32 | * MacOSX 33 | * Linux 34 | * FreeBSD 35 | * Windows (including MSYS2/MinGW, MSVC, and Cygwin environments) 36 | 37 | If the platform is not recognized or is not supported, 38 | the `Total::CantDetect` exception is raised. 39 | You catch it and proceed accordingly, for example: 40 | 41 | ```ruby 42 | def total_mb 43 | Total::Mem.new.bytes / (1024 * 1024) 44 | rescue Total::CantDetect 45 | 512 46 | end 47 | ``` 48 | 49 | This code returns the actual memory size in Mb, 50 | if it can be detected, or 512 otherwise. 51 | 52 | That's it. 53 | 54 | ## How to contribute 55 | 56 | Read these [guidelines]. 57 | Make sure your build is green before you contribute your pull request. 58 | You need to have [Ruby](https://www.ruby-lang.org/en/) 2.3+ and 59 | [Bundler](https://bundler.io/) installed. Then: 60 | 61 | ```bash 62 | bundle update 63 | bundle exec rake 64 | ``` 65 | 66 | If it's clean and you don't see any error messages, submit your pull request. 67 | 68 | [guidelines]: https://www.yegor256.com/2014/04/15/github-guidelines.html 69 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | total (0.0.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | ansi (1.5.0) 10 | ast (2.4.3) 11 | builder (3.3.0) 12 | date (3.5.1) 13 | docile (1.4.1) 14 | erb (6.0.1) 15 | json (2.18.0) 16 | language_server-protocol (3.17.0.5) 17 | lint_roller (1.1.0) 18 | minitest (6.0.0) 19 | prism (~> 1.5) 20 | minitest-reporters (1.7.1) 21 | ansi 22 | builder 23 | minitest (>= 5.0) 24 | ruby-progressbar 25 | mutex_m (0.3.0) 26 | ostruct (0.6.3) 27 | parallel (1.27.0) 28 | parser (3.3.10.0) 29 | ast (~> 2.4.1) 30 | racc 31 | prism (1.6.0) 32 | psych (5.3.1) 33 | date 34 | stringio 35 | racc (1.8.1) 36 | rainbow (3.1.1) 37 | rake (13.0.6) 38 | rdoc (7.0.0) 39 | erb 40 | psych (>= 4.0.0) 41 | tsort 42 | regexp_parser (2.11.3) 43 | rexml (3.4.4) 44 | rubocop (1.82.0) 45 | json (~> 2.3) 46 | language_server-protocol (~> 3.17.0.2) 47 | lint_roller (~> 1.1.0) 48 | parallel (~> 1.10) 49 | parser (>= 3.3.0.2) 50 | rainbow (>= 2.2.2, < 4.0) 51 | regexp_parser (>= 2.9.3, < 3.0) 52 | rubocop-ast (>= 1.48.0, < 2.0) 53 | ruby-progressbar (~> 1.7) 54 | unicode-display_width (>= 2.4.0, < 4.0) 55 | rubocop-ast (1.48.0) 56 | parser (>= 3.3.7.2) 57 | prism (~> 1.4) 58 | rubocop-minitest (0.38.2) 59 | lint_roller (~> 1.1) 60 | rubocop (>= 1.75.0, < 2.0) 61 | rubocop-ast (>= 1.38.0, < 2.0) 62 | rubocop-performance (1.26.1) 63 | lint_roller (~> 1.1) 64 | rubocop (>= 1.75.0, < 2.0) 65 | rubocop-ast (>= 1.47.1, < 2.0) 66 | rubocop-rake (0.7.1) 67 | lint_roller (~> 1.1) 68 | rubocop (>= 1.72.1) 69 | ruby-progressbar (1.13.0) 70 | simplecov (0.22.0) 71 | docile (~> 1.1) 72 | simplecov-html (~> 0.11) 73 | simplecov_json_formatter (~> 0.1) 74 | simplecov-cobertura (3.1.0) 75 | rexml 76 | simplecov (~> 0.19) 77 | simplecov-html (0.13.2) 78 | simplecov_json_formatter (0.1.4) 79 | stringio (3.2.0) 80 | tsort (0.2.0) 81 | unicode-display_width (3.2.0) 82 | unicode-emoji (~> 4.1) 83 | unicode-emoji (4.1.0) 84 | 85 | PLATFORMS 86 | arm64-darwin-22 87 | arm64-darwin-23 88 | arm64-darwin-24 89 | x64-mingw-ucrt 90 | x86_64-linux 91 | 92 | DEPENDENCIES 93 | minitest (~> 6.0) 94 | minitest-reporters (~> 1.7) 95 | mutex_m (~> 0.3) 96 | ostruct (~> 0.6) 97 | rake (~> 13.0.6) 98 | rdoc (~> 7.0.0) 99 | rubocop (~> 1.82.0) 100 | rubocop-minitest (~> 0.38) 101 | rubocop-performance (~> 1.25) 102 | rubocop-rake (~> 0.7) 103 | simplecov (~> 0.22) 104 | simplecov-cobertura (~> 3.0) 105 | total! 106 | 107 | BUNDLED WITH 108 | 2.5.16 109 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | total 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/total/windows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko 4 | # SPDX-License-Identifier: MIT 5 | 6 | # Windows specific. 7 | # 8 | # Author:: Yegor Bugayenko (yegor256@gmail.com) 9 | # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko 10 | # License:: MIT 11 | module Total 12 | # Windows specifics. 13 | class Windows 14 | # Detects the total physical memory available on Windows systems. 15 | # 16 | # This method attempts to determine the system's total RAM using multiple 17 | # detection strategies to ensure compatibility across different Windows 18 | # environments and configurations. 19 | # 20 | # Detection methods (tried in order): 21 | # 1. **wmic**: Windows Management Instrumentation Command-line tool 22 | # - Most reliable on traditional Windows installations 23 | # - Uses: `wmic computersystem get TotalPhysicalMemory /value` 24 | # 2. **PowerShell**: Windows PowerShell with WMI queries 25 | # - Available on Windows 7+ and MSYS2 environments 26 | # - Uses: `Get-WmiObject -Class Win32_ComputerSystem` 27 | # 3. **systeminfo**: Built-in Windows system information utility 28 | # - Fallback method for restricted environments 29 | # - Parses output to extract memory information in MB/GB format 30 | # 31 | # @return [Integer] the total amount of physical memory in bytes 32 | # @raise [CantDetect] if all detection methods fail or are unavailable 33 | # 34 | # @example Typical usage 35 | # windows = Total::Windows.new 36 | # total_memory = windows.memory 37 | # puts "Total RAM: #{total_memory / (1024**3)} GB" 38 | # 39 | # @note This method requires at least one of the Windows utilities 40 | # (wmic, powershell.exe, or systeminfo) to be available in the system PATH 41 | def memory 42 | try_wmic || try_powershell || try_systeminfo || 43 | raise(CantDetect, 'Can\'t detect memory size via wmic, PowerShell, or systeminfo') 44 | end 45 | 46 | private 47 | 48 | # Try to get memory using wmic command 49 | # @return [Integer, nil] memory in bytes or nil if failed 50 | def try_wmic 51 | output = `wmic computersystem get TotalPhysicalMemory /value 2>NUL`.strip 52 | output.split("\n").each do |line| 53 | next unless line.start_with?('TotalPhysicalMemory=') 54 | memory_bytes = line.split('=')[1].to_i 55 | return memory_bytes if memory_bytes.positive? 56 | end 57 | nil 58 | rescue StandardError 59 | nil 60 | end 61 | 62 | # Try to get memory using PowerShell 63 | # @return [Integer, nil] memory in bytes or nil if failed 64 | def try_powershell 65 | command = 'powershell.exe -Command "Get-WmiObject -Class Win32_ComputerSystem | ' \ 66 | 'Select-Object -ExpandProperty TotalPhysicalMemory" 2>NUL' 67 | output = `#{command}`.strip 68 | memory_bytes = output.to_i 69 | return memory_bytes if memory_bytes.positive? 70 | nil 71 | rescue StandardError 72 | nil 73 | end 74 | 75 | # Try to get memory using systeminfo command 76 | # @return [Integer, nil] memory in bytes or nil if failed 77 | def try_systeminfo 78 | output = `systeminfo 2>NUL`.strip 79 | output.split("\n").each do |line| 80 | next unless line =~ /Total Physical Memory:\s*(.+)/i 81 | return parse_memory_string(::Regexp.last_match(1).strip) 82 | end 83 | nil 84 | rescue StandardError 85 | nil 86 | end 87 | 88 | # Parse memory string from systeminfo output 89 | # @param memory_str [String] memory string like "16,384 MB" or "16.384 MB" 90 | # @return [Integer, nil] memory in bytes or nil if parsing failed 91 | def parse_memory_string(memory_str) 92 | if memory_str =~ /([0-9,.\s]+)\s*MB/i 93 | memory_mb = ::Regexp.last_match(1).gsub(/[,\s]/, '').to_f 94 | (memory_mb * 1024 * 1024).to_i 95 | elsif memory_str =~ /([0-9,.\s]+)\s*GB/i 96 | memory_gb = ::Regexp.last_match(1).gsub(/[,\s]/, '').to_f 97 | (memory_gb * 1024 * 1024 * 1024).to_i 98 | end 99 | end 100 | end 101 | end 102 | --------------------------------------------------------------------------------