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 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: []