├── VERSION ├── .document ├── spec ├── spec_helper.rb └── static_list_spec.rb ├── test ├── test_static_list.rb └── helper.rb ├── Gemfile ├── Gemfile.lock ├── .gitignore ├── lib ├── core_ext │ └── concern.rb └── static_list.rb ├── LICENSE.txt ├── Rakefile ├── README.rdoc └── static_list.gemspec /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.2 -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require "active_support/core_ext" 3 | 4 | require File.join(File.dirname(__FILE__), '..', 'lib', 'static_list') 5 | 6 | RSpec.configure do |config| 7 | config.mock_with :rspec 8 | end 9 | 10 | -------------------------------------------------------------------------------- /test/test_static_list.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestStaticList < Test::Unit::TestCase 4 | should "probably rename this file and start testing for real" do 5 | flunk "hey buddy, you should probably rename this file and start testing for real" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | # Add dependencies required to use your gem here. 3 | # Example: 4 | # gem "activesupport", ">= 2.3.5" 5 | 6 | gem 'activesupport', '>= 2.3.4' 7 | 8 | # Add dependencies to develop your gem here. 9 | # Include everything needed to run rake, tests, features, etc. 10 | group :development do 11 | gem "shoulda", ">= 0" 12 | gem "bundler", "~> 1.0.0" 13 | gem "jeweler", "~> 1.5.2" 14 | gem "rcov", ">= 0" 15 | gem 'rspec', '>= 2.5.0' 16 | gem 'i18n' 17 | end 18 | 19 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :development) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'test/unit' 11 | require 'shoulda' 12 | 13 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 14 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 15 | require 'static_list' 16 | 17 | class Test::Unit::TestCase 18 | end 19 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activesupport (3.0.5) 5 | diff-lcs (1.1.2) 6 | git (1.2.5) 7 | i18n (0.5.0) 8 | jeweler (1.5.2) 9 | bundler (~> 1.0.0) 10 | git (>= 1.2.5) 11 | rake 12 | rake (0.8.7) 13 | rcov (0.9.9) 14 | rspec (2.5.0) 15 | rspec-core (~> 2.5.0) 16 | rspec-expectations (~> 2.5.0) 17 | rspec-mocks (~> 2.5.0) 18 | rspec-core (2.5.1) 19 | rspec-expectations (2.5.0) 20 | diff-lcs (~> 1.1.2) 21 | rspec-mocks (2.5.0) 22 | shoulda (2.11.3) 23 | 24 | PLATFORMS 25 | ruby 26 | 27 | DEPENDENCIES 28 | activesupport (>= 2.3.4) 29 | bundler (~> 1.0.0) 30 | i18n 31 | jeweler (~> 1.5.2) 32 | rcov 33 | rspec (>= 2.5.0) 34 | shoulda 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | 4 | # rdoc generated 5 | rdoc 6 | 7 | # yard generated 8 | doc 9 | .yardoc 10 | 11 | # bundler 12 | .bundle 13 | 14 | # jeweler generated 15 | pkg 16 | 17 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 18 | # 19 | # * Create a file at ~/.gitignore 20 | # * Include files you want ignored 21 | # * Run: git config --global core.excludesfile ~/.gitignore 22 | # 23 | # After doing this, these files will be ignored in all your git projects, 24 | # saving you from having to 'pollute' every project you touch with them 25 | # 26 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 27 | # 28 | # For MacOS: 29 | # 30 | #.DS_Store 31 | # 32 | # For TextMate 33 | #*.tmproj 34 | #tmtags 35 | # 36 | # For emacs: 37 | #*~ 38 | #\#* 39 | #.\#* 40 | # 41 | # For vim: 42 | #*.swp 43 | -------------------------------------------------------------------------------- /lib/core_ext/concern.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | module Concern 3 | def self.extended(base) 4 | base.instance_variable_set("@_dependencies", []) 5 | end 6 | 7 | def append_features(base) 8 | if base.instance_variable_defined?("@_dependencies") 9 | base.instance_variable_get("@_dependencies") << self 10 | return false 11 | else 12 | return false if base < self 13 | @_dependencies.each { |dep| base.send(:include, dep) } 14 | super 15 | base.extend const_get("ClassMethods") if const_defined?("ClassMethods") 16 | base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods") 17 | base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") 18 | end 19 | end 20 | 21 | def included(base = nil, &block) 22 | if base.nil? 23 | @_included_block = block 24 | else 25 | super 26 | end 27 | end 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011 Novelys 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 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :development) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'rake' 11 | 12 | require 'jeweler' 13 | Jeweler::Tasks.new do |gem| 14 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 15 | gem.name = "static_list" 16 | gem.homepage = "http://github.com/novelys/static_list" 17 | gem.license = "MIT" 18 | gem.summary = %q{This gem is very useful to handle static lists (like enumerations).} 19 | gem.description = %q{ 20 | In your application you may want to handle things in your User model like sex (female, male) or other static lists. 21 | You want these lists to be handled using 'textual keys' in your application but stored in your database using codes in an integer column. 22 | } 23 | gem.email = "yann.klis@novelys.com" 24 | gem.authors = ["Yann Klis"] 25 | # Include your dependencies below. Runtime dependencies are required when using your gem, 26 | # and development dependencies are only needed for development (ie running rake tasks, tests, etc) 27 | # gem.add_runtime_dependency 'jabber4r', '> 0.1' 28 | # gem.add_development_dependency 'rspec', '> 1.2.3' 29 | gem.add_runtime_dependency 'activesupport', '>= 2.3.4' 30 | end 31 | Jeweler::RubygemsDotOrgTasks.new 32 | 33 | require "rspec" 34 | require "rspec/core/rake_task" 35 | 36 | Rspec::Core::RakeTask.new(:spec) do |spec| 37 | spec.pattern = "spec/**/*_spec.rb" 38 | end 39 | 40 | task :default => :spec 41 | 42 | require 'rake/rdoctask' 43 | Rake::RDocTask.new do |rdoc| 44 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 45 | 46 | rdoc.rdoc_dir = 'rdoc' 47 | rdoc.title = "static_list #{version}" 48 | rdoc.rdoc_files.include('README*') 49 | rdoc.rdoc_files.include('lib/**/*.rb') 50 | end 51 | 52 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Static List 2 | 3 | This module is very useful to handle static lists (like enumerations). 4 | 5 | == The problem: 6 | 7 | In your application you may want to handle things in your User model like sex (female, male) or other static lists. 8 | You want these lists to be handled using 'textual keys' in your application but stored in your database using codes in an integer column. 9 | You don't want to join other tables to display these information. 10 | You want these lists to be easily ordered, localized and translated using Rails i18n. You want view helpers to display these lists localized and validations helpers to validate the values in the 'receiving' model. 11 | 12 | Example : 13 | (I want to store the hair color of the user...) 14 | 15 | (hair_color.rb) 16 | class HairColor 17 | include StaticList::Model 18 | static_list [[:white, 1], [:blond, 2], [:red, 3], [:light_brown, 4], [:brown, 5], [:black, 6], [:colored, 7], [:bald, 8]] 19 | end 20 | 21 | (user.rb) 22 | class User < ActiveRecord::Base 23 | ... 24 | include StaticList::Validate 25 | 26 | validates_static_list_value :hair_color, HairColor, :allow_blank => true 27 | ... 28 | end 29 | 30 | (application_helper.rb) 31 | module ApplicationHelper 32 | ... 33 | include StaticList::Helpers 34 | ... 35 | end 36 | 37 | (_form.html.erb) 38 | ... 39 | <%= f.select :hair_color, static_list_select_options(HairColor) %> 40 | ... 41 | 42 | (show.html.erb) 43 | ... 44 | <%= t_static_list(@user.hair_color, HairColor) %> 45 | ... 46 | 47 | (en.yml) 48 | ... 49 | hair_color: 50 | white: white 51 | blond: blond 52 | red: red 53 | ... 54 | 55 | (fr.yml) 56 | ... 57 | hair_color: 58 | white: blancs 59 | blond: blonds 60 | red: rouges 61 | ... 62 | 63 | == Copyright 64 | 65 | Copyright (c) 2010-2011 Novelys[http://www.novelys.com]. See LICENSE.txt for details. 66 | 67 | == Contributors 68 | 69 | * {Yann Klis}[http://github.com/yannski] 70 | * {Nicolas Blanco}[http://github.com/slainer68] 71 | * {David Bourguignon}[http://github.com/dbourguignon] 72 | 73 | -------------------------------------------------------------------------------- /spec/static_list_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | class HairColor 4 | include StaticList::Model 5 | include StaticList::Validate 6 | @@hair_color_list = [[:white, 1], [:blond, 2], [:red, 3], [:light_brown, 4], [:brown, 5], [:black, 6], [:colored, 7], [:bald, 8]] 7 | cattr_accessor :hair_color_list 8 | 9 | static_list @@hair_color_list 10 | end 11 | 12 | include StaticList::Helpers 13 | 14 | describe HairColor do 15 | describe ".static_list" do 16 | it "should return the static list array" do 17 | HairColor.static_list_codes.should == HairColor.hair_color_list 18 | end 19 | end 20 | 21 | describe ".code_to_sym" do 22 | it "should return the code from the symbol" do 23 | HairColor.code_to_sym(3).should == :red 24 | end 25 | end 26 | 27 | describe ".sym_to_code" do 28 | it "should return the symbol from the code" do 29 | HairColor.sym_to_code(:black).should == 6 30 | end 31 | end 32 | 33 | describe ".all" do 34 | it "should return all the list" do 35 | HairColor.all.should have(8).elements 36 | end 37 | end 38 | 39 | describe ".static_codes" do 40 | it "should return all the codes" do 41 | HairColor.static_codes.should == [:white, :blond, :red, :light_brown, :brown, :black, :colored, :bald] 42 | end 43 | end 44 | 45 | describe ".static_keys" do 46 | it "should return all the keys" do 47 | HairColor.static_keys.should == [1, 2, 3, 4, 5, 6, 7, 8] 48 | end 49 | end 50 | 51 | describe ".t_symbol" do 52 | it "should return the correct translation key" do 53 | HairColor.t_key_from_code(3).should == "hair_color.red" 54 | end 55 | end 56 | end 57 | 58 | describe "StaticList::Helpers" do 59 | describe ".t_static_list" do 60 | it "should return the translation" do 61 | StaticList::Helpers.should_receive(:t).with("hair_color.white") 62 | 63 | StaticList::Helpers.t_static_list(1, HairColor) 64 | end 65 | end 66 | 67 | describe ".static_list_select_options" do 68 | it "should return a list for a select" do 69 | StaticList::Helpers.should_receive(:t_static_list).exactly(8).times.and_return("ok") 70 | 71 | StaticList::Helpers.static_list_select_options(HairColor).should be_an(Array) 72 | end 73 | end 74 | end 75 | 76 | describe "StaticList::Validate" do 77 | describe ".validates_static_list_value" do 78 | it "should validate inclusion" do 79 | HairColor.should_receive(:validates_inclusion_of).with(:hair_color_code, { :in => [1, 2, 3, 4, 5, 6, 7, 8] }) 80 | 81 | HairColor.validates_static_list_value(:hair_color_code, HairColor) 82 | end 83 | end 84 | end 85 | 86 | -------------------------------------------------------------------------------- /static_list.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{static_list} 8 | s.version = "0.2.2" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Yann Klis"] 12 | s.date = %q{2011-05-11} 13 | s.description = %q{ 14 | In your application you may want to handle things in your User model like sex (female, male) or other static lists. 15 | You want these lists to be handled using 'textual keys' in your application but stored in your database using codes in an integer column. 16 | } 17 | s.email = %q{yann.klis@novelys.com} 18 | s.extra_rdoc_files = [ 19 | "LICENSE.txt", 20 | "README.rdoc" 21 | ] 22 | s.files = [ 23 | ".document", 24 | "Gemfile", 25 | "Gemfile.lock", 26 | "LICENSE.txt", 27 | "README.rdoc", 28 | "Rakefile", 29 | "VERSION", 30 | "lib/core_ext/concern.rb", 31 | "lib/static_list.rb", 32 | "spec/spec_helper.rb", 33 | "spec/static_list_spec.rb", 34 | "static_list.gemspec", 35 | "test/helper.rb", 36 | "test/test_static_list.rb" 37 | ] 38 | s.homepage = %q{http://github.com/novelys/static_list} 39 | s.licenses = ["MIT"] 40 | s.require_paths = ["lib"] 41 | s.rubygems_version = %q{1.5.0} 42 | s.summary = %q{This gem is very useful to handle static lists (like enumerations).} 43 | s.test_files = [ 44 | "spec/spec_helper.rb", 45 | "spec/static_list_spec.rb", 46 | "test/helper.rb", 47 | "test/test_static_list.rb" 48 | ] 49 | 50 | if s.respond_to? :specification_version then 51 | s.specification_version = 3 52 | 53 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 54 | s.add_runtime_dependency(%q, [">= 2.3.4"]) 55 | s.add_development_dependency(%q, [">= 0"]) 56 | s.add_development_dependency(%q, ["~> 1.0.0"]) 57 | s.add_development_dependency(%q, ["~> 1.5.2"]) 58 | s.add_development_dependency(%q, [">= 0"]) 59 | s.add_development_dependency(%q, [">= 2.5.0"]) 60 | s.add_development_dependency(%q, [">= 0"]) 61 | s.add_runtime_dependency(%q, [">= 2.3.4"]) 62 | else 63 | s.add_dependency(%q, [">= 2.3.4"]) 64 | s.add_dependency(%q, [">= 0"]) 65 | s.add_dependency(%q, ["~> 1.0.0"]) 66 | s.add_dependency(%q, ["~> 1.5.2"]) 67 | s.add_dependency(%q, [">= 0"]) 68 | s.add_dependency(%q, [">= 2.5.0"]) 69 | s.add_dependency(%q, [">= 0"]) 70 | s.add_dependency(%q, [">= 2.3.4"]) 71 | end 72 | else 73 | s.add_dependency(%q, [">= 2.3.4"]) 74 | s.add_dependency(%q, [">= 0"]) 75 | s.add_dependency(%q, ["~> 1.0.0"]) 76 | s.add_dependency(%q, ["~> 1.5.2"]) 77 | s.add_dependency(%q, [">= 0"]) 78 | s.add_dependency(%q, [">= 2.5.0"]) 79 | s.add_dependency(%q, [">= 0"]) 80 | s.add_dependency(%q, [">= 2.3.4"]) 81 | end 82 | end 83 | 84 | -------------------------------------------------------------------------------- /lib/static_list.rb: -------------------------------------------------------------------------------- 1 | require "active_support" 2 | require "active_support/version" 3 | 4 | if ActiveSupport::VERSION::MAJOR < 3 5 | require "core_ext/concern" 6 | end 7 | 8 | class Hashit 9 | def initialize(hash) 10 | hash.each do |k,v| 11 | self.instance_variable_set("@#{k}", v) ## create and initialize an instance variable for this key/value pair 12 | self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")}) ## create the getter that returns the instance variable 13 | self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)}) ## create the setter that sets the instance variable 14 | end 15 | end 16 | end 17 | 18 | module StaticList 19 | module Validate 20 | extend ActiveSupport::Concern 21 | 22 | module ClassMethods 23 | # Method to validate in the receiving model that the value received is included in the static list model. 24 | # For example : 25 | # with_options(:allow_blank => true) do |options| 26 | # options.validate_static_list_value :hair_color, HairColor 27 | # options.validate_static_list_value :ethnicity, Ethnicity 28 | # options.validate_static_list_value :sex, Sex 29 | # end 30 | def validates_static_list_value(attribute, model, options = {}) 31 | options.merge!(:in => model.static_list_codes.map { |el| el[1] }) 32 | validates_inclusion_of attribute, options 33 | end 34 | end 35 | end 36 | 37 | module Model 38 | extend ActiveSupport::Concern 39 | 40 | included do 41 | cattr_accessor :static_list_codes 42 | end 43 | 44 | module ClassMethods 45 | # Returns all elements of the static list 46 | def all 47 | static_list_codes.map{|x| Hashit.new(Hash[*["name", I18n.t("#{self.to_s.demodulize.underscore}.#{x.first.to_s}"), "id", x.last]])} 48 | end 49 | 50 | # Method to declare the static list in the static list model. 51 | def static_list(list) 52 | self.static_list_codes = list 53 | end 54 | 55 | # Returns the symbol associated with the code in parameter. 56 | # 57 | # For example : HairColor.code_to_sym(0) # => :white 58 | # 59 | def code_to_sym(code) 60 | static_list_codes.find { |el| el[1] == code }[0] 61 | end 62 | 63 | # Returns the code associated with the symbol in parameter. 64 | # 65 | # For example : HairColor.sym_to_code(:white) # => 0 66 | # 67 | def sym_to_code(sym) 68 | static_list_codes.find {|el| el[0] == sym }[1] 69 | end 70 | 71 | def t_key_from_code(code) 72 | "#{self.to_s.demodulize.underscore}.#{self.code_to_sym(code)}" 73 | end 74 | 75 | def static_codes 76 | static_list_codes.map{ |e| e[0] } 77 | end 78 | 79 | def static_keys 80 | static_list_codes.map{ |e| e[1] } 81 | end 82 | end 83 | end 84 | 85 | module Helpers 86 | # Localizes a static code 87 | # For example : 88 | # t_static_list(@user.hair_color, HairColor) 89 | # 90 | # will read the key hair_color.white 91 | # 92 | def t_static_list(code, static_object) 93 | return unless code 94 | t(static_object.t_key_from_code(code)) 95 | end 96 | 97 | # Localizes all the static codes for select options helper 98 | # 99 | # Example : 100 | # f.select :hair_color, static_list_select_options(HairColor) 101 | # 102 | def static_list_select_options(static_object) 103 | static_object.static_list_codes.map { |code| [t_static_list(code[1], static_object), code[1]] } 104 | end 105 | end 106 | end 107 | --------------------------------------------------------------------------------