├── .gitignore ├── .travis.yml ├── Gemfile ├── README ├── Rakefile ├── activerecord-column-reader.gemspec ├── gemfiles ├── Gemfile.activerecord-3.0.x └── Gemfile.activerecord-3.1.x ├── lib ├── activerecord-column-reader.rb └── penknife │ ├── active_record.rb │ └── active_record │ └── column_reader.rb └── spec ├── penknife └── column_reader_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /test.db 2 | *.lock 3 | /pkg/* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.8.7 3 | - 1.9.2 4 | - 1.9.3 5 | gemfile: 6 | - gemfiles/Gemfile.activerecord-3.0.x 7 | - gemfiles/Gemfile.activerecord-3.1.x -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gemspec -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A very simple utility, for when you want to easily read only a single column from an ActiveRecord model. 2 | 3 | Usage: 4 | 5 | class Monster < ActiveRecord::Base 6 | column_reader :name 7 | column_reader :hairyness, :as => :hirsutenesses 8 | column_reader :dob 9 | 10 | scope :dangerous, :conditions => {:carnivorous => true} 11 | end 12 | 13 | At it's simplest, it returns the values of just the given column: 14 | 15 | Monster.names 16 | # => ['Graham', 'Mike', 'Fuzzly'] 17 | 18 | The default accessor is the column name pluralized, but using the :as option, another name can be chosen: 19 | 20 | Monster.hirsutenesses 21 | # => ['bald', 'very', 'very'] 22 | 23 | Scopes are respected: 24 | 25 | Monster.dangerous.names 26 | # => ['Fuzzly'] 27 | 28 | And columns are correctly type cast: 29 | 30 | Monster.dobs: 31 | # => [#, #, #] 32 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require "rspec/core/rake_task" 3 | 4 | Bundler.setup :development 5 | 6 | RSpec::Core::RakeTask.new(:spec) do |t| 7 | t.rspec_opts = "-f n -c" 8 | t.pattern = "spec/**/*_spec.rb" 9 | end 10 | 11 | task :default => :spec -------------------------------------------------------------------------------- /activerecord-column-reader.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "activerecord-column-reader" 6 | s.version = "1.0.0" 7 | s.summary = "Simple ActiveRecord optimization" 8 | s.author = "Tom Ward" 9 | s.email = "tom@popdog.net" 10 | s.homepage = "http://tomafro.net" 11 | 12 | s.has_rdoc = true 13 | s.extra_rdoc_files = %w(README) 14 | s.rdoc_options = %w(--main README) 15 | 16 | # Add any extra files to include in the gem 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 20 | s.require_paths = ["lib"] 21 | 22 | # Main dependencies 23 | s.add_dependency 'activerecord', '~>3.0' 24 | 25 | # Development only dependencies 26 | s.add_development_dependency 'rspec', '~>2.7.0' 27 | s.add_development_dependency 'rake', '~>0.8.7' 28 | s.add_development_dependency 'sqlite3', '~>1.3.4' 29 | s.add_development_dependency 'sqlite3-ruby', '~>1.3.3' 30 | s.add_development_dependency 'sdoc', '~>0.2.20' 31 | end -------------------------------------------------------------------------------- /gemfiles/Gemfile.activerecord-3.0.x: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gemspec :path => ".." 4 | 5 | gem 'activerecord', '~>3.0.0' -------------------------------------------------------------------------------- /gemfiles/Gemfile.activerecord-3.1.x: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gemspec :path => ".." 4 | 5 | gem 'activerecord', '~>3.1.0' -------------------------------------------------------------------------------- /lib/activerecord-column-reader.rb: -------------------------------------------------------------------------------- 1 | require 'penknife/active_record/column_reader' -------------------------------------------------------------------------------- /lib/penknife/active_record.rb: -------------------------------------------------------------------------------- 1 | module Penknife 2 | module ActiveRecord 3 | end 4 | end -------------------------------------------------------------------------------- /lib/penknife/active_record/column_reader.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require 'penknife/active_record' 3 | 4 | module Penknife::ActiveRecord::ColumnReader 5 | def column_reader(column_name, options = {}) 6 | name = options.delete(:as) || column_name.to_s.pluralize 7 | column = columns_hash[column_name.to_s] 8 | 9 | type_cast_code = column.type_cast_code('v') 10 | 11 | line = __LINE__ 12 | self.class_eval %{ 13 | def self.#{name} 14 | query = scoped.select(arel_table[:#{column_name}]) 15 | connection.select_all(query.to_sql).collect do |value| 16 | v = value.values.first 17 | #{type_cast_code} 18 | end 19 | end 20 | }, __FILE__, line + 1 21 | end 22 | 23 | ::ActiveRecord::Base.extend(self) 24 | end -------------------------------------------------------------------------------- /spec/penknife/column_reader_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class Monster < ActiveRecord::Base 4 | column_reader :name 5 | column_reader :dob, :as => :birth_dates 6 | column_reader :stomachs, :as => :stomachs 7 | scope :carnivorous, :conditions => {:carnivorous => true} 8 | end 9 | 10 | describe "Penknife::ActiveRecord::ColumnReader" do 11 | before :all do 12 | @growler = Monster.create! :name => 'Growler', :carnivorous => true, :dob => 5.years.ago.to_date, :stomachs => 1.0 13 | @munchy = Monster.create! :name => 'Munchy', :carnivorous => true, :dob => 12.years.ago, :stomachs => 2.5 14 | @thistletop = Monster.create! :name => 'Thistletop', :carnivorous => false, :dob => 1.years.ago, :stomachs => 3 15 | end 16 | 17 | it "returns only specified values" do 18 | Monster.names.sort.should eql(['Growler', 'Munchy', 'Thistletop']) 19 | end 20 | 21 | it "respects scope" do 22 | Monster.carnivorous.names.sort.should eql(['Growler', 'Munchy']) 23 | end 24 | 25 | it "casts returned dates" do 26 | Monster.birth_dates.should eql([@growler.dob.to_date, @munchy.dob.to_date, @thistletop.dob.to_date]) 27 | end 28 | 29 | it "casts returned floats" do 30 | Monster.stomachs.should eql([@growler.stomachs, @munchy.stomachs, @thistletop.stomachs]) 31 | end 32 | 33 | after :all do 34 | Monster.destroy_all 35 | end 36 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'active_record' 3 | require 'activerecord-column-reader' 4 | require 'logger' 5 | 6 | ActiveRecord::Base.establish_connection({ 7 | :adapter => "sqlite3", 8 | :database => "test.db" 9 | }) 10 | 11 | ActiveRecord::Schema.define(:version => 1) do 12 | create_table 'monsters', :force => true do |t| 13 | t.string 'name' 14 | t.date 'dob' 15 | t.boolean 'carnivorous' 16 | t.float 'stomachs' 17 | end 18 | end 19 | --------------------------------------------------------------------------------