hashdown 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ bin/
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hashdown.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Solomon White
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.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Hashdown
2
+
3
+ Hashdown is a super lightweight rails plugin that adds hash-style lookups and
4
+ option lists (for generating dropdowns) to ActiveRecord models. Note: Hashdown
5
+ has been updated to support Rails 3. If you're looking for the original plugin
6
+ version, it's in the [rails 2 branch](https://github.com/rubysolo/hashdown/tree/rails2)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'hashdown'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install hashdown
21
+
22
+ ## Usage
23
+
24
+ Given the following class definition:
25
+
26
+ class State < ActiveRecord::Base
27
+ finder :abbreviation
28
+ end
29
+
30
+ You can look up states via their abbreviations like so:
31
+
32
+ @colorado = State[:CO]
33
+
34
+ By default, calling the finder method with a token that does not exist in the
35
+ database will result in an ActiveRecord::RecordNotFount exception being thrown.
36
+ This can be overridden by adding a :default option to your finder declaration.
37
+
38
+ class PurchaseOrder < ActiveRecord::Base
39
+ finder :number, :default => nil
40
+ end
41
+
42
+ In this case, PurchaseOrder['00734'] will silently return nil if that number is
43
+ not found.
44
+
45
+ These types of reference data models are often something you need to populate a
46
+ select list on your form, so hashdown includes a method to generate your option
47
+ list:
48
+
49
+ 1. Declare your model to be selectable:
50
+
51
+ class State < ActiveRecord::Base
52
+ selectable
53
+ end
54
+
55
+ 2. Call select_options in your form to return a set of name, value pairs to
56
+ pass into a select builder:
57
+
58
+ <%= form.select :state_id, State.select_options %>
59
+
60
+ By default, selectable will return the :id of the record as the value, and the
61
+ :name attribute value as the display. This can be overridden inline in the
62
+ select_options call:
63
+
64
+ State.select_options(:name, :abbreviation)
65
+
66
+ The grouped_options_for_select format is also supported:
67
+
68
+ State.select_options(:group => :region)
69
+
70
+ Adding finder and selectable to a model is roughly equivalent to the following
71
+ implementation:
72
+
73
+ class State < ActiveRecord::Base
74
+ validates_uniqueness_of :abbreviation
75
+
76
+ def self.[](state_code)
77
+ find_by_abbreviation(state_code)
78
+ end
79
+
80
+ def self.select_options
81
+ find(:all).map{|s| [s.name, s.id] }
82
+ end
83
+ end
84
+
85
+ ...except hashdown adds configuration for flexibility and caching for speedy
86
+ lookups.
87
+
88
+
89
+ ## Contributing
90
+
91
+ 1. Fork it
92
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
93
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
94
+ 4. Push to the branch (`git push origin my-new-feature`)
95
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run specs"
5
+ task :spec do
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.rspec_opts = %w{--colour --format progress}
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ end
10
+ end
11
+
12
+ desc "Default: run specs."
13
+ task :default => :spec
data/hashdown.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hashdown/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Solomon White"]
6
+ gem.email = ["rubysolo@gmail.com"]
7
+ gem.description = %q{Hashdown}
8
+ gem.summary = %q{super lightweight Rails plugin that adds hash-style lookups and option list (for generating dropdowns) to ActiveRecord models}
9
+ gem.homepage = "https://github.com/rubysolo/hashdown"
10
+
11
+ gem.add_dependency 'activerecord', '~> 3.0'
12
+
13
+ gem.add_development_dependency 'pry-nav'
14
+ gem.add_development_dependency 'rake'
15
+ gem.add_development_dependency 'rspec'
16
+ gem.add_development_dependency 'sqlite3'
17
+
18
+ gem.files = `git ls-files`.split($\)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.name = "hashdown"
22
+ gem.require_paths = ["lib"]
23
+ gem.version = Hashdown::VERSION
24
+ end
@@ -0,0 +1,51 @@
1
+ module Hashdown
2
+ def self.cache
3
+ @cache ||= rails_cache || local_cache
4
+ end
5
+
6
+ def self.cache=(value)
7
+ @cache = value
8
+ end
9
+
10
+ def self.force_cache_miss?
11
+ Rails.env.test? if defined?(Rails)
12
+ end
13
+
14
+ def self.cache_key(source, class_name, token=nil)
15
+ ['hashdown', class_name, source, token].compact.join('-')
16
+ end
17
+
18
+ def self.uncache(source, class_name, token)
19
+ cache.delete(cache_key(source, class_name, token))
20
+ end
21
+
22
+ class Config
23
+ def initialize(hash={})
24
+ @data = hash
25
+ end
26
+
27
+ def method_missing(method_id, *args)
28
+ if method_id.to_s =~ /^(\w*)=$/
29
+ @data[$1.to_sym] = args.first
30
+ elsif method_id.to_s =~ /^(\w*)\?$/
31
+ @data.has_key?($1.to_sym)
32
+ else
33
+ if @data.has_key?(method_id)
34
+ @data[method_id]
35
+ else
36
+ super
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def self.rails_cache
45
+ Rails.cache if defined?(Rails)
46
+ end
47
+
48
+ def self.local_cache
49
+ ActiveSupport::Cache::MemoryStore.new
50
+ end
51
+ end
@@ -0,0 +1,45 @@
1
+ require 'hashdown/cache'
2
+
3
+ module Hashdown
4
+ module Finder
5
+ def finder(attribute, options={})
6
+ self.extend ClassMethods
7
+ self.send(:include, InstanceMethods)
8
+
9
+ hashdown.finder = Config.new(options.merge(:key => attribute))
10
+
11
+ validates attribute, :uniqueness => true
12
+
13
+ after_save :clear_hashdown_finder_cache
14
+ after_destroy :clear_hashdown_finder_cache
15
+ end
16
+
17
+ module ClassMethods
18
+ def hashdown
19
+ @hashdown ||= Config.new
20
+ end
21
+
22
+ def [](token)
23
+ Hashdown.cache.fetch(Hashdown.cache_key(:finder, self.to_s, token), :force => Hashdown.force_cache_miss?) do
24
+ where({hashdown.finder.key => token.to_s}).first || (
25
+ hashdown.finder.default? ?
26
+ hashdown.finder.default :
27
+ raise(ActiveRecord::RecordNotFound)
28
+ )
29
+ end
30
+ end
31
+ end
32
+
33
+ module InstanceMethods
34
+ def hashdown
35
+ self.class.hashdown
36
+ end
37
+
38
+ def clear_hashdown_finder_cache
39
+ Hashdown.uncache(:finder, self.class.to_s, self[hashdown.finder.key])
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ ActiveRecord::Base.extend Hashdown::Finder
@@ -0,0 +1,56 @@
1
+ module Hashdown
2
+ module SelectOptions
3
+ def select_options(*args)
4
+ unless self.respond_to?(:map_records_to_select_options)
5
+ self.extend ClassMethods
6
+ self.send(:include, InstanceMethods)
7
+
8
+ after_save :clear_select_options_cache
9
+ after_destroy :clear_select_options_cache
10
+ end
11
+
12
+ options = args.pop if args.last.is_a?(Hash)
13
+ options ||= {}
14
+
15
+ options[:label] ||= args.shift || :name
16
+ options[:value] ||= args.shift || :id
17
+
18
+ scope = scoped
19
+ scope = scope.order(options[:label]) if scope.arel.orders.empty? && columns.any?{|c| c.name == options[:label].to_s }
20
+
21
+ Hashdown.cache.fetch(Hashdown.cache_key(:select_options, self.to_s, select_options_cache_key(options, scope)), :force => Hashdown.force_cache_miss?) do
22
+ if grouping = options[:group]
23
+ scope.all.group_by {|record| grouping.call(record) }.map do |group, records|
24
+ [group, map_records_to_select_options(records, options)]
25
+ end
26
+ else
27
+ map_records_to_select_options(scope.all, options)
28
+ end
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ private
34
+
35
+ def select_options_cache_key(options, scope)
36
+ key = options.sort.each_with_object(""){|ck,(k,v)| ck << "#{ k }:#{ v };" }
37
+ key << scope.to_sql
38
+ key = Digest::MD5.hexdigest(key)
39
+ end
40
+
41
+ def map_records_to_select_options(records, options)
42
+ records.map{|record| [ record[options[:label]], record[options[:value]] ] }
43
+ end
44
+ end
45
+
46
+ module InstanceMethods
47
+ private
48
+
49
+ def clear_select_options_cache
50
+ Hashdown.cache.delete_matched(Regexp.new(Hashdown.cache_key(:select_options, self.class.to_s)))
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ ActiveRecord::Base.extend Hashdown::SelectOptions
@@ -0,0 +1,3 @@
1
+ module Hashdown
2
+ VERSION = "0.2.0"
3
+ end
data/lib/hashdown.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "hashdown/version"
2
+ require "hashdown/finder"
3
+ require "hashdown/select_options"
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashdown do
4
+ describe 'cache' do
5
+ describe 'in a Rails project' do
6
+ before { Object.const_set('Rails', mock(cache: :rails_cache)) }
7
+ after { Object.send(:remove_const, 'Rails') }
8
+
9
+ it 'delegates to Rails.cache if available' do
10
+ Hashdown.cache.should eq Rails.cache
11
+ end
12
+ end
13
+
14
+ it 'creates a new cache store if Rails.cache unavailable' do
15
+ Hashdown.cache.class.should eq ActiveSupport::Cache::MemoryStore
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashdown::Finder do
4
+ describe 'bracket lookup' do
5
+ it 'is added to enabled models' do
6
+ State.should respond_to(:[])
7
+ end
8
+
9
+ it 'finds a record by a string key value' do
10
+ State['CA'].name.should eq 'California'
11
+ end
12
+
13
+ it 'finds a record by a symbol key value' do
14
+ State[:CO].name.should eq 'Colorado'
15
+ end
16
+
17
+ it 'adds uniqueness validation to key attribute' do
18
+ State.where(abbreviation: 'CO').count.should eq 1
19
+ State.new(abbreviation: 'CO').should_not be_valid
20
+ end
21
+ end
22
+
23
+ describe 'missing/invalid key' do
24
+ it 'raises record not found exception' do
25
+ lambda { State[:HI] }.should raise_error(ActiveRecord::RecordNotFound)
26
+ end
27
+
28
+ it 'allows setting a default to avoid exception' do
29
+ lambda { StateDefaultNil[:HI].should be_nil }.should_not raise_error
30
+ end
31
+ end
32
+
33
+ describe 'cache layer' do
34
+ let(:florida) { State.new(abbreviation: 'FL', name: 'Florida') }
35
+
36
+ it 'caches found records' do
37
+ scope = mock(first: florida)
38
+ State.should_receive(:where).once.and_return(scope)
39
+
40
+ 2.times { State[:FL].name.should eq 'Florida' }
41
+ end
42
+
43
+ describe 'in test environment' do
44
+ before { Object.const_set('Rails', mock(env: mock(test?: true), cache: ActiveSupport::Cache::MemoryStore.new)) }
45
+ after { Object.send(:remove_const, 'Rails') }
46
+
47
+ it 'forces cache miss' do
48
+ scope = mock(first: florida)
49
+ State.should_receive(:where).twice.and_return(scope)
50
+
51
+ 2.times { State[:FL].name.should eq 'Florida' }
52
+ end
53
+ end
54
+
55
+ it 'clears the cache on save' do
56
+ scope = mock(first: florida)
57
+ State.should_receive(:where).twice.and_return(scope)
58
+
59
+ State[:FL].save
60
+ State[:FL]
61
+ end
62
+
63
+ it 'clears the cache on destroy' do
64
+ scope = mock(first: florida)
65
+ State.should_receive(:where).twice.and_return(scope)
66
+
67
+ State[:FL].destroy
68
+ State[:FL]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashdown::SelectOptions do
4
+ let(:state_names) { %w(Arizona California Colorado New\ York Texas) }
5
+
6
+ it 'adds #select_options to ActiveRecord classes' do
7
+ State.should respond_to(:select_options)
8
+ end
9
+
10
+ it 'returns one entry per database record' do
11
+ State.select_options.length.should eq State.count
12
+ end
13
+
14
+ it 'sorts by label if no order is specified' do
15
+ State.select_options.map(&:first).should eq state_names
16
+ end
17
+
18
+ it 'respects order if specified' do
19
+ Currency.select_options.map(&:first).should eq ['Renminbi', 'Euro', 'Pound Sterling', 'US Dollar']
20
+ end
21
+
22
+ it 'returns a list of records formatted for the #select form helper' do
23
+ option = State.select_options.first
24
+ option.first.should eq 'Arizona'
25
+ option.last.should eq State[:AZ].id
26
+ end
27
+
28
+ it 'generates grouped options' do
29
+ grouped_states = State.select_options(group: lambda{|state| state.name.first })
30
+ grouped_states.map(&:first).should eq %w( A C N T )
31
+ grouped_states.detect{|group, states| group == 'C' }.last.length.should eq 2
32
+ end
33
+
34
+ it 'supports alternate option labels' do
35
+ Currency.select_options(:code).map(&:first).should eq %w( CNY EUR GBP USD )
36
+ end
37
+
38
+ it 'supports alternate option values' do
39
+ Currency.select_options(:name, :code).map(&:last).should eq %w( CNY EUR GBP USD )
40
+ end
41
+
42
+ describe 'cache layer' do
43
+ let(:states) { State.all }
44
+ let(:mock_scope) { mock(arel: mock(orders: %w( name )), to_sql: "SELECT * FROM states") }
45
+
46
+ it 'should cache found records' do
47
+ mock_scope.should_receive(:all).once.and_return(states)
48
+ State.stub(:scoped).and_return(mock_scope)
49
+
50
+ 2.times { State.select_options.length.should eq states.length }
51
+ end
52
+
53
+ describe 'in test environment' do
54
+ before { Object.const_set('Rails', mock(env: mock(test?: true), cache: ActiveSupport::Cache::MemoryStore.new)) }
55
+ after { Object.send(:remove_const, 'Rails') }
56
+
57
+ it 'forces cache miss' do
58
+ mock_scope.should_receive(:all).twice.and_return(states)
59
+ State.stub(:scoped).and_return(mock_scope)
60
+
61
+ 2.times { State.select_options.length.should eq states.length }
62
+ end
63
+ end
64
+
65
+ it 'respects scopes' do
66
+ State.select_options.map(&:first).should eq state_names
67
+ State.starting_with_c.select_options.map(&:first).should eq %w( California Colorado )
68
+ end
69
+
70
+ it 'clears the cache on save' do
71
+ mock_scope.should_receive(:all).twice.and_return(states)
72
+ State.stub(:scoped).and_return(mock_scope)
73
+
74
+ State.select_options
75
+ states.first.save
76
+ State.select_options
77
+ end
78
+
79
+ it 'clears the cache on destroy' do
80
+ mock_scope.should_receive(:all).twice.and_return(states)
81
+ State.stub(:scoped).and_return(mock_scope)
82
+
83
+ State.select_options
84
+ states.first.destroy
85
+ State.select_options
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,27 @@
1
+ require 'pry-nav'
2
+ require 'rspec'
3
+ require 'rspec/autorun'
4
+
5
+ require 'sqlite3'
6
+ require 'active_record'
7
+ require 'hashdown/finder'
8
+ require 'hashdown/select_options'
9
+
10
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
11
+ load 'support/models.rb'
12
+
13
+ RSpec.configure do |config|
14
+ config.mock_with :rspec
15
+
16
+ config.before(:each) do
17
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
18
+ load 'support/schema.rb'
19
+ load 'support/seeds.rb'
20
+
21
+ Hashdown.cache = nil
22
+ end
23
+
24
+ config.after(:each) do
25
+ Hashdown.cache = nil
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ class State < ActiveRecord::Base
2
+ scope :starting_with_c, where("name like 'C%'")
3
+ finder :abbreviation
4
+ end
5
+
6
+ class SortedState < ActiveRecord::Base
7
+ self.table_name = 'states'
8
+ default_scope order(:abbreviation)
9
+ finder :abbreviation
10
+ end
11
+
12
+ class StateDefaultNil < ActiveRecord::Base
13
+ self.table_name = 'states'
14
+ finder :abbreviation, default: nil
15
+ end
16
+
17
+ class Currency < ActiveRecord::Base
18
+ default_scope order(:code)
19
+ end
@@ -0,0 +1,11 @@
1
+ ActiveRecord::Schema.verbose = false
2
+
3
+ ActiveRecord::Schema.define(:version => 1) do
4
+ create_table :states do |t|
5
+ t.string :name, :abbreviation
6
+ end
7
+
8
+ create_table :currencies do |t|
9
+ t.string :name, :code
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ class ActiveRecord::Base
2
+ def self.seed(data_table)
3
+ data_table = data_table.strip.split(/\s*\n\s*/)
4
+ data_table.reject!{|row| row =~ /---/ }
5
+
6
+ headers = data_table.shift.split(/\s*\|\s*/)[1..-1]
7
+
8
+ delete_all
9
+
10
+ data_table.each do |row|
11
+ data = Hash[*headers.zip(row.split(/\s*\|\s*/)[1..-1]).flatten].with_indifferent_access
12
+ if data[primary_key]
13
+ connection.execute("INSERT INTO #{table_name} SET #{ data.map{|k,v| "#{ connection.quote_column_name(k) } = #{ connection.quote(v) }" }.join(', ') }")
14
+ else
15
+ create(data)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ State.seed %q{
22
+ +--------------+------------+
23
+ | abbreviation | name |
24
+ +--------------+------------+
25
+ | AZ | Arizona |
26
+ | NY | New York |
27
+ | CA | California |
28
+ | TX | Texas |
29
+ | CO | Colorado |
30
+ +--------------+------------+
31
+ }
32
+
33
+ Currency.seed %q{
34
+ +------+----------------+
35
+ | code | name |
36
+ +------+----------------+
37
+ | GBP | Pound Sterling |
38
+ | EUR | Euro |
39
+ | USD | US Dollar |
40
+ | CNY | Renminbi |
41
+ +------+----------------+
42
+ }
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashdown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Solomon White
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70301816154100 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70301816154100
25
+ - !ruby/object:Gem::Dependency
26
+ name: pry-nav
27
+ requirement: &70301816153640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70301816153640
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70301816152900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70301816152900
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70301815747100 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70301815747100
58
+ - !ruby/object:Gem::Dependency
59
+ name: sqlite3
60
+ requirement: &70301815746680 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70301815746680
69
+ description: Hashdown
70
+ email:
71
+ - rubysolo@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - hashdown.gemspec
82
+ - lib/hashdown.rb
83
+ - lib/hashdown/cache.rb
84
+ - lib/hashdown/finder.rb
85
+ - lib/hashdown/select_options.rb
86
+ - lib/hashdown/version.rb
87
+ - spec/lib/hashdown/cache_spec.rb
88
+ - spec/lib/hashdown/finder_spec.rb
89
+ - spec/lib/hashdown/select_options_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/support/models.rb
92
+ - spec/support/schema.rb
93
+ - spec/support/seeds.rb
94
+ homepage: https://github.com/rubysolo/hashdown
95
+ licenses: []
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ segments:
107
+ - 0
108
+ hash: -1096798538704753083
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: -1096798538704753083
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.11
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: super lightweight Rails plugin that adds hash-style lookups and option list
124
+ (for generating dropdowns) to ActiveRecord models
125
+ test_files:
126
+ - spec/lib/hashdown/cache_spec.rb
127
+ - spec/lib/hashdown/finder_spec.rb
128
+ - spec/lib/hashdown/select_options_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/support/models.rb
131
+ - spec/support/schema.rb
132
+ - spec/support/seeds.rb