classmeta 0.0.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/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: []
|