├── .gitignore ├── init.rb ├── Gemfile ├── lib ├── acts_as_label │ ├── version.rb │ └── base.rb └── acts_as_label.rb ├── Rakefile ├── test ├── test_helper.rb └── acts_as_label_test.rb ├── MIT-LICENSE ├── acts_as_label.gemspec ├── .specification ├── Gemfile.lock └── README.rdoc /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.gem -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require "acts_as_label" -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec -------------------------------------------------------------------------------- /lib/acts_as_label/version.rb: -------------------------------------------------------------------------------- 1 | module Coroutine 2 | module ActsAsLabel 3 | VERSION = "1.1.7" 4 | end 5 | end -------------------------------------------------------------------------------- /lib/acts_as_label.rb: -------------------------------------------------------------------------------- 1 | # acts_as_label extension 2 | require File.dirname(__FILE__) + "/acts_as_label/base" 3 | 4 | 5 | # add extensions to active record 6 | begin 7 | ::ActiveRecord::Base.send(:include, Coroutine::ActsAsLabel::Base) 8 | rescue 9 | # do nothing 10 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require "rake" 5 | require "rake/testtask" 6 | 7 | 8 | desc "Default: run tests." 9 | task :default => [:test] 10 | 11 | 12 | desc "Test the gem." 13 | Rake::TestTask.new(:test) do |t| 14 | t.libs << ["lib", "test"] 15 | t.pattern = "test/**/*_test.rb" 16 | t.verbose = true 17 | end -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # require rails stuff 2 | require "rubygems" 3 | require "active_record" 4 | require "active_support" 5 | require "active_support/test_case" 6 | require "test/unit" 7 | 8 | # require plugin 9 | require "#{File.dirname(__FILE__)}/../init" 10 | 11 | #---------------------------------------------------------- 12 | # Define global methods 13 | #---------------------------------------------------------- 14 | 15 | class ActiveSupport::TestCase 16 | 17 | # This method allows us to use a convenient notation for testing 18 | # model validations. 19 | def assert_not_valid(object, msg="Object is valid when it should be invalid") 20 | assert(!object.valid?, msg) 21 | end 22 | alias :assert_invalid :assert_not_valid 23 | 24 | end -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Coroutine LLC 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 | -------------------------------------------------------------------------------- /acts_as_label.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "acts_as_label/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "acts_as_label" 7 | s.version = Coroutine::ActsAsLabel::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Coroutine", "John Dugan"] 10 | s.email = ["gems@coroutine.com"] 11 | s.homepage = "http://github.com/coroutine/acts_as_label" 12 | s.summary = %q{This acts_as extension simplifies the process of assigning mutable user-friendly labels to immutable system labels.} 13 | s.description = %q{This acts_as extension implements a system label and a friendly label on a class and centralizes the logic for performing validations and accessing items by system label.} 14 | 15 | s.add_dependency "rails", ">= 3.0.0" 16 | 17 | s.add_development_dependency "rspec", ">= 2.0.0" 18 | s.add_development_dependency "sqlite3", ">= 1.3.6" 19 | 20 | s.rubyforge_project = "acts_as_label" 21 | 22 | s.files = `git ls-files`.split("\n") 23 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 24 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 25 | s.require_paths = ["lib"] 26 | end 27 | -------------------------------------------------------------------------------- /.specification: -------------------------------------------------------------------------------- 1 | --- !ruby/object:Gem::Specification 2 | name: acts_as_label 3 | version: !ruby/object:Gem::Version 4 | prerelease: 5 | version: 1.1.0 6 | platform: ruby 7 | authors: 8 | - Coroutine 9 | - John Dugan 10 | - Rick Branson 11 | autorequire: 12 | bindir: bin 13 | cert_chain: [] 14 | 15 | date: 2011-05-12 00:00:00 Z 16 | dependencies: 17 | - !ruby/object:Gem::Dependency 18 | name: activerecord 19 | prerelease: false 20 | requirement: &id001 !ruby/object:Gem::Requirement 21 | none: false 22 | requirements: 23 | - - ">=" 24 | - !ruby/object:Gem::Version 25 | version: 2.3.4 26 | type: :runtime 27 | version_requirements: *id001 28 | - !ruby/object:Gem::Dependency 29 | name: activesupport 30 | prerelease: false 31 | requirement: &id002 !ruby/object:Gem::Requirement 32 | none: false 33 | requirements: 34 | - - ">=" 35 | - !ruby/object:Gem::Version 36 | version: 2.3.4 37 | type: :development 38 | version_requirements: *id002 39 | description: This acts_as extension implements a system label and a friendly label on a class and centralizes the logic for performing validations and accessing items by system label. 40 | email: jdugan@coroutine.com 41 | executables: [] 42 | 43 | extensions: [] 44 | 45 | extra_rdoc_files: 46 | - README.rdoc 47 | files: 48 | - .specification 49 | - MIT-LICENSE 50 | - README.rdoc 51 | - Rakefile 52 | - VERSION 53 | - acts_as_label.gemspec 54 | - init.rb 55 | - lib/acts_as_label.rb 56 | - lib/acts_as_label/base.rb 57 | - rails/init.rb 58 | - test/acts_as_label_test.rb 59 | - test/test_helper.rb 60 | homepage: http://github.com/coroutine/acts_as_label 61 | licenses: [] 62 | 63 | post_install_message: 64 | rdoc_options: [] 65 | 66 | require_paths: 67 | - lib 68 | required_ruby_version: !ruby/object:Gem::Requirement 69 | none: false 70 | requirements: 71 | - - ">=" 72 | - !ruby/object:Gem::Version 73 | version: "0" 74 | required_rubygems_version: !ruby/object:Gem::Requirement 75 | none: false 76 | requirements: 77 | - - ">=" 78 | - !ruby/object:Gem::Version 79 | version: "0" 80 | requirements: [] 81 | 82 | rubyforge_project: 83 | rubygems_version: 1.8.2 84 | signing_key: 85 | specification_version: 3 86 | summary: Gem version of acts_as_label Rails plugin. 87 | test_files: [] 88 | 89 | 90 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | acts_as_label (1.1.6) 5 | rails (>= 3.0.0) 6 | 7 | GEM 8 | remote: http://rubygems.org/ 9 | specs: 10 | actionmailer (4.0.0) 11 | actionpack (= 4.0.0) 12 | mail (~> 2.5.3) 13 | actionpack (4.0.0) 14 | activesupport (= 4.0.0) 15 | builder (~> 3.1.0) 16 | erubis (~> 2.7.0) 17 | rack (~> 1.5.2) 18 | rack-test (~> 0.6.2) 19 | activemodel (4.0.0) 20 | activesupport (= 4.0.0) 21 | builder (~> 3.1.0) 22 | activerecord (4.0.0) 23 | activemodel (= 4.0.0) 24 | activerecord-deprecated_finders (~> 1.0.2) 25 | activesupport (= 4.0.0) 26 | arel (~> 4.0.0) 27 | activerecord-deprecated_finders (1.0.3) 28 | activesupport (4.0.0) 29 | i18n (~> 0.6, >= 0.6.4) 30 | minitest (~> 4.2) 31 | multi_json (~> 1.3) 32 | thread_safe (~> 0.1) 33 | tzinfo (~> 0.3.37) 34 | arel (4.0.0) 35 | atomic (1.1.13) 36 | builder (3.1.4) 37 | diff-lcs (1.2.4) 38 | erubis (2.7.0) 39 | hike (1.2.3) 40 | i18n (0.6.5) 41 | mail (2.5.4) 42 | mime-types (~> 1.16) 43 | treetop (~> 1.4.8) 44 | mime-types (1.24) 45 | minitest (4.7.5) 46 | multi_json (1.7.9) 47 | polyglot (0.3.3) 48 | rack (1.5.2) 49 | rack-test (0.6.2) 50 | rack (>= 1.0) 51 | rails (4.0.0) 52 | actionmailer (= 4.0.0) 53 | actionpack (= 4.0.0) 54 | activerecord (= 4.0.0) 55 | activesupport (= 4.0.0) 56 | bundler (>= 1.3.0, < 2.0) 57 | railties (= 4.0.0) 58 | sprockets-rails (~> 2.0.0) 59 | railties (4.0.0) 60 | actionpack (= 4.0.0) 61 | activesupport (= 4.0.0) 62 | rake (>= 0.8.7) 63 | thor (>= 0.18.1, < 2.0) 64 | rake (10.1.0) 65 | rspec (2.14.1) 66 | rspec-core (~> 2.14.0) 67 | rspec-expectations (~> 2.14.0) 68 | rspec-mocks (~> 2.14.0) 69 | rspec-core (2.14.5) 70 | rspec-expectations (2.14.2) 71 | diff-lcs (>= 1.1.3, < 2.0) 72 | rspec-mocks (2.14.3) 73 | sprockets (2.10.0) 74 | hike (~> 1.2) 75 | multi_json (~> 1.0) 76 | rack (~> 1.0) 77 | tilt (~> 1.1, != 1.3.0) 78 | sprockets-rails (2.0.0) 79 | actionpack (>= 3.0) 80 | activesupport (>= 3.0) 81 | sprockets (~> 2.8) 82 | sqlite3 (1.3.8) 83 | thor (0.18.1) 84 | thread_safe (0.1.2) 85 | atomic 86 | tilt (1.4.1) 87 | treetop (1.4.15) 88 | polyglot 89 | polyglot (>= 0.3.1) 90 | tzinfo (0.3.37) 91 | 92 | PLATFORMS 93 | ruby 94 | 95 | DEPENDENCIES 96 | acts_as_label! 97 | rspec (>= 2.0.0) 98 | sqlite3 (>= 1.3.6) 99 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = acts_as_label 2 | 3 | 4 | == Description 5 | 6 | This acts_as extension implements a system label and a friendly label on a class and centralizes 7 | the logic for performing validations and accessing items by system label. 8 | 9 | The extension is particularly useful for tabled, enumerated lists. 10 | 11 | The system label is declared as a read-only attribute, allowing models and controllers to code 12 | to the value safely, secure in the knowledge that the value will never change. 13 | 14 | The friendly value can be changed to any value required by the end-user without affecting the 15 | model/controller code in any way. 16 | 17 | 18 | == Usage 19 | 20 | If you just want validations and system label accessors, simply add acts_as_label to the model. 21 | 22 | class Role < ActiveRecord::Base 23 | acts_as_label 24 | end 25 | 26 | 27 | The plugin accepts three optional parameters. 28 | 29 | +system_label_column+:: specifies the name of the column to use for the system label (default: system_label) 30 | +label_column+:: specifies the name of the column to use for the label (default: label) 31 | +default+:: specifies the system label value to use as the default record 32 | 33 | class Role < ActiveRecord::Base 34 | acts_as_label :system_label_column => :key, :label_column => :name, :default => :guest 35 | end 36 | 37 | 38 | If you use the plugin within a single table inheritance (STI) design, the easiest way to specify a default 39 | record is by implementing a class method on the subclass. 40 | 41 | class Label < ActiveRecord::Base 42 | acts_as_label 43 | end 44 | 45 | class BillingFrequency < Label 46 | self.default 47 | BillingFrequency.monthly 48 | end 49 | end 50 | 51 | 52 | The extension also allows records to be access by system label as though the system label were 53 | a class method. The default option can be accessed in a similar manner using the class 54 | method +default+. This yields more expressive code. 55 | 56 | class Role < ActiveRecord::Base 57 | has_many :users 58 | 59 | acts_as_label :default => :guest 60 | end 61 | 62 | class User < ActiveRecord::Base 63 | belongs_to :role 64 | end 65 | 66 | Role.create!({ :system_label => "SUPERUSER", :label => "Superuser"} ) 67 | Role.create!({ :system_label => "GUEST", :label => "Guest"} ) 68 | 69 | User.create!({ :name => "John Dugan", :role => Role.superuser }) 70 | User.create!({ :name => "Anonymous Dude", :role => Role.default }) 71 | 72 | 73 | To help avoid excessive querying, the extension overrides the equality operator. 74 | This allows record comparisons in a couple of formats: 75 | 76 | u.role == Role.superuser # as you would expect 77 | u.role == :superuser # to spare a superfluous query (same as u.role.to_sym == :superuser) 78 | 79 | 80 | Other useful utility methods: 81 | 82 | +to_s+:: Returns the friendly label value 83 | +to_sym+:: Returns the downcased, symbolized system label value 84 | 85 | 86 | 87 | == Helpful Links 88 | 89 | * Repository: http://github.com/coroutine/acts_as_label 90 | * Gem: http://rubygems.org/gems/acts_as_label 91 | * Authors: http://coroutine.com 92 | 93 | 94 | 95 | == Installation (Rails 3) 96 | 97 | Install me from RubyGems.org by adding a gem dependency to your Gemfile. Bundler does 98 | the rest. 99 | 100 | gem "acts_as_label" 101 | 102 | $ bundle install 103 | 104 | 105 | 106 | == Installation (Rails 2) 107 | 108 | Install me from RubyGems.org and add a gem dependency in the appropriate file. 109 | 110 | $ gem install acts_as_label 111 | 112 | Or install me as a plugin. 113 | 114 | $ script/plugin install git://github.com/coroutine/acts_as_label.git 115 | 116 | 117 | 118 | == Gemroll 119 | 120 | Other gems by Coroutine include: 121 | 122 | * {acts_as_current}[http://github.com/coroutine/acts_as_current] 123 | * {acts_as_list_with_sti_support}[http://github.com/coroutine/acts_as_list_with_sti_support] 124 | * {delayed_form_observer}[http://github.com/coroutine/delayed_form_observer] 125 | * {kenny_dialoggins}[http://github.com/coroutine/kenny_dialoggins] 126 | * {michael_hintbuble}[http://github.com/coroutine/michael_hintbuble] 127 | * {tiny_navigation}[http://github.com/coroutine/tiny_navigation] 128 | 129 | 130 | 131 | == License 132 | 133 | Copyright (c) 2010 {Coroutine LLC}[http://coroutine.com]. 134 | 135 | Permission is hereby granted, free of charge, to any person obtaining 136 | a copy of this software and associated documentation files (the 137 | "Software"), to deal in the Software without restriction, including 138 | without limitation the rights to use, copy, modify, merge, publish, 139 | distribute, sublicense, and/or sell copies of the Software, and to 140 | permit persons to whom the Software is furnished to do so, subject to 141 | the following conditions: 142 | 143 | The above copyright notice and this permission notice shall be 144 | included in all copies or substantial portions of the Software. 145 | 146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 147 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 148 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 149 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 150 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 151 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 152 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/acts_as_label/base.rb: -------------------------------------------------------------------------------- 1 | module Coroutine #:nodoc: 2 | module ActsAsLabel #:nodoc: 3 | module Base #:nodoc: 4 | 5 | def self.included(base) #:nodoc: 6 | base.extend(ClassMethods) 7 | end 8 | 9 | 10 | module ClassMethods 11 | 12 | 13 | # == Description 14 | # 15 | # This +acts_as+ extension implements a system label and a friendly label on a class and centralizes 16 | # the logic for performing validations and accessing items by system label. 17 | # 18 | # 19 | # == Usage 20 | # 21 | # Simple Example 22 | # 23 | # class BillingFrequency < ActiveRecord::Base 24 | # has_many :subscriptions 25 | # acts_as_label :default => :monthly 26 | # end 27 | # 28 | # class Subscription < ActiveRecord::Base 29 | # belongs_to :billing_frequency 30 | # end 31 | # 32 | # subscription.billing_frequency = BillingFrequency.monthly 33 | # subscription.billing_frequency = BillingFrequency.default 34 | # 35 | # 36 | # STI Example: 37 | # 38 | # class Label < ActiveRecord::Base 39 | # acts_as_label :scoped_to => :type 40 | # end 41 | # 42 | # class BillingFrequency < Label 43 | # has_many :subscriptions 44 | # def self.default 45 | # BillingFrequency.monthly 46 | # end 47 | # end 48 | # 49 | # class Subscription < ActiveRecord::Base 50 | # belongs_to :billing_frequency 51 | # end 52 | # 53 | # subscription.billing_frequency = BillingFrequency.monthly 54 | # subscription.billing_frequency = BillingFrequency.default 55 | # 56 | # 57 | # == Configuration 58 | # 59 | # * +system_label_cloumn+ - specifies the column name to use for storing the system label (default: +system_label+) 60 | # * +label_column+ - specifies the column name to use for storing the label (default: +label+) 61 | # * +default+ - specifies the system label value of the default instance (default: the first record in the default scope) 62 | # 63 | def acts_as_label(options = {}) 64 | 65 | #------------------------------------------- 66 | # scrub options 67 | #------------------------------------------- 68 | options = {} unless options.is_a?(Hash) 69 | system_label = options.key?(:system_label_column) ? options[:system_label_column].to_sym : :system_label 70 | label = options.key?(:label_column) ? options[:label_column].to_sym : :label 71 | scope = options.key?(:scope) ? options[:scope] : "1 = 1" 72 | default = options.key?(:default) ? options[:default].to_sym : nil 73 | 74 | 75 | #-------------------------------------------- 76 | # mix methods into class definition 77 | #-------------------------------------------- 78 | class_eval do 79 | 80 | # inheritable accessors 81 | class_attribute :acts_as_label_system_label_column 82 | class_attribute :acts_as_label_label_column 83 | class_attribute :acts_as_label_scope 84 | class_attribute :acts_as_label_default_system_label 85 | 86 | self.acts_as_label_system_label_column = system_label 87 | self.acts_as_label_label_column = label 88 | self.acts_as_label_scope = scope 89 | self.acts_as_label_default_system_label = default 90 | 91 | # protect attributes 92 | attr_readonly system_label 93 | 94 | # validations 95 | validates_presence_of system_label 96 | validates_length_of system_label, :maximum => 255 97 | validates_format_of system_label, :with => /\A[A-Z][_A-Z0-9]*\Z/ 98 | validates_presence_of label 99 | validates_length_of label, :maximum => 255 100 | 101 | # This method catches all undefined method calls. It first sees if any ancestor 102 | # understands the request. If not, it tries to match the method call to an 103 | # existing system label. If that is found, it lazily manufacturers a method on the 104 | # class of the same name. Otherwise, it throws the NoMethodError. 105 | # 106 | def self.method_missing(method, *args, &block) 107 | begin 108 | super 109 | rescue NoMethodError => e 110 | if has_acts_as_label_method?(method) 111 | self.__send__(method) 112 | else 113 | raise e 114 | end 115 | end 116 | end 117 | 118 | 119 | # This method determines whether or not the class has an instance with 120 | # the given system label. If it does, it also lazily creates a method 121 | # that can be accessed without all this method missing nonsense. 122 | # 123 | def self.has_acts_as_label_method?(method_name) 124 | mn = method_name.to_s.underscore 125 | sl = mn.upcase 126 | 127 | if record = by_acts_as_label_system_label(sl) 128 | eval %Q{ 129 | class << self 130 | def #{mn} 131 | by_acts_as_label_system_label('#{sl}') 132 | end 133 | 134 | alias_method :#{mn.upcase}, :#{mn} 135 | end 136 | } 137 | end 138 | 139 | !!record 140 | end 141 | 142 | 143 | # This method finds an active record object for the given system label. 144 | # 145 | def self.by_acts_as_label_system_label(system_label) 146 | where("#{acts_as_label_system_label_column} = ?", system_label.to_s.upcase).first 147 | end 148 | 149 | 150 | # This block adds a class method to return the default record. 151 | # 152 | unless self.method_defined? :default 153 | if default.nil? 154 | def self.default 155 | self.first 156 | end 157 | else 158 | def self.default 159 | self.send("#{acts_as_label_default_system_label}") 160 | end 161 | end 162 | end 163 | 164 | 165 | # This method overrides the system label column writer to force 166 | # upcasing of the value. 167 | # 168 | define_method("#{acts_as_label_system_label_column}=") do |value| 169 | value = value.to_s.strip.upcase unless value.nil? 170 | write_attribute("#{acts_as_label_system_label_column}", value) 171 | end 172 | 173 | 174 | # Add all the instance methods 175 | include Coroutine::ActsAsLabel::Base::InstanceMethods 176 | 177 | end 178 | end 179 | end 180 | 181 | 182 | module InstanceMethods 183 | 184 | # This method overrides to_ary to return nil, which tells anything trying to 185 | # flatten this we are already flat. This is necessary because we are overriding 186 | # active records method missing, which must do this somewhere in the bowels 187 | # of rails. 188 | # 189 | def to_ary 190 | nil 191 | end 192 | 193 | 194 | # This method overrides the to_s method to return the friendly label value. 195 | # 196 | def to_s 197 | self.send("#{acts_as_label_label_column}") 198 | end 199 | 200 | 201 | # This method overrides the to_sym method to return the downcased symbolized 202 | # system label value. This method is particularly useful in conjunction with 203 | # role-based authorization systems. 204 | # 205 | def to_sym 206 | self.send("#{acts_as_label_system_label_column}").underscore.to_sym 207 | end 208 | 209 | 210 | # This method compares two values by running to_sym on both sides. This allows 211 | # comparisons like the following: 212 | # u.role == Role.superuser 213 | # u.role == :superuser 214 | # 215 | def ==(other) 216 | self.to_sym == (other.to_sym rescue false) 217 | end 218 | 219 | end 220 | 221 | end 222 | end 223 | end 224 | -------------------------------------------------------------------------------- /test/acts_as_label_test.rb: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------- 2 | # Requirements 3 | #--------------------------------------------------------- 4 | 5 | # all generic requirements are in the helper 6 | require "test_helper" 7 | 8 | 9 | #--------------------------------------------------------- 10 | # Database config 11 | #--------------------------------------------------------- 12 | 13 | # establish db connection 14 | ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") 15 | 16 | # define and seed tables 17 | def setup_db 18 | ActiveRecord::Schema.define(:version => 1) do 19 | create_table :labels do |t| 20 | t.string :type, :limit => 255 21 | t.string :system_label, :limit => 255 22 | t.string :label, :limit => 255 23 | 24 | t.timestamps 25 | end 26 | 27 | create_table :frameworks do |t| 28 | t.string :system_name, :limit => 255 29 | t.string :name, :limit => 255 30 | 31 | t.timestamps 32 | end 33 | end 34 | 35 | Role.create!({ :system_label => "SUPERUSER", :label => "Admin" }) 36 | Role.create!({ :system_label => "EMPLOYEE", :label => "Employee" }) 37 | Role.create!({ :system_label => "GUEST", :label => "Guest" }) 38 | 39 | BillingFrequency.create!({ :system_label => "MONTHLY", :label => "Monthly" }) 40 | BillingFrequency.create!({ :system_label => "QUARTERLY", :label => "Quarterly" }) 41 | BillingFrequency.create!({ :system_label => "YEARLY", :label => "Yearly" }) 42 | 43 | TaxFrequency.create!({ :system_label => "MONTHLY", :label => "Monthly" }) 44 | TaxFrequency.create!({ :system_label => "QUARTERLY", :label => "Quarterly" }) 45 | TaxFrequency.create!({ :system_label => "YEARLY", :label => "Yearly" }) 46 | 47 | Framework.create!({ :system_name => "RUBY_ON_RAILS", :name => "Rails" }) 48 | Framework.create!({ :system_name => "DJANGO", :name => "Django" }) 49 | end 50 | 51 | 52 | # drop all tables 53 | def teardown_db 54 | ActiveRecord::Base.connection.tables.each do |table| 55 | ActiveRecord::Base.connection.drop_table(table) 56 | end 57 | end 58 | 59 | 60 | #--------------------------------------------------------- 61 | # Model definitions 62 | #--------------------------------------------------------- 63 | 64 | # Labels (STI base) 65 | class Label < ActiveRecord::Base 66 | acts_as_label 67 | end 68 | 69 | 70 | # Roles (STI extension with default) 71 | class Role < Label 72 | def self.default 73 | Role.guest 74 | end 75 | end 76 | 77 | 78 | # BillingFrequency (STI extension without default) 79 | class BillingFrequency < Label 80 | validates_uniqueness_of :system_label, :scope => :type 81 | end 82 | 83 | 84 | # TaxFrequency (STI extension for testing duplicate system labels) 85 | class TaxFrequency < Label 86 | validates_uniqueness_of :system_label, :scope => :type 87 | end 88 | 89 | 90 | # Frameworks (stand-alone model with overrides) 91 | class Framework < ActiveRecord::Base 92 | acts_as_label :system_label_column => :system_name, :label_column => :name, :default => :ruby_on_rails 93 | end 94 | 95 | 96 | #--------------------------------------------------------- 97 | # Tests 98 | #--------------------------------------------------------- 99 | 100 | class ActsAsLabelTest < ActiveSupport::TestCase 101 | 102 | #--------------------------------------------- 103 | # setup and teardown delegations 104 | #--------------------------------------------- 105 | 106 | def setup 107 | setup_db 108 | end 109 | def teardown 110 | teardown_db 111 | end 112 | 113 | #--------------------------------------------- 114 | # test validations 115 | #--------------------------------------------- 116 | 117 | def test_validations_with_standard_columns 118 | 119 | # get valid record 120 | record = Role.new({ :system_label => "CUSTOMER", :label => "Client" }) 121 | assert record.valid? 122 | 123 | # system label cannot be null 124 | record.system_label = nil 125 | assert !record.valid? 126 | 127 | # system label cannot be blank 128 | record.system_label = "" 129 | assert !record.valid? 130 | 131 | # system label cannot be longer than 255 characters 132 | record.system_label = "" 133 | 256.times { record.system_label << "x" } 134 | assert !record.valid? 135 | 136 | # system label cannot have illegal characters 137 | record.system_label = "SUPER-USER" 138 | assert !record.valid? 139 | 140 | # reset system label 141 | record.system_label = "CUSTOMER" 142 | assert record.valid? 143 | 144 | # label cannot be null 145 | record.label = nil 146 | assert !record.valid? 147 | 148 | # label cannot be blank 149 | record.label = "" 150 | assert !record.valid? 151 | 152 | # label cannot be longer than 255 characters 153 | record.label = "" 154 | 256.times { record.label << "x" } 155 | assert !record.valid? 156 | 157 | end 158 | 159 | 160 | def test_validations_with_custom_columns 161 | 162 | # get valid record 163 | record = Framework.new({ :system_name => "SPRING", :name => "Spring" }) 164 | assert record.valid? 165 | 166 | # system name cannot be null 167 | record.system_name = nil 168 | assert !record.valid? 169 | 170 | # system name cannot be blank 171 | record.system_name = "" 172 | assert !record.valid? 173 | 174 | # system name cannot be longer than 255 characters 175 | record.system_name = "" 176 | 256.times { record.system_name << "x" } 177 | assert !record.valid? 178 | 179 | # system name cannot have illegal characters 180 | record.system_name = "SPRING-JAVA" 181 | assert !record.valid? 182 | 183 | # reset system name 184 | record.system_name = "SPRING" 185 | assert record.valid? 186 | 187 | # name cannot be null 188 | record.name = nil 189 | assert !record.valid? 190 | 191 | # name cannot be blank 192 | record.name = "" 193 | assert !record.valid? 194 | 195 | # name cannot be longer than 255 characters 196 | record.name = "" 197 | 256.times { record.name << "x" } 198 | assert !record.valid? 199 | 200 | end 201 | 202 | 203 | #--------------------------------------------- 204 | # test method missing 205 | #--------------------------------------------- 206 | 207 | def test_method_missing_accessors 208 | 209 | # lookup database objects using syntax appropriate for version of rails 210 | begin 211 | role_superuser = Role.where("system_label = ?", "SUPERUSER").first 212 | role_guest = Role.where("system_label = ?", "GUEST").first 213 | role_fresh = Role.create!(:system_label => "FRESH", :label => "Fresh") 214 | framework_rails = Framework.where("system_name = ?", "RUBY_ON_RAILS").first 215 | rescue 216 | role_superuser = Role.find(:first, :conditions => ["system_label = ?", "SUPERUSER"]) 217 | role_guest = Role.find(:first, :conditions => ["system_label = ?", "GUEST"]) 218 | framework_rails = Framework.find(:first, :conditions => ["system_name = ?", "RUBY_ON_RAILS"]) 219 | end 220 | 221 | # Won't have a method now 222 | assert !Role.methods.map(&:to_s).include?("fresh") # some rubies report strings, some symbols 223 | 224 | # test lookup by system label 225 | assert_equal role_fresh, Role.fresh 226 | 227 | # should have a method now 228 | assert Role.methods.map(&:to_s).include?("fresh") # some rubies report strings, some symbols 229 | 230 | # test default with implemented method 231 | assert_equal role_guest, Role.default 232 | 233 | # test default with unspecified behavior 234 | assert_equal BillingFrequency.first, BillingFrequency.default 235 | 236 | # test default with specified system label 237 | assert_equal framework_rails, Framework.default 238 | end 239 | 240 | def test_method_missing_finders 241 | 242 | # dynamic find on stand-alone model 243 | record = Framework.find_by_system_name("RUBY_ON_RAILS") 244 | assert !record.nil? 245 | 246 | #dynamic find on sti model 247 | record = Role.find_by_system_label("SUPERUSER") 248 | assert !record.nil? 249 | 250 | end 251 | 252 | 253 | 254 | #--------------------------------------------- 255 | # test validations 256 | #--------------------------------------------- 257 | 258 | def test_system_label_is_readonly 259 | 260 | # build valid record 261 | record = Role.new({ :system_label => "CUSTOMER", :label => "Client" }) 262 | assert record.valid? 263 | 264 | # save it and remember id and system label 265 | record.save 266 | id = record.id 267 | system_label = record.system_label 268 | 269 | # system label unchanged on safe update 270 | record.label = "Customer" 271 | record.save 272 | record = Role.find(id) # we have to get the record again to verify what's in the db 273 | assert_equal system_label, record.system_label 274 | 275 | # system_label unchanged on unsafe update 276 | record.system_label = "CLIENT" 277 | record.label = "Client" 278 | record.save 279 | record = Role.find(id) # we have to get the record again to verify what's in the db 280 | assert_equal "Client", record.label 281 | assert_equal system_label, record.system_label 282 | 283 | end 284 | 285 | 286 | 287 | #--------------------------------------------- 288 | # test instance methods 289 | #--------------------------------------------- 290 | 291 | def test_equality 292 | r1 = Role.superuser 293 | 294 | assert (r1 == Role.superuser) 295 | assert (r1 == :superuser) 296 | 297 | assert !(r1 == false) 298 | end 299 | 300 | def test_to_s 301 | role = Role.first 302 | assert_equal role.label, role.to_s 303 | end 304 | 305 | def test_to_sym 306 | role = Role.first 307 | assert_equal role.system_label.downcase.to_sym, role.to_sym 308 | end 309 | 310 | def test_upcase_system_label_value 311 | # default system label column name 312 | record = Role.create!({ :system_label => "Customer", :label => "Client" }) 313 | assert_equal record.system_label, "CUSTOMER" 314 | 315 | # custom system label column name 316 | record = Framework.create!( :system_name => "example", :name => "Example") 317 | assert_equal record.system_name, "EXAMPLE" 318 | end 319 | 320 | def test_support_upcase_accessors 321 | assert Role.SUPERUSER == Role.superuser 322 | assert Role.SUPERUSER == :superuser 323 | end 324 | 325 | end 326 | --------------------------------------------------------------------------------