classmeta 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +98 -0
- data/Rakefile +11 -0
- data/lib/classmeta/class.rb +34 -0
- data/lib/classmeta/class_registry.rb +20 -0
- data/lib/classmeta/dependencies.rb +11 -0
- data/lib/classmeta/echo.rb +7 -0
- data/lib/classmeta/options.rb +21 -0
- data/lib/classmeta/railtie.rb +1 -0
- data/lib/classmeta/version.rb +3 -0
- data/lib/classmeta.rb +6 -0
- metadata +56 -0
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
Classmeta for Rails 3.2+
|
2
|
+
=====
|
3
|
+
|
4
|
+
Creates new classes on the fly, to allow you to refactor common changes.
|
5
|
+
|
6
|
+
Lets say you wanted whitelisted attributes to be different depending which class was including it as an association.
|
7
|
+
|
8
|
+
In car, you only want the names of passengers:
|
9
|
+
|
10
|
+
has_many :passengers, class_name: Person.meta(:std, attrs: [:name]).name
|
11
|
+
|
12
|
+
In train, you want their seat preference:
|
13
|
+
|
14
|
+
has_many :riders, class_name: Person.meta(:std, attrs: [:name, :seat_preference]).name
|
15
|
+
|
16
|
+
etc. You have a ton of these for whatever reason.
|
17
|
+
|
18
|
+
So, include the gem in Gemfile and bundle install:
|
19
|
+
|
20
|
+
gem 'classmeta'
|
21
|
+
|
22
|
+
Add a transformer somewhere in the load path like app/models/:
|
23
|
+
|
24
|
+
class StandardTransformer
|
25
|
+
def transform(klazz, options)
|
26
|
+
klazz.class_eval "self._accessible_attributes[:default] = #{options[:attrs].inspect}" if options[:attrs]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
And add this to environment.rb:
|
31
|
+
|
32
|
+
Classmeta::Options.configure({
|
33
|
+
:transformers => {
|
34
|
+
:std => StandardTransformer
|
35
|
+
}
|
36
|
+
})
|
37
|
+
|
38
|
+
Now magically car passengers have their accessible attributes set to `:name` and train riders to `[:name, :seat_preference]`!
|
39
|
+
|
40
|
+
### How?
|
41
|
+
|
42
|
+
Hooking into Rails' load_missing_constant, duping the original class, and doing stuff to it in the transform.
|
43
|
+
|
44
|
+
Works with class reloading or caching.
|
45
|
+
|
46
|
+
### Why?
|
47
|
+
|
48
|
+
Useful if you have a number of classes, like models, in Rails 3.2+ that just differ by a little bit and module includes just aren't solving the problem of doubling, tripling, etc. the number of files you are having to create just to represent new classes.
|
49
|
+
|
50
|
+
### Just Rails?
|
51
|
+
|
52
|
+
Feel free to do a pull request and add off-Rails support.
|
53
|
+
|
54
|
+
### Quick Test!
|
55
|
+
|
56
|
+
Add to your Gemfile:
|
57
|
+
|
58
|
+
gem 'classmeta'
|
59
|
+
|
60
|
+
Install it:
|
61
|
+
|
62
|
+
bundle install
|
63
|
+
|
64
|
+
Open console:
|
65
|
+
|
66
|
+
rails c
|
67
|
+
|
68
|
+
Try it:
|
69
|
+
|
70
|
+
YourModel.meta(:echo)
|
71
|
+
YourModel.meta(:echo).name
|
72
|
+
YourModel.meta(:echo, {:say => 'Hello World!'})
|
73
|
+
YourModel.named_meta('Fabular', :echo, {:say => 'Hello World!'})
|
74
|
+
Fabular.all
|
75
|
+
|
76
|
+
Note: if you define `Classmeta::Options.configure({...})`, it automatically gets rid of the echo transformer which is just for demonstration purposes. If you want to use it at runtime, you can use:
|
77
|
+
|
78
|
+
Classmeta::Options.configure({
|
79
|
+
:transformers => {
|
80
|
+
:echo => Classmeta::Echo
|
81
|
+
}
|
82
|
+
})
|
83
|
+
|
84
|
+
### Troubleshooting
|
85
|
+
|
86
|
+
Since Classmeta delegates to Rails's dependency loading, any errors you get like this:
|
87
|
+
|
88
|
+
.../classmeta/lib/classmeta/dependencies.rb:7:in `load_missing_constant': uninitialized constant (something) (NameError)
|
89
|
+
|
90
|
+
Just means Rails couldn't find your class.
|
91
|
+
|
92
|
+
For other errors, too, for the most part, pretend you are getting that from .../activesupport/lib/activesupport/dependencies.rb, then Google it and scratch your head until you figure out you mistyped something.
|
93
|
+
|
94
|
+
### License
|
95
|
+
|
96
|
+
Copyright (c) 2012 Gary S. Weaver, released under the [MIT license][lic].
|
97
|
+
|
98
|
+
[lic]: http://github.com/garysweaver/classmeta/blob/master/LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
#http://nicksda.apotomo.de/2010/10/testing-your-rails-3-engine-sitting-in-a-gem/
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << 'test'
|
6
|
+
t.test_files = FileList['test/**/*_test.rb']
|
7
|
+
t.verbose = true
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Run tests"
|
11
|
+
task :default => :test
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Classmeta
|
4
|
+
module ClassMethods
|
5
|
+
# e.g. has_many :employees, class_name: MyModel.meta(:ref, attrs: [:name, :status], special_column: 'my_column').name
|
6
|
+
def meta(*args)
|
7
|
+
options = args.extract_options! # separates options hash and leaves one or more transform names
|
8
|
+
self.named_meta("#{self}#{SecureRandom.uuid.gsub('-','')}", args, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
# e.g. has_many :employees, class_name: MyModel.named_meta(MyNewModel, :ref, {attrs: [:name, :status], special_column: 'my_column'}).name
|
12
|
+
# e.g. has_many :employees, class_name: MyModel.named_meta(MyNewModel, [:transform1, :transform2], {attrs: [:name, :status], special_column: 'my_column'}).name
|
13
|
+
def named_meta(new_classname, transform_name_or_names, options = {})
|
14
|
+
transforms = Array.wrap(transform_name_or_names).flatten
|
15
|
+
#puts "#{self.name}.dup will be named: #{name}"
|
16
|
+
new_class = self.dup
|
17
|
+
new_class.instance_eval("def name; #{new_classname.inspect}; end")
|
18
|
+
transforms.each do |transform|
|
19
|
+
transformer = Classmeta::Options.get_transformer(transform)
|
20
|
+
if transformer
|
21
|
+
#puts "#{transformer.name}.transform(#{new_class.name}, #{options.inspect})"
|
22
|
+
transformer.transform(new_class, options)
|
23
|
+
else
|
24
|
+
Classmeta::Options.output
|
25
|
+
raise "Classmeta::Options needs a transformer mapping for #{transform.inspect}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
#puts "Registering #{name} so class lookup works, if riding Rails"
|
29
|
+
Classmeta::ClassRegistry.register(new_classname, [self, transforms])
|
30
|
+
# TODO: figure out how to cache class in Rails: ActiveSupport::Dependencies::Registry.store(new_class)
|
31
|
+
new_class
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Classmeta
|
2
|
+
class ClassRegistry
|
3
|
+
@@registry = {}
|
4
|
+
|
5
|
+
def self.register(name, arr)
|
6
|
+
@@registry[name.to_sym] = arr
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.get(name)
|
10
|
+
arr = @@registry[name.to_sym]
|
11
|
+
if arr
|
12
|
+
# re-meta, so the class that was derived from can be reloaded
|
13
|
+
arr[0].meta(arr[1])
|
14
|
+
else
|
15
|
+
#puts "Didn't find anything for name #{name} in Classmeta's class registry: #{@@registry.inspect}"
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module Dependencies
|
3
|
+
alias_method(:load_missing_constant_classmeta_renamed, :load_missing_constant)
|
4
|
+
undef_method(:load_missing_constant)
|
5
|
+
def load_missing_constant(from_mod, const_name)
|
6
|
+
#puts "Classmeta's load_missing_constant from_mod=#{from_mod} const_name=#{const_name}"
|
7
|
+
klazz = Classmeta::ClassRegistry.get(const_name) || load_missing_constant_classmeta_renamed(from_mod, const_name)
|
8
|
+
klazz.class_eval('extend Classmeta::ClassMethods')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Classmeta
|
2
|
+
class Options
|
3
|
+
@@options = {
|
4
|
+
:transformers => {
|
5
|
+
:echo => Classmeta::Echo
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.configure(hash)
|
10
|
+
@@options = hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_transformer(name)
|
14
|
+
(@@options[:transformers])[name]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.output
|
18
|
+
puts "Classmeta::Options=#{@@options.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'classmeta'
|
data/lib/classmeta.rb
ADDED
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: classmeta
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gary S. Weaver
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-29 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Magic class creator that lets you create and transform classes dynamically.
|
15
|
+
email:
|
16
|
+
- garysweaver@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/classmeta/class.rb
|
22
|
+
- lib/classmeta/class_registry.rb
|
23
|
+
- lib/classmeta/dependencies.rb
|
24
|
+
- lib/classmeta/echo.rb
|
25
|
+
- lib/classmeta/options.rb
|
26
|
+
- lib/classmeta/railtie.rb
|
27
|
+
- lib/classmeta/version.rb
|
28
|
+
- lib/classmeta.rb
|
29
|
+
- Rakefile
|
30
|
+
- README.md
|
31
|
+
homepage: https://github.com/garysweaver/activerecord-refs
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.8.24
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Magic class creation.
|
56
|
+
test_files: []
|