├── .gitignore ├── lib └── activerecord │ ├── enum_translation │ └── version.rb │ └── enum_translation.rb ├── test ├── test_helper.rb └── activerecord │ └── enum_translation_test.rb ├── bin ├── setup └── console ├── Gemfile ├── Rakefile ├── .github └── workflows │ └── test.yml ├── CHANGELOG.md ├── Gemfile.lock ├── LICENSE.txt ├── activerecord-enum_translation.gemspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /lib/activerecord/enum_translation/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module EnumTranslation 3 | VERSION = "0.2.1" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path("../lib", __dir__) 2 | require "activerecord/enum_translation" 3 | 4 | require "minitest/autorun" 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in activerecord-enum_translation.gemspec 4 | gemspec 5 | 6 | gem "rake", "~> 12.0" 7 | gem "minitest", "~> 5.0" 8 | gem "sqlite3" 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/*_test.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "activerecord/enum_translation" 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 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | ruby: 15 | - 2.7.2 16 | - 3.0.2 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Ruby 20 | uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: ${{ matrix.ruby }} 23 | bundler-cache: true 24 | - name: Run the default task 25 | run: bundle exec rake 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ## 0.2.1 - 2022-03-13 6 | 7 | ### Fixed 8 | 9 | - Fix keyword arguments handling for Ruby 3.0. 10 | 11 | ## 0.2.0 - 2020-09-12 12 | 13 | ### Added 14 | 15 | - Add `:count` option. 16 | - Add `"attributes.#{enum_name}.#{enum_value}"` key. 17 | - Add humanized enum value as default value. 18 | - Support `i18n_scope` override. 19 | 20 | ### Fixed 21 | 22 | - Fix broken `:default` option on `#human_enum_name_reader_for`. 23 | 24 | ## 0.1.0 - 2020-09-12 25 | 26 | ### Added 27 | 28 | - Add 1st implementation. 29 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | activerecord-enum_translation (0.2.1) 5 | activerecord 6 | activesupport 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (7.0.2.3) 12 | activesupport (= 7.0.2.3) 13 | activerecord (7.0.2.3) 14 | activemodel (= 7.0.2.3) 15 | activesupport (= 7.0.2.3) 16 | activesupport (7.0.2.3) 17 | concurrent-ruby (~> 1.0, >= 1.0.2) 18 | i18n (>= 1.6, < 2) 19 | minitest (>= 5.1) 20 | tzinfo (~> 2.0) 21 | concurrent-ruby (1.1.9) 22 | i18n (1.10.0) 23 | concurrent-ruby (~> 1.0) 24 | minitest (5.14.2) 25 | rake (12.3.3) 26 | sqlite3 (1.4.2) 27 | tzinfo (2.0.4) 28 | concurrent-ruby (~> 1.0) 29 | 30 | PLATFORMS 31 | ruby 32 | 33 | DEPENDENCIES 34 | activerecord-enum_translation! 35 | minitest (~> 5.0) 36 | rake (~> 12.0) 37 | sqlite3 38 | 39 | BUNDLED WITH 40 | 2.1.4 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Ryo Nakamura 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /activerecord-enum_translation.gemspec: -------------------------------------------------------------------------------- 1 | require_relative 'lib/activerecord/enum_translation/version' 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "activerecord-enum_translation" 5 | spec.version = ActiveRecord::EnumTranslation::VERSION 6 | spec.licenses = ["MIT"] 7 | spec.authors = ["Ryo Nakamura"] 8 | spec.email = ["r7kamura@gmail.com"] 9 | 10 | spec.summary = "Provides integration between ActiveRecord::Enum and I18n." 11 | spec.homepage = "https://github.com/r7kamura/activerecord-enum_translation" 12 | spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") 13 | 14 | spec.metadata["homepage_uri"] = spec.homepage 15 | spec.metadata["source_code_uri"] = spec.homepage 16 | spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md" 17 | 18 | # Specify which files should be added to the gem when it is released. 19 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 20 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 21 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 22 | end 23 | spec.bindir = "exe" 24 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 25 | spec.require_paths = ["lib"] 26 | 27 | spec.add_runtime_dependency "activerecord" 28 | spec.add_runtime_dependency "activesupport" 29 | end 30 | -------------------------------------------------------------------------------- /test/activerecord/enum_translation_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | require "active_record" 3 | 4 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 5 | 6 | ActiveRecord::Schema.define do 7 | create_table :users, force: true do |t| 8 | t.integer :status, null: false 9 | end 10 | end 11 | 12 | class User < ActiveRecord::Base 13 | include ::ActiveRecord::EnumTranslation 14 | 15 | enum( 16 | status: %i[ 17 | active 18 | inactive 19 | pending 20 | ] 21 | ) 22 | human_enum_name_reader_for :status 23 | end 24 | 25 | I18n.backend.store_translations( 26 | :en, 27 | YAML.load(<<-YAML) 28 | activerecord: 29 | attributes: 30 | user: 31 | status: 32 | active: Active 33 | inactive: Inactive 34 | common: 35 | unknown: Unknown 36 | YAML 37 | ) 38 | 39 | class EnumTranslationTest < Minitest::Test 40 | def test_human_enum_name_for 41 | user = ::User.new(status: :active) 42 | actual = user.human_enum_name_for(:status) 43 | assert_equal actual, "Active" 44 | end 45 | 46 | def test_human_enum_name_reader_for 47 | user = ::User.new(status: :active) 48 | actual = user.human_enum_name_for_status 49 | assert_equal actual, "Active" 50 | end 51 | 52 | def test_enum_value_is_nil 53 | user = ::User.new 54 | actual = user.human_enum_name_for_status 55 | assert_nil actual 56 | end 57 | 58 | def test_default_is_nil 59 | user = ::User.new 60 | actual = user.human_enum_name_for_status(default: nil) 61 | assert_nil actual 62 | end 63 | 64 | def test_default_is_string 65 | user = ::User.new 66 | actual = user.human_enum_name_for_status(default: "default") 67 | assert_equal actual, "default" 68 | end 69 | 70 | def test_default_is_symbol 71 | user = ::User.new 72 | actual = user.human_enum_name_for_status(default: :"common.unknown") 73 | assert_equal actual, "Unknown" 74 | end 75 | 76 | def test_default_is_missing_but_enum_value_exists 77 | user = ::User.new(status: :pending) 78 | actual = user.human_enum_name_for_status 79 | assert_equal actual, "Pending" 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/activerecord/enum_translation.rb: -------------------------------------------------------------------------------- 1 | require "active_support/concern" 2 | require "activerecord/enum_translation/version" 3 | 4 | module ActiveRecord 5 | module EnumTranslation 6 | extend ::ActiveSupport::Concern 7 | 8 | # Translates enum identifiers into a more human format, such as `"Active"` instead of `:active`. 9 | # @param [Symbol] enum_name 10 | # @param [Integer] count 11 | # @param [Symbol, String, nil] default 12 | # @return [String, nil] 13 | # @example 14 | # User.new(status: :active).human_enum_name_for(:status) #=> "Active" 15 | # User.new.human_enum_name_for(:status) #=> nil 16 | # User.new.human_enum_name_for(:status, default: "Unknown") #=> "Unknown" 17 | # User.new.human_enum_name_for(:status, default: :"common.unknown") #=> "Unknown" 18 | def human_enum_name_for(enum_name, count: 1, default: nil) 19 | options = { count: count } 20 | enum_value = public_send(enum_name) 21 | scope = "#{self.class.i18n_scope}.attributes" 22 | 23 | defaults = [] 24 | if enum_value 25 | defaults += self.class.lookup_ancestors.map do |ancestor| 26 | :"#{scope}.#{ancestor.model_name.i18n_key}.#{enum_name}.#{enum_value}" 27 | end 28 | defaults << :"attributes.#{enum_name}.#{enum_value}" 29 | else 30 | defaults << nil 31 | end 32 | defaults << default if default 33 | defaults << enum_value.humanize if enum_value 34 | 35 | key = defaults.shift 36 | options[:default] = defaults.empty? ? nil : defaults 37 | ::I18n.t(key, **options) 38 | end 39 | 40 | module ClassMethods 41 | # Defines handy reader method for enum translation. 42 | # @param [Symbol] enum_name 43 | # @param [Hash] options 44 | # @example 45 | # class User < ApplicationRecord 46 | # human_enum_name_reader_for :status 47 | # end 48 | # 49 | # User.new(status: :active).human_enum_name_for_status #=> "Active" 50 | def human_enum_name_reader_for(enum_name) 51 | define_method("human_enum_name_for_#{enum_name}") do |**options| 52 | human_enum_name_for(enum_name, **options) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActiveRecord::EnumTranslation 2 | 3 | [![test](https://github.com/r7kamura/activerecord-enum_translation/actions/workflows/test.yml/badge.svg)](https://github.com/r7kamura/activerecord-enum_translation/actions/workflows/test.yml) 4 | [![Gem Version](https://badge.fury.io/rb/activerecord-enum_translation.svg)](https://rubygems.org/gems/activerecord-enum_translation) 5 | 6 | Provides integration between ActiveRecord::Enum and I18n. 7 | 8 | ## Installation 9 | 10 | Add this line to your application's Gemfile: 11 | 12 | ```ruby 13 | gem 'activerecord-enum_translation' 14 | ``` 15 | 16 | And then execute: 17 | 18 | $ bundle install 19 | 20 | Or install it yourself as: 21 | 22 | $ gem install activerecord-enum_translation 23 | 24 | ## Usage 25 | 26 | Include `ActiveRecord::EnumTranslation`: 27 | 28 | ```ruby 29 | class ApplicationRecord < ActiveRecord::Base 30 | include ActiveRecord::EnumTranslation 31 | end 32 | ``` 33 | 34 | Define enum: 35 | 36 | ```ruby 37 | class User < ApplicationRecord 38 | enum status: %i[active inactive] 39 | end 40 | ``` 41 | 42 | Add translations in your locale files: 43 | 44 | ```yaml 45 | ja: 46 | activerecord: 47 | attributes: 48 | user: 49 | status: 50 | active: 利用中 51 | inactive: 停止中 52 | ``` 53 | 54 | Get the translation by `human_enum_name_for`: 55 | 56 | ```ruby 57 | user = User.new(status: :active) 58 | user.human_enum_name_for(:status) #=> "利用中" 59 | ``` 60 | 61 | This gem also provides `human_enum_name_reader_for`: 62 | 63 | ```ruby 64 | class User < ApplicationRecord 65 | enum status: %i[active inactive] 66 | human_enum_name_reader_for :status 67 | end 68 | ``` 69 | 70 | Finally you can use `human_enum_name_for_status`: 71 | 72 | ```ruby 73 | user = User.new(status: :active) 74 | user.human_enum_name_for_status #=> "利用中" 75 | ``` 76 | 77 | ## Development 78 | 79 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 80 | 81 | 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). 82 | 83 | ## Contributing 84 | 85 | Bug reports and pull requests are welcome on GitHub at https://github.com/r7kamura/activerecord-enum_translation. 86 | 87 | --------------------------------------------------------------------------------