magic_meta_methods 1.0.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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Nick Zalabak
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.
data/README ADDED
@@ -0,0 +1,64 @@
1
+ = magic_meta_methods
2
+
3
+ == Instructions
4
+
5
+ A plugin to generate many methods or "attributes" on ActiveRecord that
6
+ persists data into a serialized text based column as defined by :column. This
7
+ plugin is particularly useful for storing display data or meta data that isn't intended to be queried
8
+ directly via SQL. It reduces the number of "one-off" columns and the clutter of serialized declarations
9
+ with getter/setters on your model.
10
+
11
+ == Installation
12
+
13
+ ruby script/plugin install git://github.com/techwhizbang/magic_meta_methods.git
14
+
15
+ == Usage
16
+
17
+ === Prepare database
18
+
19
+ Add this migration to the desired table(s):
20
+
21
+ class MagicMetaMethods < ActiveRecord::Migration
22
+ def self.up
23
+ add_column :your_table_here, :magic_meta_methods, :mediumtext
24
+ end
25
+
26
+ def self.down
27
+ remove_column :your_table_here, :magic_meta_methods
28
+ end
29
+ end
30
+
31
+ rake db:migrate
32
+
33
+ === Meta Magic Methods in action
34
+
35
+ Let's suppose your model needs to store a variety of data and types, but
36
+ these values don't need to be queried on, many times these values are purely
37
+ for display or front end purposes.
38
+
39
+ class YourModel < ActiveRecord::Base
40
+ magic_meta_methods([:abstract,
41
+ [:some_hash, :hash],
42
+ [:some_array, :array],
43
+ [:some_indifferent_hash, :indifferent_hash]], :column => 'alternate_meta')
44
+ end
45
+
46
+ We can now use the magic meta methods just like a regular attribute setter
47
+
48
+ m = YourModel.new
49
+ m.abstract= "This is a brief synopsis of magic meta methods"
50
+ m.some_hash = {:a => 1, :b => 2, :c => 3}
51
+ m.some_array = [["Monday", "9-5"], ["Tuesday", "9-5"], ["Wed", "Closed"]]
52
+ m.some_indifferent_hash = {:dog => "Fido", :cat => "Garfield"}
53
+ m.save
54
+
55
+ You can retrieve the values
56
+
57
+ m.some_indifferent_hash['dog'] #Fido
58
+ m.abstract #This is a brief synopsis of magic meta methods
59
+ m.some_array[0] #["Monday", "9-5"]
60
+
61
+ === Other
62
+
63
+ Problems, comments, and suggestions all welcome. techwhizbang [at] gmail dot com or visit my blog
64
+ http://techwhizbang.com
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |s|
7
+ s.name = "magic_meta_methods"
8
+ s.summary = "A gem that polishes ActiveRecord data serialization to make it really awesome"
9
+ s.description = "magic_meta_methods uses ActiveRecord data serialization and allows for several objects by key to be stored in _one_ column while still providing the same attr_accessor behavior as if there were a column for _each_ serialized object - hence the magic 'meta' methods"
10
+ s.email = "techwhizbang@gmail.com"
11
+ s.homepage = "http://github.com/techwhizbang/magic_meta_methods"
12
+ s.rubyforge_project = 'magic_meta_methods'
13
+ s.authors = ["Nick Zalabak"]
14
+ s.files = FileList["[A-Za-z]*", "{lib,test}/**/*"]
15
+ s.test_files = FileList["test/**/*"]
16
+ s.add_dependency "activerecord", ">= 2.3.2"
17
+ end
18
+
19
+ rescue LoadError
20
+ puts "Jeweler not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs << 'lib'
25
+ t.pattern = 'test/**/*_test.rb'
26
+ t.verbose = false
27
+ end
28
+
29
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'activerecord'
2
+ require 'magic_meta_methods'
@@ -0,0 +1,85 @@
1
+ module ActiveRecord
2
+
3
+ module MagicMetaMethods
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ class << base
8
+ attr_accessor :meta_methods_column
9
+ attr_accessor :meta_methods_declarations
10
+ end
11
+ base.send(:before_create, :init_meta_methods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def magic_meta_methods(methods=[], options={})
16
+
17
+ column_name = options[:column].blank? ? :magic_meta_methods : options[:column].to_sym
18
+ self.meta_methods_column = column_name
19
+ self.meta_methods_declarations = methods
20
+ self.meta_methods_declarations.each do |attribute|
21
+ if attribute.is_a?(Array)
22
+ name = attribute[0]
23
+ type = attribute[1]
24
+ else
25
+ #default to string since its the most common usage
26
+ name = attribute
27
+ type = :string
28
+ end
29
+
30
+ serialize column_name
31
+ define_method(name.to_sym) { self.get_meta_value(name, type)}
32
+ define_method((name.to_s + '=').to_sym) {|val| self.set_meta_value(name, val, type)}
33
+ end
34
+ end
35
+ end
36
+
37
+ def init_meta_methods
38
+ self[self.class.meta_methods_column] = {} unless self[self.class.meta_methods_column].kind_of?(Hash)
39
+ end
40
+
41
+ def set_meta_value(method_name, val, type)
42
+ init_meta_methods #should never happen, but a failsafe
43
+ self[self.class.meta_methods_column][method_name] = convert_to_type(val, type)
44
+ end
45
+
46
+ def get_meta_value(method_name, type)
47
+ return nil unless self[self.class.meta_methods_column].kind_of?(Hash)
48
+ convert_to_type(self[self.class.meta_methods_column][method_name], type)
49
+ end
50
+
51
+ def convert_to_type (val, type)
52
+ return nil if val.nil? or (val.is_a?(String) and val.blank?)
53
+ converted_value = nil
54
+ case type
55
+ when :string
56
+ converted_value = val.to_s
57
+ when :integer
58
+ converted_value = val.to_i
59
+ when :float
60
+ converted_value = val.to_f
61
+ when :date
62
+ converted_value = val.is_a?(Date) ? val : Date.parse(val.to_s, true)
63
+ when :time
64
+ converted_value = val.is_a?(Time) ? val : Time.parse(val.to_s, true)
65
+ when :datetime
66
+ converted_value = val.is_a?(DateTime) ? val : DateTime.parse(val.to_s)
67
+ when :indifferent_hash
68
+ converted_value = val.is_a?(HashWithIndifferentAccess) ? val : (raise InvalidMetaTypeError.new("Invalid HashWithIndifferentAccess #{val}"))
69
+ when :hash
70
+ converted_value = val.is_a?(Hash) ? val : (raise InvalidMetaTypeError.new("Invalid Hash #{val}"))
71
+ when :array
72
+ converted_value = val.is_a?(Array) ? val : [val]
73
+ when val.is_a?(type)
74
+ converted_value = val
75
+ else
76
+ raise InvalidMetaTypeError.new("Invalid type #{type.name}")
77
+ end
78
+ converted_value
79
+ end
80
+ end
81
+ end
82
+
83
+ class InvalidMetaTypeError < ArgumentError; end
84
+
85
+ ActiveRecord::Base.send(:include, ActiveRecord::MagicMetaMethods)
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{magic_meta_methods}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nick Zalabak"]
12
+ s.date = %q{2010-03-09}
13
+ s.description = %q{magic_meta_methods uses ActiveRecord data serialization and allows for several objects by key to be stored in _one_ column while still providing the same attr_accessor behavior as if there were a column for _each_ serialized object - hence the magic 'meta' methods}
14
+ s.email = %q{techwhizbang@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "init.rb",
24
+ "lib/magic_meta_methods.rb",
25
+ "magic_meta_methods.gemspec",
26
+ "test/magic_meta_methods_test.rb"
27
+ ]
28
+ s.homepage = %q{http://github.com/techwhizbang/magic_meta_methods}
29
+ s.rdoc_options = ["--charset=UTF-8"]
30
+ s.require_paths = ["lib"]
31
+ s.rubyforge_project = %q{magic_meta_methods}
32
+ s.rubygems_version = %q{1.3.5}
33
+ s.summary = %q{A gem that polishes ActiveRecord data serialization to make it really awesome}
34
+ s.test_files = [
35
+ "test/magic_meta_methods_test.rb"
36
+ ]
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.3.2"])
44
+ else
45
+ s.add_dependency(%q<activerecord>, [">= 2.3.2"])
46
+ end
47
+ else
48
+ s.add_dependency(%q<activerecord>, [">= 2.3.2"])
49
+ end
50
+ end
51
+
@@ -0,0 +1,174 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ gem 'activerecord'
5
+ require 'active_record'
6
+
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/magic_meta_methods")
8
+
9
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
10
+
11
+ def setup_db
12
+ ActiveRecord::Schema.define(:version => 1) do
13
+ create_table :base_meta_models do |t|
14
+ t.column :magic_meta_methods, :text
15
+ t.column :type, :string
16
+ t.column :alternate_meta, :text
17
+ end
18
+ end
19
+ end
20
+
21
+ def teardown_db
22
+ ActiveRecord::Base.connection.tables.each do |table|
23
+ ActiveRecord::Base.connection.drop_table(table)
24
+ end
25
+ end
26
+
27
+ class BaseMetaModel < ActiveRecord::Base
28
+ magic_meta_methods([
29
+ :a_string,
30
+ [:a_hash, :hash],
31
+ [:hours, :array],
32
+ [:cats, :indifferent_hash]]
33
+ )
34
+ end
35
+
36
+ class MetaModel < BaseMetaModel
37
+ magic_meta_methods([
38
+ :a_string,
39
+ [:a_hash, :hash],
40
+ [:hours, :array],
41
+ [:cats, :indifferent_hash]]
42
+ )
43
+ end
44
+
45
+ class MetaModelChild < MetaModel
46
+ magic_meta_methods(
47
+ [:a_string]
48
+ )
49
+ end
50
+
51
+ class EmptyMetaModel < MetaModel
52
+ magic_meta_methods
53
+ end
54
+
55
+ class AlternateColumnMetaModel < BaseMetaModel
56
+ magic_meta_methods([:a_string,
57
+ [:a_hash, :hash],
58
+ [:hours, :array],
59
+ [:cats, :indifferent_hash]], :column => 'alternate_meta')
60
+ end
61
+
62
+ class MagicMetaMethodsTest < Test::Unit::TestCase
63
+
64
+ def setup
65
+ setup_db
66
+ (1..4).each do |counter|
67
+ m = MetaModel.create!
68
+ m.a_string = "yes"
69
+ m.a_hash = {:a => 1}
70
+ m.hours = ["1", "2", "3"]
71
+ m.cats = HashWithIndifferentAccess.new(:cat => "cat", :cat2 => "cat2", "cat3" => "cat3")
72
+ m.save!
73
+ end
74
+ MetaModelChild.create!
75
+ end
76
+
77
+ def teardown
78
+ teardown_db
79
+ end
80
+
81
+ def test_alternate_column_name
82
+ assert_equal AlternateColumnMetaModel.meta_methods_column, :alternate_meta
83
+ end
84
+
85
+ def test_meta_method_persistance
86
+ assert_equal "yes", MetaModel.first.a_string
87
+ end
88
+
89
+ def test_alternate_column_create_with_meta_data
90
+ a = AlternateColumnMetaModel.create!(:a_string => 'hello', :a_hash => {:a => 1, :b => 2})
91
+ a = AlternateColumnMetaModel.first
92
+ assert_equal('hello', a.a_string)
93
+ assert_equal({:a => 1, :b => 2}, a.a_hash)
94
+ assert_not_nil(a.alternate_meta)
95
+ end
96
+
97
+ def test_meta_method_hashes
98
+ assert_equal({:a => 1}, MetaModel.first.a_hash)
99
+ end
100
+
101
+ def test_meta_method_indifferent_hashes
102
+ assert_equal({"cat"=>"cat", "cat2"=>"cat2", "cat3"=>"cat3"}, MetaModel.first.cats)
103
+ end
104
+
105
+
106
+ def test_update_meta_method_value
107
+ m = MetaModel.first
108
+ m.a_string = "changed"
109
+ m.save!
110
+ assert_equal "changed", MetaModel.first.a_string
111
+ end
112
+
113
+ def test_empty_meta_method_should_return_nil
114
+ assert_equal nil, MetaModelChild.first.a_string
115
+ end
116
+
117
+ def test_blank_declaration_of_meta_methods_class
118
+ assert_nothing_raised do
119
+ EmptyMetaModel.create!
120
+ end
121
+ end
122
+
123
+ def test_init_meta_methods_should_return_hash_if_nil
124
+ assert_equal({}, MetaModel.new.send(:init_meta_methods))
125
+ end
126
+
127
+ def test_should_convert_string_type
128
+ assert_equal("String", MetaModel.new.send(:convert_to_type, "String", :string))
129
+ end
130
+
131
+ def test_should_convert_hash_type
132
+ assert_equal({:a => 1, :b => 2}, MetaModel.new.send(:convert_to_type, {:a => 1, :b => 2}, :hash))
133
+ end
134
+
135
+ def test_should_convert_date_type
136
+ d = Date.parse("12/5/1982")
137
+ assert_equal(d, MetaModel.new.send(:convert_to_type, d, :date))
138
+ end
139
+
140
+ def test_should_raise_an_error_if_an_invalid_hash
141
+ d = Date.parse("2/5/1998")
142
+ assert_raise(InvalidMetaTypeError) do
143
+ MetaModel.new.send(:convert_to_type, d, :hash)
144
+ end
145
+ end
146
+
147
+ def test_should_convert_time_type
148
+ t = Time.now
149
+ assert_equal(t, MetaModel.new.send(:convert_to_type, t, :time))
150
+ end
151
+
152
+ def test_should_convert_datetime_type
153
+ t = DateTime.now
154
+ assert_equal(t, MetaModel.new.send(:convert_to_type, t, :datetime))
155
+ end
156
+
157
+ def test_should_convert_array_type
158
+ a = [1, 2, "3"]
159
+ assert_equal(a, MetaModel.new.send(:convert_to_type, a, :array))
160
+ end
161
+
162
+ def test_should_convert_integer_type
163
+ assert_equal(1, MetaModel.new.send(:convert_to_type, 1, :integer))
164
+ end
165
+
166
+ def test_should_convert_float_type
167
+ assert_equal(1.123456789, MetaModel.new.send(:convert_to_type, 1.123456789, :float) )
168
+ end
169
+
170
+ def test_should_convert_hash_with_indifferent_access
171
+ hida = HashWithIndifferentAccess.new(:a => 1, :b => 2)
172
+ assert_equal(hida, MetaModel.new.send(:convert_to_type, hida, :indifferent_hash))
173
+ end
174
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: magic_meta_methods
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Zalabak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-09 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.2
24
+ version:
25
+ description: magic_meta_methods uses ActiveRecord data serialization and allows for several objects by key to be stored in _one_ column while still providing the same attr_accessor behavior as if there were a column for _each_ serialized object - hence the magic 'meta' methods
26
+ email: techwhizbang@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ files:
34
+ - MIT-LICENSE
35
+ - README
36
+ - Rakefile
37
+ - VERSION
38
+ - init.rb
39
+ - lib/magic_meta_methods.rb
40
+ - magic_meta_methods.gemspec
41
+ - test/magic_meta_methods_test.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/techwhizbang/magic_meta_methods
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: magic_meta_methods
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A gem that polishes ActiveRecord data serialization to make it really awesome
70
+ test_files:
71
+ - test/magic_meta_methods_test.rb