classmeta 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,7 @@
1
+ module Classmeta
2
+ class Echo
3
+ def self.transform(klazz, options)
4
+ puts "Classmeta::Echo.transform(klazz, options) called with klazz: #{klazz.name} and options: #{options.inspect}"
5
+ end
6
+ end
7
+ 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'
@@ -0,0 +1,3 @@
1
+ module Classmeta
2
+ VERSION = '0.0.1'
3
+ end
data/lib/classmeta.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'classmeta/version'
2
+ require 'classmeta/class'
3
+ require 'classmeta/class_registry'
4
+ require 'classmeta/dependencies'
5
+ require 'classmeta/echo'
6
+ require 'classmeta/options'
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: []