has_magic_columns 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/CONTRIBUTORS.md +37 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +121 -0
- data/Rakefile +25 -0
- data/has_magic_columns.gemspec +27 -0
- data/init.rb +1 -0
- data/lib/app/models/magic_attribute.rb +29 -0
- data/lib/app/models/magic_attribute_relationship.rb +4 -0
- data/lib/app/models/magic_column.rb +33 -0
- data/lib/app/models/magic_column_relationship.rb +5 -0
- data/lib/app/models/magic_option.rb +7 -0
- data/lib/generators/has_magic_columns/install/USAGE +8 -0
- data/lib/generators/has_magic_columns/install/install_generator.rb +24 -0
- data/lib/generators/has_magic_columns/install/templates/migration.rb +53 -0
- data/lib/has_magic_columns/has_magic_columns.rb +172 -0
- data/lib/has_magic_columns/railtie.rb +22 -0
- data/lib/has_magic_columns/version.rb +3 -0
- data/lib/has_magic_columns.rb +24 -0
- data/test/has_magic_columns_test.rb +8 -0
- metadata +178 -0
data/.gitignore
ADDED
data/CONTRIBUTORS.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Pull Requests
|
2
|
+
|
3
|
+
We love pull requests. Here's a quick guide:
|
4
|
+
|
5
|
+
1. Fork the repo.
|
6
|
+
|
7
|
+
2. Run the tests. We only take pull requests with passing tests, and it's great
|
8
|
+
to know that you have a clean slate: `bundle && rake`
|
9
|
+
|
10
|
+
3. Add a test for your change. Only refactoring and documentation changes
|
11
|
+
require no new tests. If you are adding functionality or fixing a bug, we need
|
12
|
+
a test!
|
13
|
+
|
14
|
+
4. Make the test pass.
|
15
|
+
|
16
|
+
5. Push to your fork and submit a pull request.
|
17
|
+
|
18
|
+
|
19
|
+
At this point you're waiting on us. We'll be in contact as soon as we are able.
|
20
|
+
We may suggest some changes, improvements, alternatives.
|
21
|
+
|
22
|
+
Some things that will increase the chance that your pull request is accepted,
|
23
|
+
taken straight from the Ruby on Rails guide:
|
24
|
+
|
25
|
+
* Use Rails idioms and helpers
|
26
|
+
* Include tests that fail without your code, and pass with it
|
27
|
+
* Update the documentation, the surrounding one, examples elsewhere, guides,
|
28
|
+
whatever is affected by your contribution
|
29
|
+
|
30
|
+
# Conventions
|
31
|
+
|
32
|
+
* Foremost, follow the conventions you see in the source.
|
33
|
+
* Two spaces, no tabs.
|
34
|
+
* No trailing whitespace. Blank lines should not have any space.
|
35
|
+
* Prefer `&&`/`||` over `and`/`or`.
|
36
|
+
* `MyClass.my_method(my_arg)` not `my_method( my_arg )` or `my_method my_arg`.
|
37
|
+
* `a = b` instead of `a=b`.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Brandon Keene <bkeene AT gmail DOT com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
has_magic_columns
|
2
|
+
===============
|
3
|
+
|
4
|
+
Allows the addition of custom "magic" columns and attributes on a per-model
|
5
|
+
or per-parent-model basis. This is useful for situations where custom fields are
|
6
|
+
required for a specific model or for multi-user, multi-account environments where
|
7
|
+
accounts can customize attributes for subordinate models.
|
8
|
+
|
9
|
+
**NOTE**: This plugin is probably not production-ready. While all the described
|
10
|
+
features work, not all features have been tested. It was originally a Rails 2
|
11
|
+
plugin written by Brandon Keene and has been updated to a gem with Rails 3
|
12
|
+
support.
|
13
|
+
|
14
|
+
Installation
|
15
|
+
============
|
16
|
+
|
17
|
+
Add to your Gemfile:
|
18
|
+
|
19
|
+
gem "has_magic_columns"
|
20
|
+
|
21
|
+
Create the migrations for MagicColumns and migrate:
|
22
|
+
|
23
|
+
rails g has_magic_columns:install
|
24
|
+
rake db:migrate
|
25
|
+
|
26
|
+
Usage
|
27
|
+
=====
|
28
|
+
|
29
|
+
## Model
|
30
|
+
|
31
|
+
Sprinkle a little magic into an existing model:
|
32
|
+
|
33
|
+
class User < ActiveRecord::Base
|
34
|
+
has_magic_columns
|
35
|
+
end
|
36
|
+
|
37
|
+
Add magic columns to your model:
|
38
|
+
|
39
|
+
@bob = User.create(:email => "bob@example.com")
|
40
|
+
@bob.magic_columns.create(:name => "first_name")
|
41
|
+
|
42
|
+
Supply additional options if you have more specific requirements for your columns:
|
43
|
+
|
44
|
+
@bob.magic_columns.create(:name => "last_name", :is_required => true)
|
45
|
+
@bob.magic_columns.create(:name => "birthday", :datatype => :date)
|
46
|
+
@bob.magic_columns.create(:name => "salary", :default => "40000", :pretty_name => "Yearly Salary")
|
47
|
+
|
48
|
+
The :datatype option supports :check_box_boolean, :date, :datetime, or :integer.
|
49
|
+
|
50
|
+
Use your new columns just like you would with any other ActiveRecord attribute:
|
51
|
+
|
52
|
+
@bob.first_name = "Bob"
|
53
|
+
@bob.last_name = "Magic!"
|
54
|
+
@bob.birthday = Date.today
|
55
|
+
@bob.save
|
56
|
+
|
57
|
+
Find @bob and inspect him:
|
58
|
+
|
59
|
+
@bob = User.find(@bob.id)
|
60
|
+
@bob.first_name #=> "Bob"
|
61
|
+
@bob.last_name #=> "Magic!"
|
62
|
+
@bob.birthday #=> #<Date: 4908497/2,0,2299161>
|
63
|
+
@bob.salary #=> "40000", this is from :salary having a :default
|
64
|
+
|
65
|
+
## Inherited Model
|
66
|
+
|
67
|
+
A child can inherit magic columns from a parent. To do this, declare the parent
|
68
|
+
as having magic columns:
|
69
|
+
|
70
|
+
class Account < ActiveRecord::Base
|
71
|
+
has_many :users
|
72
|
+
has_magic_columns
|
73
|
+
end
|
74
|
+
|
75
|
+
And declare the child as having magic columns :through the parent.
|
76
|
+
|
77
|
+
class User < ActiveRecord::Base
|
78
|
+
belongs_to :account
|
79
|
+
has_magic_columns :through => :account
|
80
|
+
end
|
81
|
+
|
82
|
+
To see all the magic columns available for a child from its parent:
|
83
|
+
|
84
|
+
@user.magic_columns #=> [#<MagicColumn>,...]
|
85
|
+
@user.account.magic_columns #=> [#<MagicColumn>,...]
|
86
|
+
|
87
|
+
To add magic columns, go through the parent or child:
|
88
|
+
|
89
|
+
@user.magic_columns.create(...)
|
90
|
+
@user.account.magic_columns.create(...)
|
91
|
+
|
92
|
+
All children for a given parent will have access to the same magic columns:
|
93
|
+
|
94
|
+
@account = Account.create(:name => "BobCorp")
|
95
|
+
|
96
|
+
@bob = User.create(:name => "bob", :account => @account)
|
97
|
+
@bob.magic_columns.create(:name => "salary")
|
98
|
+
@bob.salary = "40000"
|
99
|
+
|
100
|
+
@steve = User.create(:name => "bob", :account => @account)
|
101
|
+
# no need to add the column again
|
102
|
+
@steve.salary = "50000"
|
103
|
+
|
104
|
+
To Do
|
105
|
+
=====
|
106
|
+
|
107
|
+
This gem is mostly functional. Here's a short list of things that need to be
|
108
|
+
done to polish it up:
|
109
|
+
|
110
|
+
* Test
|
111
|
+
* Benchmark and optimize
|
112
|
+
|
113
|
+
Maintainers
|
114
|
+
===========
|
115
|
+
|
116
|
+
* Drew Ulmer ([latortuga](http://github.com/latortuga))
|
117
|
+
* Nicholas Zielinski ([zieski](http://github.com/zieski))
|
118
|
+
|
119
|
+
Contribute
|
120
|
+
==========
|
121
|
+
See the [CONTRIBUTORS guide](https://github.com/latortuga/has_magic_columns/blob/master/CONTRIBUTORS.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rake'
|
2
|
+
gem 'rdoc'
|
3
|
+
require 'rdoc/task'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
require 'bundler'
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
#desc 'Default: run unit tests.'
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
Rake::TestTask.new do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Generate documentation.'
|
19
|
+
RDoc::Task.new do |rdoc|
|
20
|
+
rdoc.main = "README.md"
|
21
|
+
rdoc.rdoc_files.include("README.md","lib/**/*.rb")
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = 'HasMagicColumns'
|
24
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'has_magic_columns/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "has_magic_columns"
|
7
|
+
s.version = HasMagicColumns::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Drew Ulmer","Nicholas Zielinski","Brandon Keene"]
|
10
|
+
s.email = ["latortuga@gmail.com","zieski@gmail.com"]
|
11
|
+
s.homepage = %q{https://github.com/latortuga/has_magic_columns}
|
12
|
+
s.summary = "Custom fields for Rails 3"
|
13
|
+
s.description = "Allow addition of custom 'magic' columns to ActiveRecord models."
|
14
|
+
s.date = Date.today
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/`.split("\n")
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency("activesupport", ["~> 3.0"])
|
21
|
+
s.add_dependency("activerecord", ["~> 3.0"])
|
22
|
+
|
23
|
+
s.add_development_dependency "rails", ["~> 3.0"]
|
24
|
+
s.add_development_dependency "sqlite3"
|
25
|
+
s.add_development_dependency "rspec", ["~> 2.0"]
|
26
|
+
s.add_development_dependency "rdoc"
|
27
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::Base.send :include, Has::Magic::Columns
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Always work through the interface MagicAttribute.value
|
2
|
+
class MagicAttribute < ActiveRecord::Base
|
3
|
+
belongs_to :magic_column
|
4
|
+
belongs_to :magic_option
|
5
|
+
|
6
|
+
before_save :update_magic
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
(magic_option) ? magic_option.value : value
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_magic
|
13
|
+
if option = find_magic_option_for(value)
|
14
|
+
unless magic_option and magic_option == option
|
15
|
+
self.value = nil
|
16
|
+
self.magic_option = option
|
17
|
+
end
|
18
|
+
elsif magic_column.allow_other
|
19
|
+
self.magic_option = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def find_magic_option_for(value)
|
26
|
+
magic_column.magic_options.find(:first,
|
27
|
+
:conditions => ["value = ? or synonym = ?", value, value])
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class MagicColumn < ActiveRecord::Base
|
2
|
+
has_many :magic_column_relationships
|
3
|
+
has_many :owners, :through => :magic_column_relationships, :as => :owner
|
4
|
+
has_many :magic_options
|
5
|
+
|
6
|
+
validates_presence_of :name, :datatype
|
7
|
+
validates_format_of :name, :with => /^[a-z][a-z0-9_]+$/
|
8
|
+
|
9
|
+
def type_cast(value)
|
10
|
+
begin
|
11
|
+
case datatype.to_sym
|
12
|
+
when :check_box_boolean
|
13
|
+
(value.to_int == 1) ? true : false
|
14
|
+
when :date
|
15
|
+
Date.parse(value)
|
16
|
+
when :datetime
|
17
|
+
Time.parse(value)
|
18
|
+
when :integer
|
19
|
+
value.to_int
|
20
|
+
else
|
21
|
+
value
|
22
|
+
end
|
23
|
+
rescue
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Display a nicer (possibly user-defined) name for the column or use a fancified default.
|
29
|
+
def pretty_name
|
30
|
+
super || name.humanize
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class MagicOption < ActiveRecord::Base
|
2
|
+
belongs_to :magic_column
|
3
|
+
|
4
|
+
validates_presence_of :value
|
5
|
+
validates_uniqueness_of :value, :scope => :magic_column_id
|
6
|
+
validates_uniqueness_of :synonym, :scope => :magic_column_id, :if => Proc.new{|this| !this.synonym.nil? and !this.synonym.empty?}
|
7
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module HasMagicColumns
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates',__FILE__)
|
8
|
+
|
9
|
+
desc "Add has_magic_columns migration."
|
10
|
+
|
11
|
+
def self.next_migration_number(path)
|
12
|
+
unless @prev_migration_nr
|
13
|
+
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
14
|
+
else
|
15
|
+
@prev_migration_nr += 1
|
16
|
+
end
|
17
|
+
@prev_migration_nr.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_migration
|
21
|
+
migration_template "migration.rb", "db/migrate/add_has_magic_columns_tables.rb"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class AddHasMagicColumnsTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :magic_columns do |t|
|
4
|
+
t.column :name, :string
|
5
|
+
t.column :pretty_name, :string
|
6
|
+
t.column :datatype, :string, :default => "string"
|
7
|
+
t.column :default, :string
|
8
|
+
t.column :is_required, :boolean, :default => false
|
9
|
+
t.column :include_blank, :boolean, :default => false
|
10
|
+
t.column :allow_other, :boolean, :default => true
|
11
|
+
t.column :created_at, :datetime
|
12
|
+
t.column :updated_at, :datetime
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :magic_attributes do |t|
|
16
|
+
t.column :magic_column_id, :integer
|
17
|
+
t.column :magic_option_id, :integer
|
18
|
+
t.column :value, :string
|
19
|
+
t.column :created_at, :datetime
|
20
|
+
t.column :updated_at, :datetime
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :magic_options do |t|
|
24
|
+
t.column :magic_column_id, :integer
|
25
|
+
t.column :value, :string
|
26
|
+
t.column :synonym, :string
|
27
|
+
t.column :created_at, :datetime
|
28
|
+
t.column :updated_at, :datetime
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :magic_column_relationships do |t|
|
32
|
+
t.column :magic_column_id, :integer
|
33
|
+
t.column :owner_id, :integer
|
34
|
+
t.column :owner_type, :string
|
35
|
+
t.column :created_at, :datetime
|
36
|
+
t.column :updated_at, :datetime
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :magic_attribute_relationships do |t|
|
40
|
+
t.column :magic_attribute_id, :integer
|
41
|
+
t.column :owner_id, :integer
|
42
|
+
t.column :owner_type, :string
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.down
|
47
|
+
drop_table "magic_columns"
|
48
|
+
drop_table "magic_attributes"
|
49
|
+
drop_table "magic_options"
|
50
|
+
drop_table "magic_column_relationships"
|
51
|
+
drop_table "magic_attribute_relationships"
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module HasMagicColumns #:nodoc:
|
2
|
+
def self.included(base) # :nodoc:
|
3
|
+
base.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def has_magic_columns(options = {})
|
8
|
+
unless magical?
|
9
|
+
# Associations
|
10
|
+
has_many :magic_attribute_relationships, :as => :owner, :dependent => :destroy
|
11
|
+
has_many :magic_attributes, :through => :magic_attribute_relationships, :dependent => :destroy
|
12
|
+
|
13
|
+
# Eager loading - EXPERIMENTAL!
|
14
|
+
if options[:eager]
|
15
|
+
class_eval do
|
16
|
+
def after_initialize
|
17
|
+
initialize_magic_columns
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Inheritence
|
23
|
+
cattr_accessor :inherited_from
|
24
|
+
|
25
|
+
# if options[:through] is supplied, treat as an inherited relationship
|
26
|
+
if self.inherited_from = options[:through]
|
27
|
+
class_eval do
|
28
|
+
def inherited_magic_columns
|
29
|
+
raise "Cannot inherit MagicColumns from a non-existant association: #{@inherited_from}" unless self.class.method_defined?(inherited_from)# and self.send(inherited_from)
|
30
|
+
self.send(inherited_from).magic_columns
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :magic_columns, :inherited_magic_columns unless method_defined? :magic_columns
|
34
|
+
|
35
|
+
# otherwise the calling model has the relationships
|
36
|
+
else
|
37
|
+
has_many :magic_column_relationships, :as => :owner, :dependent => :destroy
|
38
|
+
has_many :magic_columns, :through => :magic_column_relationships, :dependent => :destroy
|
39
|
+
end
|
40
|
+
|
41
|
+
# Hook into Base
|
42
|
+
class_eval do
|
43
|
+
alias_method :reload_without_magic, :reload
|
44
|
+
alias_method :create_or_update_without_magic, :create_or_update
|
45
|
+
alias_method :read_attribute_without_magic, :read_attribute
|
46
|
+
end
|
47
|
+
end
|
48
|
+
include InstanceMethods
|
49
|
+
|
50
|
+
# Add Magic to Base
|
51
|
+
alias_method :reload, :reload_with_magic
|
52
|
+
alias_method :read_attribute, :read_attribute_with_magic
|
53
|
+
alias_method :create_or_update, :create_or_update_with_magic
|
54
|
+
end
|
55
|
+
|
56
|
+
def magical?
|
57
|
+
self.included_modules.include?(InstanceMethods)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module InstanceMethods #:nodoc:
|
62
|
+
# Reinitialize MagicColumns and MagicAttributes when Model is reloaded
|
63
|
+
def reload_with_magic
|
64
|
+
initialize_magic_columns
|
65
|
+
reload_without_magic
|
66
|
+
end
|
67
|
+
|
68
|
+
def update_attributes(new_attributes)
|
69
|
+
attributes = new_attributes.stringify_keys
|
70
|
+
magic_attrs = magic_columns.map(&:name)
|
71
|
+
|
72
|
+
super(attributes.select{ |k, v| !magic_attrs.include?(k) })
|
73
|
+
attributes.select{ |k, v| magic_attrs.include?(k) }.each do |k, v|
|
74
|
+
col = find_magic_column_by_name(k)
|
75
|
+
attr = find_magic_attribute_by_column(col).first
|
76
|
+
attr.update_attributes(:value => v)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Save MagicAttributes from @attributes
|
83
|
+
def create_or_update_with_magic
|
84
|
+
if result = create_or_update_without_magic
|
85
|
+
magic_columns.each do |column|
|
86
|
+
value = @attributes[column.name]
|
87
|
+
existing = find_magic_attribute_by_column(column)
|
88
|
+
|
89
|
+
unless column.datatype == 'check_box_multiple'
|
90
|
+
(attr = existing.first) ?
|
91
|
+
update_magic_attribute(attr, value) :
|
92
|
+
create_magic_attribute(column, value)
|
93
|
+
else
|
94
|
+
#TODO - make this more efficient
|
95
|
+
value = [value] unless value.is_a? Array
|
96
|
+
existing.map(&:destroy) if existing
|
97
|
+
value.collect {|v| create_magic_attribute(column, v)}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
result
|
102
|
+
end
|
103
|
+
|
104
|
+
# Load (lazily) MagicAttributes or fall back
|
105
|
+
def method_missing(method_id, *args)
|
106
|
+
super(method_id, *args)
|
107
|
+
rescue NoMethodError
|
108
|
+
method_name = method_id.to_s
|
109
|
+
attr_names = magic_columns.map(&:name)
|
110
|
+
initialize_magic_columns and retry if attr_names.include?(method_name) or
|
111
|
+
(md = /[\?|\=]/.match(method_name) and
|
112
|
+
attr_names.include?(md.pre_match))
|
113
|
+
super(method_id, *args)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Load the MagicAttribute(s) associated with attr_name and cast them to proper type.
|
117
|
+
def read_attribute_with_magic(attr_name)
|
118
|
+
return read_attribute_without_magic(attr_name) if column_for_attribute(attr_name) # filter for regular columns
|
119
|
+
attr_name = attr_name.to_s
|
120
|
+
|
121
|
+
if !(value = @attributes[attr_name]).nil?
|
122
|
+
if column = find_magic_column_by_name(attr_name)
|
123
|
+
if value.is_a? Array
|
124
|
+
value.map {|v| column.type_cast(v)}
|
125
|
+
else
|
126
|
+
column.type_cast(value)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
value
|
130
|
+
end
|
131
|
+
else
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Lookup all MagicAttributes and setup @attributes
|
137
|
+
def initialize_magic_columns
|
138
|
+
magic_columns.each do |column|
|
139
|
+
attribute = find_magic_attribute_by_column(column)
|
140
|
+
name = column.name
|
141
|
+
|
142
|
+
# Validation
|
143
|
+
self.class.validates_presence_of(name) if column.is_required?
|
144
|
+
|
145
|
+
# Write attribute
|
146
|
+
unless column.datatype == 'check_box_multiple'
|
147
|
+
(attr = attribute.first) ?
|
148
|
+
write_attribute(name, attr.to_s) :
|
149
|
+
write_attribute(name, column.default)
|
150
|
+
else
|
151
|
+
write_attribute(name, attribute.map(&:to_s))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def find_magic_attribute_by_column(column)
|
157
|
+
magic_attributes.to_a.find_all {|attr| attr.magic_column_id == column.id}
|
158
|
+
end
|
159
|
+
|
160
|
+
def find_magic_column_by_name(attr_name)
|
161
|
+
magic_columns.to_a.find {|column| column.name == attr_name}
|
162
|
+
end
|
163
|
+
|
164
|
+
def create_magic_attribute(magic_column, value)
|
165
|
+
magic_attributes << MagicAttribute.create(:magic_column => magic_column, :value => value)
|
166
|
+
end
|
167
|
+
|
168
|
+
def update_magic_attribute(magic_attribute, value)
|
169
|
+
magic_attribute.update_attributes(:value => value)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'has_magic_columns'
|
2
|
+
|
3
|
+
module HasMagicColumns
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer "has_magic_columns" do
|
6
|
+
|
7
|
+
# I think this is probably a ghetto way of doing this
|
8
|
+
# based on some limited research into other plugins. Neverhtless
|
9
|
+
# it's the only way I know for the moment.
|
10
|
+
ActiveSupport.on_load :active_record do
|
11
|
+
ActiveRecord::Base.send(:include, HasMagicColumns)
|
12
|
+
|
13
|
+
%w{ models }.each do |dir|
|
14
|
+
path = File.join(File.dirname(__FILE__), '../app', dir)
|
15
|
+
$LOAD_PATH << path
|
16
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
17
|
+
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'has_magic_columns/has_magic_columns'
|
2
|
+
require 'has_magic_columns/version'
|
3
|
+
require 'has_magic_columns/railtie' if defined?(Rails)
|
4
|
+
|
5
|
+
# Has Magic Columns
|
6
|
+
#
|
7
|
+
# Copyright (c) 2007 Brandon Keene <bkeene AT gmail DOT com>
|
8
|
+
#
|
9
|
+
# Extends @attributes on a per model basis. Column definitions exist through
|
10
|
+
# the built-in :magic_columns association.
|
11
|
+
#
|
12
|
+
# class Model < ActiveRecord::Base
|
13
|
+
# has_magic_columns
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @model = Model.create(...) #=> @model.id == 1
|
17
|
+
# @model.magic_columns << MagicColumn.create(:name => "first_name")
|
18
|
+
# @model.first_name = "Brandon"
|
19
|
+
# @model.save
|
20
|
+
# @model = Model.find(1)
|
21
|
+
# @model.first_name #=> "Brandon"
|
22
|
+
module HasMagicColumns # :nodoc:
|
23
|
+
end
|
24
|
+
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_magic_columns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Drew Ulmer
|
14
|
+
- Nicholas Zielinski
|
15
|
+
- Brandon Keene
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2011-08-28 00:00:00 -05:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: activesupport
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 7
|
32
|
+
segments:
|
33
|
+
- 3
|
34
|
+
- 0
|
35
|
+
version: "3.0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: activerecord
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 7
|
47
|
+
segments:
|
48
|
+
- 3
|
49
|
+
- 0
|
50
|
+
version: "3.0"
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rails
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 7
|
62
|
+
segments:
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
version: "3.0"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: sqlite3
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rspec
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 2
|
93
|
+
- 0
|
94
|
+
version: "2.0"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rdoc
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
type: :development
|
110
|
+
version_requirements: *id006
|
111
|
+
description: Allow addition of custom 'magic' columns to ActiveRecord models.
|
112
|
+
email:
|
113
|
+
- latortuga@gmail.com
|
114
|
+
- zieski@gmail.com
|
115
|
+
executables: []
|
116
|
+
|
117
|
+
extensions: []
|
118
|
+
|
119
|
+
extra_rdoc_files: []
|
120
|
+
|
121
|
+
files:
|
122
|
+
- .gitignore
|
123
|
+
- CONTRIBUTORS.md
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- has_magic_columns.gemspec
|
129
|
+
- init.rb
|
130
|
+
- lib/app/models/magic_attribute.rb
|
131
|
+
- lib/app/models/magic_attribute_relationship.rb
|
132
|
+
- lib/app/models/magic_column.rb
|
133
|
+
- lib/app/models/magic_column_relationship.rb
|
134
|
+
- lib/app/models/magic_option.rb
|
135
|
+
- lib/generators/has_magic_columns/install/USAGE
|
136
|
+
- lib/generators/has_magic_columns/install/install_generator.rb
|
137
|
+
- lib/generators/has_magic_columns/install/templates/migration.rb
|
138
|
+
- lib/has_magic_columns.rb
|
139
|
+
- lib/has_magic_columns/has_magic_columns.rb
|
140
|
+
- lib/has_magic_columns/railtie.rb
|
141
|
+
- lib/has_magic_columns/version.rb
|
142
|
+
- test/has_magic_columns_test.rb
|
143
|
+
has_rdoc: true
|
144
|
+
homepage: https://github.com/latortuga/has_magic_columns
|
145
|
+
licenses: []
|
146
|
+
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
hash: 3
|
158
|
+
segments:
|
159
|
+
- 0
|
160
|
+
version: "0"
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
requirements: []
|
171
|
+
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 1.6.2
|
174
|
+
signing_key:
|
175
|
+
specification_version: 3
|
176
|
+
summary: Custom fields for Rails 3
|
177
|
+
test_files: []
|
178
|
+
|