├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .rspec ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── benchmark └── bench.rb ├── bin ├── console └── setup ├── build-libpsl.sh ├── lib ├── mini_suffix.rb └── mini_suffix │ └── version.rb ├── mini_suffix.gemspec ├── spec ├── mini_suffix_spec.rb └── spec_helper.rb └── vendor ├── libpsl.aarch64.so ├── libpsl.darwin-arm64.dylib ├── libpsl.darwin-i386.dylib ├── libpsl.ppc64le.so └── libpsl.x86_64.so /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Mini Suffix Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - main 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | name: Ruby ${{ matrix.ruby }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ruby: ["2.7", "2.6", "2.5"] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: ${{ matrix.ruby }} 23 | - name: Bundler cache 24 | uses: actions/cache@v2 25 | with: 26 | path: vendor/bundle 27 | key: ${{ runner.os }}-${{ matrix.ruby }}-gems-${{ hashFiles('**/Gemfile.lock') }} 28 | restore-keys: | 29 | ${{ runner.os }}-${{ matrix.ruby }}-gems- 30 | - name: Setup gems 31 | run: | 32 | bundle config path vendor/bundle 33 | bundle install --jobs 4 34 | - name: Tests 35 | run: bundle exec rspec 36 | publish: 37 | if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') 38 | needs: build 39 | runs-on: ubuntu-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v2 43 | 44 | - name: Release Gem 45 | uses: discourse/publish-rubygems-action@v2 46 | env: 47 | RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} 48 | GIT_EMAIL: team@discourse.org 49 | GIT_NAME: discoursebot 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | Gemfile.lock 14 | *.gem 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libpsl"] 2 | path = libpsl 3 | url = https://github.com/rockdaboot/libpsl.git 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.3.3 2 | 3 | * Add libpsl shared libary for Linux aarch64 4 | 5 | *xfalcox* 6 | 7 | # 0.3.2 8 | 9 | * Bump libpsl to v0.21.1, and bump binaries 10 | * Add binary for Apple silicon devices 11 | 12 | *davidtaylorhq* 13 | 14 | # 0.3.1 15 | 16 | * Add libpsl shared libary for Linux ppc64le 17 | 18 | *runlevel5* 19 | 20 | # 0.3.0 21 | 22 | * Add libpsl shared library for osx support. 23 | 24 | *tgxworld* 25 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in mini_suffix.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Guo Xiang Tan 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniSuffix 2 | 3 | ## Installation 4 | 5 | ### Debian 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'mini_suffix' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install mini_suffix 20 | 21 | ## Usage 22 | 23 | ``` 24 | require 'mini_suffix' 25 | 26 | MiniSuffix.domain("meta.discourse.org") 27 | # => "discourse.org" 28 | 29 | MiniSuffix.domain("www.careers.gov.sg") 30 | # => "careers.gov.sg" 31 | ``` 32 | 33 | ## Benchmark 34 | 35 | ``` 36 | # benchmark/bench.rb 37 | 38 | PublicSuffix.domain total allocated memsize: 6574255 39 | PublicSuffix.domain total retained memsize: 1133266 40 | MiniSuffix.domain total allocated memsize: 8000 41 | MiniSuffix.domain total retained memsize: 0 42 | 43 | Warming up -------------------------------------- 44 | PublicSuffix.domain 4.503k i/100ms 45 | MiniSuffix.domain 77.107k i/100ms 46 | Calculating ------------------------------------- 47 | PublicSuffix.domain 47.521k (± 2.2%) i/s - 238.659k in 5.024541s 48 | MiniSuffix.domain 875.595k (± 3.8%) i/s - 4.395M in 5.027237s 49 | 50 | Comparison: 51 | MiniSuffix.domain: 875594.7 i/s 52 | PublicSuffix.domain: 47521.2 i/s - 18.43x slower 53 | ``` 54 | 55 | ## Development 56 | 57 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 58 | 59 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 60 | 61 | ## Updating libpsl 62 | 63 | Libpsl binaries are bundled with the gem. The current version is checked out as a submodule in `./libpsl/`. To update, checkout a later commit 64 | in the submodule (ideally matching an upstream release tag). You will then need to rebuild the binaries for all architectures. This requires running `./build-libpsl.sh` in each environment. Depending on your setup, you may be able to accomplish that via virtual machines and/or Docker. 65 | 66 | Check the comments in `./build-libpsl.sh` for more information. 67 | 68 | The submodule bump and updated binaries should be checked in to the repository. 69 | 70 | ## Contributing 71 | 72 | Bug reports and pull requests are welcome on GitHub at https://github.com/discourse/mini_suffix. 73 | 74 | ## License 75 | 76 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 77 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /benchmark/bench.rb: -------------------------------------------------------------------------------- 1 | require 'memory_profiler' 2 | require 'benchmark/ips' 3 | require 'public_suffix' 4 | require 'mini_suffix' 5 | 6 | public_suffix_mem = MemoryProfiler.report do 7 | 100.times { PublicSuffix.domain("www.careers.gov.sg") } 8 | end 9 | 10 | mini_suffix_mem = MemoryProfiler.report do 11 | 100.times { MiniSuffix.domain("www.careers.gov.sg") } 12 | end 13 | 14 | puts "PublicSuffix.domain total allocated memsize: #{public_suffix_mem.total_allocated_memsize}" 15 | puts "PublicSuffix.domain total retained memsize: #{public_suffix_mem.total_retained_memsize}" 16 | puts "MiniSuffix.domain total allocated memsize: #{mini_suffix_mem.total_allocated_memsize}" 17 | puts "MiniSuffix.domain total retained memsize: #{mini_suffix_mem.total_retained_memsize}" 18 | puts 19 | 20 | Benchmark.ips do |x| 21 | x.report('PublicSuffix.domain') { PublicSuffix.domain("www.careers.gov.sg") } 22 | x.report('MiniSuffix.domain') { MiniSuffix.domain("www.careers.gov.sg") } 23 | x.compare! 24 | end 25 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "mini_suffix" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /build-libpsl.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | export DEBIAN_FRONTEND=noninteractive 5 | cd $(dirname ${BASH_SOURCE[0]})/libpsl 6 | 7 | # A rough script to build libpsl from source, and move the binary to the /vendor directory. 8 | # 9 | # On linux, you will need a number of dependencies: 10 | # apt-get update 11 | # apt-get install -y meson gcc clang libtool llvm-dev valgrind python3 python3-setuptools libicu-dev 12 | # 13 | # On macOS, run this script in the homebrew build environment using `brew sh -c ./build-libpsl.sh` 14 | # 15 | # If you're running this, make sure to update the binaries for all other architectures as well 16 | 17 | rm -rf builddir 18 | meson builddir --buildtype=release -Druntime=no -Dbuiltin=libicu 19 | ninja -C builddir 20 | ninja -C builddir test 21 | 22 | if [[ "$OSTYPE" == "darwin"* ]]; then 23 | cp builddir/src/libpsl.dylib ../vendor/libpsl.darwin-$(arch).dylib 24 | else 25 | cp builddir/src/libpsl.so ../vendor/libpsl.$(arch).so 26 | fi 27 | 28 | echo "Done $(arch)" 29 | -------------------------------------------------------------------------------- /lib/mini_suffix.rb: -------------------------------------------------------------------------------- 1 | require "mini_suffix/version" 2 | require 'ffi' 3 | 4 | module MiniSuffix 5 | extend FFI::Library 6 | 7 | ffi_lib [ 8 | File.join(File.dirname(File.expand_path('..', __FILE__)), 'vendor/libpsl.aarch64.so'), 9 | File.join(File.dirname(File.expand_path('..', __FILE__)), 'vendor/libpsl.x86_64.so'), 10 | File.join(File.dirname(File.expand_path('..', __FILE__)), 'vendor/libpsl.ppc64le.so'), 11 | File.join(File.dirname(File.expand_path('..', __FILE__)), 'vendor/libpsl.darwin-i386.dylib'), 12 | File.join(File.dirname(File.expand_path('..', __FILE__)), 'vendor/libpsl.darwin-arm64.dylib'), 13 | ] 14 | 15 | attach_function :psl_builtin, [], :pointer 16 | attach_function :psl_registrable_domain, [:pointer, :string], :string 17 | attach_function :psl_get_version, [], :string 18 | 19 | @@context = psl_builtin 20 | 21 | # Extracts the shortest private suffix part of the hostname 22 | # 23 | # @param [String] hostname 24 | # @return [String] private_suffix 25 | def self.domain(hostname) 26 | self.psl_registrable_domain(@@context, hostname) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/mini_suffix/version.rb: -------------------------------------------------------------------------------- 1 | module MiniSuffix 2 | VERSION = "0.3.3" 3 | end 4 | -------------------------------------------------------------------------------- /mini_suffix.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "mini_suffix/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "mini_suffix" 8 | spec.version = MiniSuffix::VERSION 9 | spec.authors = ["Guo Xiang Tan"] 10 | spec.email = ["tgx@discourse.org"] 11 | 12 | spec.summary = %q{FFI wrapper for libpsl} 13 | spec.homepage = "https://github.com/discourse/mini_suffix" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 17 | f.match(%r{^(test|spec|features)/}) 18 | end 19 | 20 | spec.bindir = "exe" 21 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 22 | spec.require_paths = ["lib"] 23 | 24 | spec.add_dependency 'ffi', '~> 1.9' 25 | 26 | spec.add_development_dependency "bundler" 27 | spec.add_development_dependency "rake", "> 10.0" 28 | spec.add_development_dependency "rspec", "> 3.0" 29 | end 30 | -------------------------------------------------------------------------------- /spec/mini_suffix_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe MiniSuffix do 2 | describe '#domain' do 3 | it 'should return the right private suffix' do 4 | expect(MiniSuffix.domain('meta.discourse.org')).to eq('discourse.org') 5 | expect(MiniSuffix.domain('www.careers.gov.sg')).to eq('careers.gov.sg') 6 | expect(MiniSuffix.domain(nil)).to eq(nil) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "mini_suffix" 3 | 4 | RSpec.configure do |config| 5 | # Enable flags like --only-failures and --next-failure 6 | config.example_status_persistence_file_path = ".rspec_status" 7 | 8 | # Disable RSpec exposing methods globally on `Module` and `main` 9 | config.disable_monkey_patching! 10 | 11 | config.expect_with :rspec do |c| 12 | c.syntax = :expect 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /vendor/libpsl.aarch64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discourse/mini_suffix/d5821123c8ddd22e095164a4cf667ab8f8ef0101/vendor/libpsl.aarch64.so -------------------------------------------------------------------------------- /vendor/libpsl.darwin-arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discourse/mini_suffix/d5821123c8ddd22e095164a4cf667ab8f8ef0101/vendor/libpsl.darwin-arm64.dylib -------------------------------------------------------------------------------- /vendor/libpsl.darwin-i386.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discourse/mini_suffix/d5821123c8ddd22e095164a4cf667ab8f8ef0101/vendor/libpsl.darwin-i386.dylib -------------------------------------------------------------------------------- /vendor/libpsl.ppc64le.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discourse/mini_suffix/d5821123c8ddd22e095164a4cf667ab8f8ef0101/vendor/libpsl.ppc64le.so -------------------------------------------------------------------------------- /vendor/libpsl.x86_64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discourse/mini_suffix/d5821123c8ddd22e095164a4cf667ab8f8ef0101/vendor/libpsl.x86_64.so --------------------------------------------------------------------------------