has_magic_columns 0.1.1
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 +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
|
+
|