├── .gitignore ├── Gemfile ├── test ├── models │ ├── user.rb │ └── shape.rb ├── test_helper.rb ├── test_select_options.rb ├── test_active_model_translation.rb └── test_active_record_base.rb ├── lib ├── select_options │ ├── version.rb │ ├── select_options.rb │ ├── active_record │ │ └── base.rb │ └── active_model │ │ └── translation.rb └── select_options.rb ├── Rakefile ├── select_options.gemspec ├── LICENSE.txt ├── Gemfile.lock └── README.rdoc /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | install.rb -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /test/models/user.rb: -------------------------------------------------------------------------------- 1 | class User 2 | extend ActiveModel::Translation 3 | end 4 | -------------------------------------------------------------------------------- /lib/select_options/version.rb: -------------------------------------------------------------------------------- 1 | module SelectOptions 2 | VERSION = "1.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new do |t| 4 | t.libs << 'test' 5 | end 6 | 7 | task :default => :test 8 | -------------------------------------------------------------------------------- /test/models/shape.rb: -------------------------------------------------------------------------------- 1 | class Shape < ActiveRecord::Base 2 | DIMENTION_VALUES = [1, 2] 3 | DIMENTIONS = [3, 4] 4 | WRONG = "some value" 5 | 6 | def attributes_from_column_definition 7 | { "id" => nil } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "bundler/setup" 3 | require "test/unit" 4 | require "active_record" 5 | require "select_options" 6 | 7 | $:.push File.expand_path("../", __FILE__) 8 | require "models/user" 9 | require "models/shape" 10 | -------------------------------------------------------------------------------- /lib/select_options.rb: -------------------------------------------------------------------------------- 1 | require "select_options/active_model/translation" 2 | require "select_options/active_record/base" 3 | require "action_controller" 4 | require "select_options/select_options" 5 | 6 | ActionView::Helpers::FormBuilder.send :include, SelectOptions 7 | -------------------------------------------------------------------------------- /test/test_select_options.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SelectOptionsTests < ActionView::TestCase 4 | def setup 5 | @shape = Shape.new 6 | end 7 | 8 | def test_select_with_options 9 | fields_for :shape, @shape do |f| 10 | f.expects(:select).with(:dimention, [["3D", 3], ["4D", 4]], {}, {}) 11 | f.select_with_options :dimention 12 | end 13 | end 14 | 15 | def test_select_with_options_with_source 16 | fields_for :shape, @shape do |f| 17 | f.expects(:select).with(:dimention, [["1D", 1], ["2D", 2]], {}, {}) 18 | f.select_with_options :dimention, :source => Shape::DIMENTION_VALUES 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/select_options/select_options.rb: -------------------------------------------------------------------------------- 1 | module SelectOptions 2 | # Create a select tag and a series of contained option tags for the provided object and method. 3 | # Helper would take options from model's constant and translations to them from translations file. 4 | # For example with @user: 5 | # class User < ActiveRecord::Base 6 | # ROLES = %w(admin user) 7 | # ... 8 | # end 9 | # 10 | # in the template: 11 | # f.select_with_options :role 12 | # is the same that: 13 | # f.select :role, User::ROLES.map { |value| [translation_for_this_value, value] } 14 | # 15 | # This helper takes parameters like a rails select. 16 | def select_with_options(method, options = {}, html_options = {}) 17 | const_name = options.delete(:source) 18 | select(method, @object.attribute_options(method, const_name), options, html_options) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/select_options/active_record/base.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | class Base 3 | def self.constant_for_attribute(name) 4 | constant_name = name.to_s.pluralize.upcase 5 | const_get(constant_name) if const_defined?(constant_name) 6 | end 7 | 8 | def self.attribute_options(name, source_constant = nil) 9 | constant = source_constant.presence || constant_for_attribute(name) 10 | constant.map { |value| [human_composite_attribute_name(name, value), value] } if constant.present? && constant.is_a?(Array) 11 | end 12 | 13 | def self.composite_attribute_name?(name) 14 | attribute_options(name).present? 15 | end 16 | 17 | def attribute_options(name, source_constant = nil) 18 | self.class.attribute_options(name, source_constant) 19 | end 20 | 21 | def composite_attribute_name?(name) 22 | self.class.composite_attribute_name?(name) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /select_options.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require "select_options/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "select_options" 6 | s.version = SelectOptions::VERSION 7 | s.date = "2011-10-04" 8 | s.authors = ["Dmitriy Vorotilin"] 9 | s.email = "mydeeptown@gmail.com" 10 | s.homepage = "https://github.com/route/select_options" 11 | s.summary = "select helper with translated options for Rails form_for." 12 | s.homepage = "https://github.com/route/select_options" 13 | s.description = "Provides options for rails form_for select helper. It uses some conventions for your models." 14 | 15 | s.rubyforge_project = "select_options" 16 | 17 | s.files = Dir["Gemfile", "Gemfile.lock", "README.rdoc", "LICENSE.txt", "Rakefile", "lib/**/*"] 18 | s.test_files = Dir["test/**/*"] 19 | s.require_paths = ["lib"] 20 | 21 | %w{ activemodel activerecord actionpack }.each do |gem| 22 | s.add_runtime_dependency gem, ">= 3.0.0" 23 | end 24 | 25 | s.add_development_dependency "mocha" 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Dmitriy Vorotilin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/select_options/active_model/translation.rb: -------------------------------------------------------------------------------- 1 | module ActiveModel 2 | module Translation 3 | # Transforms composite attribute names into a humanized format. 4 | # Translations in en.yml for an ActiveRecord child looks like this: 5 | # en: 6 | # activerecord: 7 | # composite_attributes: 8 | # user: 9 | # role: 10 | # admin: Administrator 11 | # user: User 12 | # 13 | # User.human_composite_attribute_name("role", "user") # => "User" 14 | # or 15 | # en: 16 | # activerecord: 17 | # attributes: 18 | # user: 19 | # role_admin: Administrator 20 | # role_user: User 21 | # 22 | # User.human_composite_attribute_name("role", "admin") # => "Administrator" 23 | # Specify +options+ with additional translating options. 24 | def human_composite_attribute_name(attribute, value, options = {}) 25 | return "" unless value.present? 26 | default = :"#{i18n_scope}.composite_attributes.#{model_name.i18n_key}.#{attribute}.#{value}" 27 | human_attribute_name "#{attribute}_#{value}", options.reverse_merge(:default => default) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/test_active_model_translation.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ActiveModelTranslationTests < ActiveModel::TestCase 4 | def setup 5 | I18n.backend = I18n::Backend::Simple.new 6 | I18n.backend.store_translations 'en', :activemodel => { :composite_attributes => { :user => { :role => { :admin => "Administrator", :user => "User" } } } } 7 | I18n.backend.store_translations 'en', :activemodel => { :attributes => { :user => { :role_guest => "Guest" } } } 8 | end 9 | 10 | def test_translated_human_composite_attribute_name 11 | assert_equal "Administrator", User.human_composite_attribute_name("role", "admin") 12 | assert_equal "User", User.human_composite_attribute_name("role", "user") 13 | end 14 | 15 | def test_translated_human_composite_attribute_name_with_empty_value 16 | assert_equal "", User.human_composite_attribute_name("role", "") 17 | end 18 | 19 | def test_translated_human_composite_attribute_name_with_nil_value 20 | assert_equal "", User.human_composite_attribute_name("role", nil) 21 | end 22 | 23 | def test_translated_human_composite_attribute_name_in_fallback_location 24 | assert_equal "Guest", User.human_composite_attribute_name("role", "guest") 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | select_options (1.1.0) 5 | actionpack (>= 3.0.0) 6 | activemodel (>= 3.0.0) 7 | activerecord (>= 3.0.0) 8 | 9 | GEM 10 | remote: http://rubygems.org/ 11 | specs: 12 | actionpack (3.1.3) 13 | activemodel (= 3.1.3) 14 | activesupport (= 3.1.3) 15 | builder (~> 3.0.0) 16 | erubis (~> 2.7.0) 17 | i18n (~> 0.6) 18 | rack (~> 1.3.5) 19 | rack-cache (~> 1.1) 20 | rack-mount (~> 0.8.2) 21 | rack-test (~> 0.6.1) 22 | sprockets (~> 2.0.3) 23 | activemodel (3.1.3) 24 | activesupport (= 3.1.3) 25 | builder (~> 3.0.0) 26 | i18n (~> 0.6) 27 | activerecord (3.1.3) 28 | activemodel (= 3.1.3) 29 | activesupport (= 3.1.3) 30 | arel (~> 2.2.1) 31 | tzinfo (~> 0.3.29) 32 | activesupport (3.1.3) 33 | multi_json (~> 1.0) 34 | arel (2.2.1) 35 | builder (3.0.0) 36 | erubis (2.7.0) 37 | hike (1.2.1) 38 | i18n (0.6.0) 39 | metaclass (0.0.1) 40 | mocha (0.10.3) 41 | metaclass (~> 0.0.1) 42 | multi_json (1.0.4) 43 | rack (1.3.6) 44 | rack-cache (1.1) 45 | rack (>= 0.4) 46 | rack-mount (0.8.3) 47 | rack (>= 1.0.0) 48 | rack-test (0.6.1) 49 | rack (>= 1.0) 50 | sprockets (2.0.3) 51 | hike (~> 1.2) 52 | rack (~> 1.0) 53 | tilt (~> 1.1, != 1.3.0) 54 | tilt (1.3.3) 55 | tzinfo (0.3.31) 56 | 57 | PLATFORMS 58 | ruby 59 | 60 | DEPENDENCIES 61 | mocha 62 | select_options! 63 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Select Options 2 | 3 | This gem creates a select tag and a series of contained option tags for the provided object and method. 4 | Helper takes options from model's constant and translates it according to the translations file (`composite_attributes` section). 5 | 6 | = Installation 7 | 8 | In Gemfile: 9 | gem 'select_options' 10 | 11 | = Usage example 12 | 13 | For example, you have a model User with the field role and a constant ROLES: 14 | class User < ActiveRecord::Base 15 | ROLES = %w(admin user) 16 | ... 17 | end 18 | 19 | in the template you have just to write: 20 | <%= f.select_with_options :role %> 21 | 22 | In case if you have the constant, named different from the field name, like this: 23 | class Shape < ActiveRecord::Base 24 | DIMENTION_VALUES = [2, 3] 25 | ... 26 | end 27 | in the template you should use option :source 28 | <%= f.select_with_options :dimention, :source => Shape::DIMENTION_VALUES %> 29 | 30 | Translations in en.yml for an ActiveRecord child look like this: 31 | en: 32 | activerecord: 33 | composite_attributes: 34 | user: 35 | role: 36 | admin: Administrator 37 | user: User 38 | or like this: 39 | en: 40 | activerecord: 41 | attributes: 42 | shape: 43 | dimention_2: 2D 44 | dimention_3: 3D 45 | 46 | This code will generate translated collection of options for select tag: 47 | 51 | 52 | == Copyright 53 | 54 | Copyright (c) 2011 Dmitriy Vorotilin, {Evrone.com}[http://evrone.com]. See LICENSE.txt for further details. 55 | -------------------------------------------------------------------------------- /test/test_active_record_base.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ActiveRecordBaseTest < ActiveRecord::TestCase 4 | def setup 5 | I18n.backend = I18n::Backend::Simple.new 6 | I18n.backend.store_translations 'en', :activerecord => { :attributes => { :shape => { :dimention_1 => "1D", :dimention_2 => "2D", :dimention_3 => "3D", :dimention_4 => "4D" } } } 7 | end 8 | 9 | def test_constant_for_attribute 10 | assert_equal [3, 4], Shape.constant_for_attribute(:dimention) 11 | end 12 | 13 | def test_constant_for_attribute_with_undefined_constant 14 | assert_equal nil, Shape.constant_for_attribute(:undefined_attribute) 15 | end 16 | 17 | def test_attribute_options 18 | assert_equal [["3D", 3], ["4D", 4]], Shape.attribute_options(:dimention) 19 | end 20 | 21 | def test_attribute_options_with_specified_constant 22 | assert_equal [["1D", 1], ["2D", 2]], Shape.attribute_options(:dimention, Shape::DIMENTION_VALUES) 23 | end 24 | 25 | def test_attribute_options_with_undefined_constant 26 | assert_equal nil, Shape.attribute_options(:undefined_attribute) 27 | end 28 | 29 | def test_attribute_options_with_wrong_constant 30 | assert_equal nil, Shape.attribute_options(:some, Shape::WRONG) 31 | end 32 | 33 | def test_checking_composite_attribute_name 34 | assert_equal true, Shape.composite_attribute_name?(:dimention) 35 | end 36 | 37 | def test_checking_composite_attribute_name_for_wrong_attribute 38 | assert_equal false, Shape.composite_attribute_name?(:undefined_attribute) 39 | end 40 | 41 | def test_instance_attribute_options 42 | assert_equal [["3D", 3], ["4D", 4]], Shape.new.attribute_options(:dimention) 43 | end 44 | 45 | def test_instance_checking_composite_attribute_name 46 | assert_equal true, Shape.new.composite_attribute_name?(:dimention) 47 | end 48 | end 49 | --------------------------------------------------------------------------------