remi-aux_codes 1.0.1 → 1.0.5

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 ryan "remi" Taylor
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown CHANGED
@@ -83,14 +83,20 @@ Or with an Indifferent Hash-like syntax
83
83
  TODO
84
84
  ----
85
85
 
86
- * custom fields, eg:
87
- * `Breed#acronym`
88
- * `Breed#description`
89
- * `Breed#kennel_association_id` ...
90
- * these need an easy way to be defined! `alias_attribute`?
91
- * this needs to work in development mode, when the models are constantly being reloaded!
92
- * you shouldn't have to know the name of the special field to use it ... something like `attr_string :name` could just grab the (first) string column!
93
- * create (or update) method(s) for helping with the original AuxCode migration and for creating the special additional fields
86
+ - make a spec specifically for showing off the different API features quickly and easily
87
+ - convert this README to RDoc
88
+ - custom fields, eg:
89
+ - `Breed#acronym`
90
+ - `Breed#description`
91
+ - `Breed#kennel_association_id` ...
92
+ - these need an easy way to be defined! `alias_attribute`?
93
+ - this needs to work in development mode, when the models are constantly being reloaded!
94
+ - you shouldn't have to know the name of the special field to use it ... something like `attr_string :name` could just grab the (first) string column!
95
+ - should we have a field for misc serialized meta-data? could be abused ... but could also be helpful ...
96
+ - create (or update) method(s) for helping with the original AuxCode migration and for creating the special additional fields
97
+ - make table name and column names configurable (but with strong defaults)
98
+ - should have a rails generator ... should be 1 command to setup `aux_codes` in Rails
99
+ - after i add a rails generator, i'll bump to 1.1.0 and release
94
100
 
95
101
  NOTES
96
102
  -----
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rake'
2
+ require 'rubygems'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "aux_codes"
10
+ s.summary = "ActiveRecord plugin for easily managing lots of enumeration-type data"
11
+ s.email = "remi@remitaylor.com"
12
+ s.homepage = "http://github.com/remi/aux_codes"
13
+ s.description = "ActiveRecord plugin for easily managing lots of enumeration-type data"
14
+ s.authors = %w( remi )
15
+ s.files = FileList["[A-Z]*", "{lib,spec,examples,rails_generators}/**/*"]
16
+ # s.executables = "neato"
17
+ # s.add_dependency 'person-project'
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
21
+ end
22
+
23
+ Spec::Rake::SpecTask.new do |t|
24
+ t.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ desc "Run all examples with RCov"
28
+ Spec::Rake::SpecTask.new('rcov') do |t|
29
+ t.spec_files = FileList['spec/**/*_spec.rb']
30
+ t.rcov = true
31
+ end
32
+
33
+ Rake::RDocTask.new do |rdoc|
34
+ rdoc.rdoc_dir = 'rdoc'
35
+ rdoc.title = 'aux_codes'
36
+ rdoc.options << '--line-numbers' << '--inline-source'
37
+ rdoc.rdoc_files.include('README*')
38
+ rdoc.rdoc_files.include('lib/**/*.rb')
39
+ end
40
+
41
+ desc 'Confirm that gemspec is $SAFE'
42
+ task :safe do
43
+ require 'yaml'
44
+ require 'rubygems/specification'
45
+ data = File.read('aux_codes.gemspec')
46
+ spec = nil
47
+ if data !~ %r{!ruby/object:Gem::Specification}
48
+ Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
49
+ else
50
+ spec = YAML.load(data)
51
+ end
52
+ spec.validate
53
+ puts spec
54
+ puts "OK"
55
+ end
56
+
57
+ task :default => :spec
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 5
3
3
  :major: 1
4
4
  :minor: 0
@@ -0,0 +1,150 @@
1
+ #
2
+ # extend AuxCode to return a full-blown ActiveRecord class
3
+ #
4
+ class AuxCode
5
+
6
+ def aux_code_class &block
7
+ klass = Class.new(AuxCode) do
8
+
9
+ class << self
10
+
11
+ attr_accessor :aux_code_id, :aux_code
12
+
13
+ def aux_code
14
+ # @aux_code ||= AuxCode.find aux_code_id
15
+ AuxCode.find aux_code_id
16
+ end
17
+
18
+ def [] code
19
+ aux_code[code]
20
+ end
21
+
22
+ #
23
+ # this handles typical ActiveRecord::Base method_missing features, eg: aux_code.find_by_name 'foo'
24
+ #
25
+ # we wrap these methods in the scope of this aux code (category)
26
+ #
27
+ def method_missing_with_aux_code_scope name, *args, &block
28
+ if name.to_s[/^find/]
29
+ if name.to_s[/or_create_by_/]
30
+ name = "#{name}_and_aux_code_id".to_sym
31
+ args << self.aux_code_id
32
+ # method_missing_without_aux_code_scope name, *args
33
+ AuxCode.send name, *args, &block
34
+ else
35
+ with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
36
+ method_missing_without_aux_code_scope name, *args, &block
37
+ end
38
+ end
39
+ else
40
+ method_missing_without_aux_code_scope name, *args, &block
41
+ end
42
+ rescue NoMethodError => ex
43
+ begin
44
+ aux_code.send name, *args, &block # try on the AuxCode instance for this class ...
45
+ rescue
46
+ raise ex
47
+ end
48
+ end
49
+ alias_method_chain :method_missing, :aux_code_scope
50
+
51
+ def count_with_aux_code_scope options = {}
52
+ with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
53
+ count_without_aux_code_scope options
54
+ end
55
+ end
56
+ alias_method_chain :count, :aux_code_scope
57
+
58
+ def find_with_aux_code_scope first_or_all, options = {}
59
+ with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
60
+ find_without_aux_code_scope first_or_all, options
61
+ end
62
+ end
63
+ alias_method_chain :find, :aux_code_scope
64
+
65
+ def create_with_aux_code_scope options = {}
66
+ create_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
67
+ end
68
+ alias_method_chain :create, :aux_code_scope
69
+
70
+ def create_with_aux_code_scope! options = {}
71
+ create_without_aux_code_scope! options.merge({ :aux_code_id => self.aux_code_id })
72
+ end
73
+ alias_method_chain :create!, :aux_code_scope
74
+
75
+ def new_with_aux_code_scope options = {}
76
+ begin
77
+ new_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
78
+ rescue ActiveRecord::UnknownAttributeError => ex
79
+
80
+ # we were likely passed some unknown meta attributes ... define them ...
81
+ meta_attribute_name = /unknown attribute: (.*)/.match(ex.message).captures.first
82
+ meta_attributes << meta_attribute_name
83
+ self.reload_meta_attributes!
84
+ new_with_aux_code_scope options # re-call ... WARNING ... might end up in infinite loop!
85
+
86
+ end
87
+ end
88
+ alias_method_chain :new, :aux_code_scope
89
+
90
+ end
91
+ end
92
+
93
+ klass.aux_code_id = self.id # the class needs to know its own aux_code_id
94
+
95
+ #
96
+ # add custom attributes
97
+ #
98
+ klass.class.class_eval do
99
+
100
+ # an array of valid meta attribute names
101
+ attr_accessor :meta_attributes
102
+
103
+ def attr_meta *attribute_names
104
+ @meta_attributes ||= []
105
+ @meta_attributes += attribute_names.map {|attribute_name| attribute_name.to_s }
106
+ @meta_attributes
107
+ end
108
+ end
109
+
110
+ # class customizations (if block passed in)
111
+ klass.class_eval(&block) if block
112
+
113
+ # for each of the meta_attributes defined, create getter and setter methods
114
+ #
115
+ # CAUTION: the way we're currently doing this, this'll only work if attr_meta
116
+ # is set when you initially get the aux_code_class ... adding
117
+ # meta attributes later won't currently work!
118
+ #
119
+ klass.class_eval {
120
+
121
+ def self.reload_meta_attributes!
122
+ self.meta_attributes ||= []
123
+
124
+ self.meta_attributes.each do |meta_attribute|
125
+
126
+ unless self.respond_to? meta_attribute
127
+ define_method(meta_attribute) do
128
+ get_meta_attribute(meta_attribute)
129
+ end
130
+ end
131
+
132
+ unless self.respond_to? "#{meta_attribute}="
133
+ define_method("#{meta_attribute}=") do |value|
134
+ set_meta_attribute(meta_attribute, value)
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+
141
+ reload_meta_attributes!
142
+
143
+ }
144
+
145
+ # wow, need to clean this up ...
146
+
147
+ klass
148
+ end
149
+
150
+ end
@@ -1,24 +1,31 @@
1
1
  #
2
2
  # ActiveRecord migration for creating the aux_codes table
3
3
  #
4
- class CreateAuxCodes < ActiveRecord::Migration
4
+ class AuxCodes
5
+ class CreateAuxCodes < ActiveRecord::Migration
5
6
 
6
- def self.up
7
- create_table :aux_codes, :comment => 'Auxilary Codes' do |t|
7
+ def self.up
8
+ create_table :aux_codes, :comment => 'Auxilary Codes' do |t|
8
9
 
9
- t.integer :aux_code_id, :comment => 'ID of parent aux code (Category)', :null => false
10
- t.string :name, :comment => 'Name of Category code (or child code)', :null => false
10
+ t.integer :aux_code_id, :comment => 'ID of parent aux code (Category)', :null => false
11
+ t.string :name, :comment => 'Name of Category code (or child code)', :null => false
11
12
 
12
- %w( integer decimal string text boolean datetime ).each do |field_type|
13
- t.column field_type.to_sym, "#{field_type}_field"
13
+ # disabled for now, as they're not needed - no specs using this functionality
14
+ #
15
+ # %w( integer decimal string text boolean datetime ).each do |field_type|
16
+ # t.column field_type.to_sym, "#{field_type}_field"
17
+ # end
18
+
19
+ # this should be added conditionally, based on whether or not meta attributes are desired
20
+ t.text :meta, :comment => 'Serialized meta_attributes'
21
+
22
+ t.timestamps
14
23
  end
24
+ end
15
25
 
16
- t.timestamps
26
+ def self.down
27
+ drop_table :aux_codes
17
28
  end
18
- end
19
29
 
20
- def self.down
21
- drop_table :aux_codes
22
30
  end
23
-
24
31
  end
data/lib/aux_codes.rb CHANGED
@@ -2,6 +2,16 @@ $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  %w( rubygems activerecord aux_codes/migration ).each {|lib| require lib }
4
4
 
5
+ # top-level class for AuxCodes
6
+ #
7
+ # used for namespacing and global configuration (once added)
8
+ #
9
+ class AuxCodes
10
+ end
11
+
12
+ #
13
+ # the basic AuxCode ActiveRecord class
14
+ #
5
15
  class AuxCode < ActiveRecord::Base
6
16
 
7
17
  validates_presence_of :name
@@ -28,96 +38,73 @@ class AuxCode < ActiveRecord::Base
28
38
  name.gsub(/[^[:alpha:]]/,'_').titleize.gsub(' ','').singularize
29
39
  end
30
40
 
41
+ def to_s
42
+ name
43
+ end
44
+
31
45
  def [] attribute_or_code_name
32
46
  if attributes.include?attribute_or_code_name
33
47
  attributes[attribute_or_code_name]
34
48
  else
35
49
  found = codes.select {|c| c.name.to_s =~ /#{attribute_or_code_name}/ }
36
- if found.empty? # try case insensitive (sans underscores)
37
- found = codes.select {|c| c.name.downcase.gsub('_',' ').to_s =~
38
- /#{attribute_or_code_name.to_s.downcase.gsub('_',' ')}/ }
39
- end
50
+ if found.empty? # try case insensitive (sans underscores)
51
+ found = codes.select {|c| c.name.downcase.gsub('_',' ').to_s =~
52
+ /#{attribute_or_code_name.to_s.downcase.gsub('_',' ')}/ }
53
+ end
40
54
  found.first if found
41
55
  end
42
56
  end
43
57
 
58
+ def deserialized_meta_hash
59
+ require 'yaml'
60
+ self.meta ||= ""
61
+ YAML::load(self.meta) || { }
62
+ end
63
+
64
+ def get_meta_attribute meta_attribute
65
+ deserialized_meta_hash[meta_attribute.to_s]
66
+ end
67
+
68
+ def set_meta_attribute meta_attribute, value
69
+ require 'yaml'
70
+ meta_hash = deserialized_meta_hash
71
+ meta_hash[meta_attribute.to_s] = value
72
+ self.meta = meta_hash.to_yaml
73
+ end
74
+
75
+ # this allows us to say things like:
76
+ #
77
+ # foo = AuxCode.create :name => 'foo'
78
+ # foo.codes.create :name => 'bar'
79
+ #
80
+ # foo.bar # should return the bar aux code under the foo category
81
+ #
82
+ # if bar doesn't exist, we throw a normal NoMethodError
83
+ #
84
+ # this should check meta_attributes on the object too
85
+ #
44
86
  def method_missing_with_indifferent_hash_style_values name, *args, &block
45
87
  method_missing_without_indifferent_hash_style_values name, *args, &block
46
88
  rescue NoMethodError => ex
47
89
  begin
48
- self[name]
90
+ if name.to_s[/=$/]
91
+ self.set_meta_attribute(name.to_s.sub(/=$/,''), args.first) # we said `code.foo= X` so we should set the foo meta attribute to X
92
+ save
93
+ else
94
+ code = self[name]
95
+ code = self.get_meta_attribute(name) unless code
96
+ raise ex unless code
97
+ return code
98
+ end
49
99
  rescue
50
100
  raise ex
51
101
  end
52
102
  end
53
103
  alias_method_chain :method_missing, :indifferent_hash_style_values
54
104
 
55
- def aux_code_class
56
- klass = Class.new(AuxCode) do
57
- class << self
58
- attr_accessor :aux_code_id, :aux_code
59
-
60
- def aux_code
61
- # @aux_code ||= AuxCode.find aux_code_id
62
- AuxCode.find aux_code_id
63
- end
64
-
65
- def count_with_aux_code_scope options = {}
66
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
67
- count_without_aux_code_scope options
68
- end
69
- end
70
- def method_missing_with_aux_code_scope name, *args, &block
71
- if name.to_s[/^find/]
72
- if name.to_s[/or_create_by_/]
73
- name = "#{name}_and_aux_code_id".to_sym
74
- args << self.aux_code_id
75
- # method_missing_without_aux_code_scope name, *args
76
- AuxCode.send name, *args, &block
77
- else
78
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
79
- method_missing_without_aux_code_scope name, *args, &block
80
- end
81
- end
82
- else
83
- method_missing_without_aux_code_scope name, *args, &block
84
- end
85
- rescue NoMethodError => ex
86
- begin
87
- aux_code.send name, *args, &block # try on the AuxCode instance for this class ...
88
- rescue
89
- raise ex
90
- end
91
- end
92
- def find_with_aux_code_scope first_or_all, options = {}
93
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
94
- find_without_aux_code_scope first_or_all, options
95
- end
96
- end
97
- def create_with_aux_code_scope options = {}
98
- create_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
99
- end
100
- def create_with_aux_code_scope! options = {}
101
- create_without_aux_code_scope! options.merge({ :aux_code_id => self.aux_code_id })
102
- end
103
- def new_with_aux_code_scope options = {}
104
- new_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
105
- end
106
-
107
- alias_method_chain :count, :aux_code_scope
108
- alias_method_chain :method_missing, :aux_code_scope
109
- alias_method_chain :find, :aux_code_scope
110
- alias_method_chain :create, :aux_code_scope
111
- alias_method_chain :create!, :aux_code_scope
112
- alias_method_chain :new, :aux_code_scope
113
- end
114
- end
115
-
116
- klass.aux_code_id = self.id # the class needs to know its own aux_code_id
117
- klass
118
- end
119
-
105
+ # class methods
120
106
  class << self
107
+
121
108
  def categories
122
109
  AuxCode.find_all_by_aux_code_id(0)
123
110
  end
@@ -130,8 +117,15 @@ class AuxCode < ActiveRecord::Base
130
117
  obj = category_object_or_id_or_name
131
118
  return obj if obj.is_a?AuxCode
132
119
  return AuxCode.find(obj) if obj.is_a?Fixnum
133
- return AuxCode.find_by_name_and_aux_code_id(obj, 0) if obj.is_a?String
134
- return AuxCode.find_by_name_and_aux_code_id(obj.to_s, 0) if obj.is_a?Symbol
120
+ if obj.is_a?(String) || obj.is_a?(Symbol)
121
+ obj = obj.to_s
122
+ found = AuxCode.find_by_name_and_aux_code_id(obj, 0)
123
+ if found.nil?
124
+ # try replacing underscores with spaces and doing a 'LIKE' search
125
+ found = AuxCode.find :first, :conditions => ["name LIKE ? AND aux_code_id = ?", obj.gsub('_', ' '), 0]
126
+ end
127
+ return found
128
+ end
135
129
  raise "I don't know how to find an AuxCode of type #{ obj.class }"
136
130
  end
137
131
  alias [] category
@@ -151,6 +145,12 @@ class AuxCode < ActiveRecord::Base
151
145
  end
152
146
  end
153
147
 
148
+ # this allows us to say things like:
149
+ #
150
+ # AuxCode.create :name => 'foo'
151
+ #
152
+ # AuxCode.foo # should return the foo category aux code
153
+ #
154
154
  def method_missing_with_indifferent_hash_style_values name, *args, &block
155
155
  unless self.respond_to?:aux_code_id # in which case, this is a *derived* class, not AuxCode
156
156
  begin
@@ -167,12 +167,79 @@ class AuxCode < ActiveRecord::Base
167
167
  end
168
168
  end
169
169
  alias_method_chain :method_missing, :indifferent_hash_style_values
170
+
171
+ def load_yaml yaml_string
172
+ require 'yaml'
173
+ self.load YAML::load(yaml_string)
174
+ end
175
+
176
+ def load_file serialized_yaml_file_path
177
+ load_yaml File.read(serialized_yaml_file_path)
178
+ end
179
+
180
+ # initialize AuxCodes ... looks for config/aux_codes.yml
181
+ # and creates classes
182
+ def init # should eventually take configuration options (hash || block)
183
+ aux_codes_yml = File.join 'config', 'aux_codes.yml'
184
+ if File.file? aux_codes_yml
185
+ load_file aux_codes_yml
186
+ create_classes!
187
+ end
188
+ end
189
+
190
+ #
191
+ # loads AuxCodes (creates them) from a Hash, keyed on the name of the aux code categories to create
192
+ #
193
+ # hash: a Hash or an Array [ [key,value], [key,value] ] or anything with an enumerator
194
+ # that'll work with `hash.each {|key,value| ... }`
195
+ #
196
+ def load hash
197
+ return unless hash.is_a?Hash
198
+ hash.each do |category_name, codes|
199
+ category = AuxCode.find_or_create_by_name( category_name.to_s ).aux_code_class
200
+ codes.each do |name, values|
201
+
202
+ # only a name given
203
+ if values.nil? or values.empty?
204
+ if name.is_a? String or name.is_a? Symbol # we have a String || Symbol, it's likely the code's name, eg. :foo or 'bar'
205
+ category.create :name => name.to_s unless category.code_names.include?(name.to_s)
206
+
207
+ elsif name.is_a? Hash # we have a Hash, likely with the create options, eg. { :name => 'hi', :foo =>'bar' }
208
+ category.create name
209
+
210
+ else
211
+ raise "not sure how to create code in category #{ category.name } with: #{ name.inspect }"
212
+ end
213
+
214
+ # we have a name and values
215
+ else
216
+ if values.is_a? Hash and (name.is_a? String or name.is_a? Symbol) # we have a Hash, likely with the create options ... we'll merge the name in as :name and create
217
+ code = category[ name.to_s ]
218
+ if code
219
+ values.each do |attribute, new_value|
220
+ code.send "#{attribute}=", new_value # update values
221
+ end
222
+ else
223
+ code = category.create values.merge({ :name => name.to_s })
224
+ end
225
+
226
+ else
227
+ raise "not sure how to create code in category #{ category.name } with: #{ name.inspect }, #{ values.inspect }"
228
+
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+
170
235
  end
171
236
 
172
- protected
237
+ protected
173
238
 
174
239
  def set_default_values
175
240
  self.aux_code_id = 0 unless aux_code_id
176
241
  end
177
242
 
178
243
  end
244
+
245
+ %w( aux_codes/aux_code_class ).each {|lib| require lib } # define the class returned by #aux_code_class
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Bootstraps aux_codes into Rails application
3
+
4
+ Examples:
5
+ `./script/generate aux_codes`
@@ -0,0 +1,56 @@
1
+ # This generator bootstraps aux_codes into a Rails application
2
+ class AuxCodesGenerator < Rails::Generator::Base
3
+
4
+ def initialize(runtime_args, runtime_options = {})
5
+ bootstrap
6
+ super
7
+ end
8
+
9
+ # bootstrap aux_codes into this project (add a require and a call to #init to environment.rb)
10
+ def bootstrap
11
+ environment_rb = File.join RAILS_ROOT, 'config', 'environment.rb'
12
+ if File.file? environment_rb
13
+ source = File.read environment_rb
14
+ unless source =~ /require .aux_codes./
15
+ File.open(environment_rb, 'a'){|f| f << "\nrequire 'aux_codes'\nAuxCode.init" }
16
+ puts " updated config/environment.rb"
17
+ end
18
+ end
19
+ end
20
+
21
+ # checks to see whether or not there's already a create_aux_codes migration
22
+ def migration_exists?
23
+ db_dir = File.join RAILS_ROOT, 'db'
24
+ migrations_dir = File.join db_dir, 'migrate'
25
+ return false unless File.directory? db_dir
26
+ return false unless File.directory? migrations_dir
27
+ return false if Dir[ File.join(migrations_dir, '*_create_aux_codes.rb') ].empty?
28
+ return true
29
+ end
30
+
31
+ # copy files into the project, our templates:
32
+ #
33
+ # templates/
34
+ # |-- aux_codes.yml
35
+ # `-- migration.rb
36
+ #
37
+ def manifest
38
+ record do |m|
39
+ m.directory 'config'
40
+ m.file 'aux_codes.yml', 'config/aux_codes.yml'
41
+
42
+ unless migration_exists?
43
+ timestamp = Time.now.strftime '%Y%m%d%H%M%S'
44
+ m.directory 'db/migrate'
45
+ m.file 'migration.rb', "db/migrate/#{ timestamp }_create_aux_codes.rb"
46
+ end
47
+ end
48
+ end
49
+
50
+ protected
51
+
52
+ def banner
53
+ "Usage: #{$0} aux_codes"
54
+ end
55
+
56
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # define your aux_codes!
3
+ #
4
+ # commented out below are some examples of a few of
5
+ # the different ways you can define your aux_codes!
6
+ #
7
+ ### foods:
8
+ ### Pizza:
9
+ ### taste: good
10
+ ### Dirt:
11
+ ### taste: bad
12
+ ###
13
+ ### Snack Foods:
14
+ ### - Popcorn
15
+ ### - chips
16
+ ###
17
+ ### days:
18
+ ### Monday:
19
+ ### Tuesday:
20
+ ### Wednesday:
21
+ ###
22
+ ### colors:
23
+ ### red:
24
+ ### first_letter: r
25
+ ### blue:
26
+ ### first_letter: b
@@ -0,0 +1,13 @@
1
+ # autogenerated migration (generated by ./script/generate aux_codes)
2
+ #
3
+ # db/migrate/YYYYMMDDHHMMSS_create_aux_codes.rb
4
+ #
5
+ class CreateAuxCodes < ActiveRecord::Migration
6
+ def self.up
7
+ AuxCodes::CreateAuxCodes.migrate :up
8
+ end
9
+
10
+ def self.down
11
+ AuxCodes::CreateAuxCodes.migrate :down
12
+ end
13
+ end
@@ -202,4 +202,204 @@ describe AuxCode do
202
202
  foo_category[:yay_for_bacon].should == bacon
203
203
  end
204
204
 
205
+ it 'an aux_code_class should raise a NoMethodError, as per usual, if an undefined method is called' do
206
+ chunky = AuxCode.create!( :name => 'chunky' ).aux_code_class
207
+ lambda { chunky.new.bacon }.should raise_error(NoMethodError)
208
+ end
209
+
210
+ it 'should be able to pass a block to aux_code_class for quick and easy class customization' do
211
+ chunky = AuxCode.create!( :name => 'chunky' ).aux_code_class do
212
+ def bacon
213
+ "chunky bacon!"
214
+ end
215
+ end
216
+ lambda { chunky.new.bacon }.should_not raise_error(NoMethodError)
217
+ chunky.new.bacon.should == 'chunky bacon!'
218
+ end
219
+
220
+ it 'should be able to pass a block to aux_code_class for quick and easy eigenclass customization' do
221
+ chunky = AuxCode.create!( :name => 'chunky' ).aux_code_class do
222
+ def self.bacon
223
+ "chunky bacon!"
224
+ end
225
+ end
226
+ chunky.bacon.should == 'chunky bacon!'
227
+ lambda { chunky.new.bacon }.should raise_error(NoMethodError) # bacon is a class method
228
+ end
229
+
230
+ it 'should be able to define a meta attribute' do
231
+ breed = AuxCode.create!( :name => 'breed' ).aux_code_class do
232
+ attr_meta :acronym
233
+ end
234
+
235
+ apbt = breed.create :name => 'American Pit Bull Terrier', :acronym => 'APBT'
236
+ breed.find_by_name('American Pit Bull Terrier').should == apbt
237
+ breed.find_by_name('American Pit Bull Terrier').acronym.should == 'APBT'
238
+ end
239
+
240
+ it 'should be able to define multiple meta attributes' do
241
+ breed = AuxCode.create!( :name => 'breed' ).aux_code_class do
242
+ attr_meta :acronym, :foo
243
+ end
244
+
245
+ apbt = breed.create :name => 'American Pit Bull Terrier', :acronym => 'APBT', :foo => 'bar'
246
+ breed.find_by_name('American Pit Bull Terrier').should == apbt
247
+ breed.find_by_name('American Pit Bull Terrier').acronym.should == 'APBT'
248
+ breed.find_by_name('American Pit Bull Terrier').foo.should == 'bar'
249
+ end
250
+
251
+ it 'to_s should return the name of an aux_code' do
252
+ AuxCode.new( :name => 'i Am the Name' ).to_s.should == 'i Am the Name'
253
+
254
+ x = AuxCode.create :name => 'foo'
255
+ y = x.codes.create :name => 'bar'
256
+ x.to_s.should == 'foo'
257
+ y.to_s.should == 'bar'
258
+ end
259
+
260
+ it 'should be able to load AuxCodes from a Hash' do
261
+ AuxCode.count.should == 0
262
+ AuxCode.load({
263
+
264
+ # symbol => String Array
265
+ :days => %w( Monday Tuesday Wednesday ),
266
+
267
+ # symbol => Hash
268
+ :colors => {
269
+
270
+ # symbol => Hash options
271
+ :red => { :first_letter => 'r' },
272
+ :blue => { :first_letter => 'b' }
273
+
274
+ },
275
+
276
+ # symbol => Array of hashes
277
+ :foods => [
278
+ { :name => 'Pizza', :taste => 'good' },
279
+ { :name => 'Dirt', :taste => 'bad' }
280
+ ],
281
+
282
+ 'Snack Foods' => %w( Popcorn chips )
283
+
284
+ })
285
+
286
+ AuxCode.category_names.should include('days')
287
+ AuxCode.category_names.should include('colors')
288
+ AuxCode.category_names.should include('foods')
289
+
290
+ AuxCode[:days].codes.length.should == 3
291
+ AuxCode[:days].code_names.should include('Monday')
292
+ AuxCode[:days].monday.name.should == 'Monday'
293
+ AuxCode[:days][:monday].name.should == 'Monday'
294
+
295
+ AuxCode[:colors][:red].name.should == 'red'
296
+ AuxCode[:colors][:red].first_letter.should == 'r'
297
+
298
+ AuxCode[:colors].aux_code_class.red.name.should == 'red'
299
+ AuxCode[:colors].aux_code_class.red.first_letter.should == 'r'
300
+ AuxCode[:colors].aux_code_class[:red].first_letter.should == 'r'
301
+
302
+ AuxCode.foods.pizza.taste.should == 'good'
303
+ AuxCode.foods.dirt.taste.should == 'bad'
304
+
305
+ AuxCode['Snack Foods'].codes.length.should == 2
306
+ AuxCode.category('Snack Foods').codes.length.should == 2
307
+ AuxCode.snack_foods.codes.length.should == 2
308
+ AuxCode.snack_foods.code_names.should include('Popcorn')
309
+ AuxCode.snack_foods[:popcorn].name.should == 'Popcorn'
310
+ AuxCode[:snack_foods].popcorn.name.should == 'Popcorn'
311
+ AuxCode['Snack Foods'].popcorn.name.should == 'Popcorn'
312
+
313
+ end
314
+
315
+ it 'should be able to load AuxCodes from a Yaml string' do
316
+ require 'yaml'
317
+ AuxCode.count.should == 0
318
+ yaml = {
319
+
320
+ # symbol => String Array
321
+ :days => %w( Monday Tuesday Wednesday ),
322
+
323
+ # symbol => Hash
324
+ :colors => {
325
+
326
+ # symbol => Hash options
327
+ :red => { :first_letter => 'r' },
328
+ :blue => { :first_letter => 'b' }
329
+
330
+ },
331
+
332
+ # symbol => Array of hashes
333
+ :foods => [
334
+ { :name => 'Pizza', :taste => 'good' },
335
+ { :name => 'Dirt', :taste => 'bad' }
336
+ ],
337
+
338
+ 'Snack Foods' => %w( Popcorn chips )
339
+
340
+ }.to_yaml
341
+ AuxCode.load_yaml yaml
342
+
343
+ AuxCode.category_names.should include('days')
344
+ AuxCode[:days].code_names.should include('Monday')
345
+ AuxCode[:days][:monday].name.should == 'Monday'
346
+ AuxCode[:colors][:red].name.should == 'red'
347
+ AuxCode[:colors][:red].first_letter.should == 'r'
348
+ AuxCode[:colors].aux_code_class.red.first_letter.should == 'r'
349
+ AuxCode.foods.pizza.taste.should == 'good'
350
+ AuxCode.category('Snack Foods').codes.length.should == 2
351
+ AuxCode.snack_foods.code_names.should include('Popcorn')
352
+ AuxCode.snack_foods[:popcorn].name.should == 'Popcorn'
353
+ end
354
+
355
+ it 'should be able to load AuxCodes from a Yaml file' do
356
+ AuxCode.count.should == 0
357
+
358
+ AuxCode.load_file File.dirname(__FILE__) + '/example_aux_codes.yml'
359
+
360
+ AuxCode.category_names.should include('days')
361
+ AuxCode[:days].code_names.should include('Monday')
362
+ AuxCode[:days][:monday].name.should == 'Monday'
363
+ AuxCode[:colors][:red].name.should == 'red'
364
+ AuxCode[:colors][:red].first_letter.should == 'r'
365
+ AuxCode[:colors].aux_code_class.red.first_letter.should == 'r'
366
+ AuxCode.foods.pizza.taste.should == 'good'
367
+ AuxCode.category('Snack Foods').codes.length.should == 2
368
+ AuxCode.snack_foods.code_names.should include('Popcorn')
369
+ AuxCode.snack_foods[:popcorn].name.should == 'Popcorn'
370
+ end
371
+
372
+ it 'should be able to update meta attribute easily' do
373
+ AuxCode.load_file File.dirname(__FILE__) + '/more_example_aux_codes.yml'
374
+ AuxCode.foods.pizza.taste.should == 'good'
375
+ AuxCode.foods.pizza.taste = 'yummy'
376
+ AuxCode.foods.pizza.taste.should == 'yummy'
377
+ end
378
+
379
+ it 'should be able to load AuxCodes from a [slightly different] Yaml file' do
380
+ AuxCode.count.should == 0
381
+
382
+ AuxCode.load_file File.dirname(__FILE__) + '/more_example_aux_codes.yml'
383
+
384
+ AuxCode.category_names.should include('days')
385
+ AuxCode[:days].code_names.should include('Monday')
386
+ AuxCode[:days][:monday].name.should == 'Monday'
387
+ AuxCode[:colors][:red].name.should == 'red'
388
+ AuxCode[:colors][:red].first_letter.should == 'r'
389
+ AuxCode[:colors].aux_code_class.red.first_letter.should == 'r'
390
+ AuxCode.foods.pizza.taste.should == 'good'
391
+ AuxCode.category('Snack Foods').codes.length.should == 2
392
+ AuxCode.snack_foods.code_names.should include('Popcorn')
393
+ AuxCode.snack_foods[:popcorn].name.should == 'Popcorn'
394
+
395
+ # quick test - overridable ...
396
+
397
+ AuxCode.load({
398
+ :colors => {
399
+ :red => { :first_letter => 'CHANGED' }
400
+ }
401
+ })
402
+ AuxCode[:colors][:red].first_letter.should == 'CHANGED'
403
+ end
404
+
205
405
  end
@@ -0,0 +1,18 @@
1
+ ---
2
+ :foods:
3
+ - :taste: good
4
+ :name: Pizza
5
+ - :taste: bad
6
+ :name: Dirt
7
+ :colors:
8
+ :red:
9
+ :first_letter: r
10
+ :blue:
11
+ :first_letter: b
12
+ Snack Foods:
13
+ - Popcorn
14
+ - chips
15
+ :days:
16
+ - Monday
17
+ - Tuesday
18
+ - Wednesday
@@ -0,0 +1,22 @@
1
+ # cleaned up a bit ... shows different ways you can create the yaml for your aux codes
2
+
3
+ foods:
4
+ Pizza:
5
+ taste: good
6
+ Dirt:
7
+ taste: bad
8
+
9
+ Snack Foods:
10
+ - Popcorn
11
+ - chips
12
+
13
+ days:
14
+ Monday:
15
+ Tuesday:
16
+ Wednesday:
17
+
18
+ colors:
19
+ red:
20
+ first_letter: r
21
+ blue:
22
+ first_letter: b
data/spec/spec_helper.rb CHANGED
@@ -2,8 +2,8 @@ require File.dirname(__FILE__) + '/../lib/aux_codes'
2
2
  require 'spec'
3
3
 
4
4
  ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
5
- CreateAuxCodes.verbose = false
6
- CreateAuxCodes.migrate :up
5
+ AuxCodes::CreateAuxCodes.verbose = false
6
+ AuxCodes::CreateAuxCodes.migrate :up
7
7
 
8
8
  # use transactions
9
9
  AuxCode; # hit one of the AR classes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remi-aux_codes
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - remi
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-27 00:00:00 -08:00
12
+ date: 2009-02-08 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,13 +22,24 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
+ - Rakefile
25
26
  - VERSION.yml
26
27
  - README.markdown
28
+ - LICENSE
27
29
  - lib/aux_codes
30
+ - lib/aux_codes/aux_code_class.rb
28
31
  - lib/aux_codes/migration.rb
29
32
  - lib/aux_codes.rb
33
+ - spec/more_example_aux_codes.yml
30
34
  - spec/aux_code_spec.rb
31
35
  - spec/spec_helper.rb
36
+ - spec/example_aux_codes.yml
37
+ - rails_generators/aux_codes
38
+ - rails_generators/aux_codes/aux_codes_generator.rb
39
+ - rails_generators/aux_codes/USAGE
40
+ - rails_generators/aux_codes/templates
41
+ - rails_generators/aux_codes/templates/aux_codes.yml
42
+ - rails_generators/aux_codes/templates/migration.rb
32
43
  has_rdoc: true
33
44
  homepage: http://github.com/remi/aux_codes
34
45
  post_install_message: