new_class 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ .DS_Store
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/carrierwave_test/uploads
18
+ test/carrierwave_test/tmp
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
@@ -0,0 +1,5 @@
1
+ = NewClass CHANGELOG
2
+
3
+ == Version 0.1.0 (November 23, 2011)
4
+
5
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :gem_default do
6
+ gem "new_class", :path => "."
7
+ end
8
+
9
+ group :gem_development do
10
+ gem "pry"
11
+ end
12
+
13
+ group :gem_test do
14
+ gem "shoulda"
15
+ gem "mocha"
16
+ gem "activerecord", :require => "active_record"
17
+ gem "sqlite3"
18
+ gem "rmagick"
19
+ gem "carrierwave"
20
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Paul Engel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,152 @@
1
+ h1. NewClass
2
+
3
+ Define variable dependent classes without using class_eval
4
+
5
+ h2. Introduction
6
+
7
+ I think we as Ruby programmers are blessed with the power of meta programming as we can program classes and instances dynamically within the Ruby language. These are just a couple of methods we can use as tools:
8
+
9
+ * eval / module_eval / class_eval / instance_eval
10
+ * class_variables / instance_variables
11
+ * class_variable_get / class_variable_set
12
+ * instance_variable_get / instance_variable_set
13
+ * define_method / undef_method / remove_method
14
+ * send
15
+ * method_missing
16
+ * Class.new
17
+
18
+ Some of the methods (such as @eval@, @class_eval@ and @instance_eval@) are often seen as evil and also, they can be very dangerous.
19
+
20
+ h3. Dynamic CarrierWave uploader classes
21
+
22
+ In my case, I had to define "CarrierWave":https://github.com/jnicklas/carrierwave uploader classes dynamically as they are dependent of certain variables. Choosing not to use @class_eval@ for this task, I have created the @NewClass@ gem to accomplish this.
23
+
24
+ Instead of defining a CarrierWave uploader class with something like this:
25
+
26
+ <pre>
27
+ config = {
28
+ :processor => CarrierWave::RMagick,
29
+ :storage => :file,
30
+ :versions => [[800, 800], {:thumb => [200, 200]}, {:icon => [100, 100]}],
31
+ :extension_white_list => %w(jpg jpeg gif png)
32
+ }
33
+
34
+ avatar_uploader = Class.new(CarrierWave::Uploader::Base).tap do |klass|
35
+ klass.instance_eval <<-RUBY
36
+ include #{config[:processor].to_s}
37
+ storage :#{config[:storage]}
38
+
39
+ def store_dir
40
+ "uploads/\#{model.class.to_s.underscore}/\#{mounted_as}/\#{model.id}"
41
+ end
42
+
43
+ def cache_dir
44
+ "tmp"
45
+ end
46
+
47
+ def extension_white_list
48
+ %w(#{config[:extension_white_list].join " "})
49
+ end
50
+ RUBY
51
+
52
+ config[:versions].each do |v|
53
+ case v.class.name
54
+ when "Array"
55
+ klass.process :resize_to_fit => v
56
+ when "Hash"
57
+ klass.instance_eval <<-RUBY
58
+ version :#{v.keys.first} do
59
+ process :resize_to_fit => [#{v.values.first[0]}, #{v.values.first[1]}]
60
+ end
61
+ RUBY
62
+ end
63
+ end
64
+ end
65
+ </pre>
66
+
67
+ I am able to define the class like this:
68
+
69
+ <pre>
70
+ config = {
71
+ :processor => CarrierWave::RMagick,
72
+ :storage => :file,
73
+ :versions => [[800, 800], {:thumb => [200, 200]}, {:icon => [100, 100]}],
74
+ :extension_white_list => %w(jpg jpeg gif png)
75
+ }
76
+
77
+ class Uploader < CarrierWave::Uploader::Base
78
+ include NewClass
79
+
80
+ def self.defined
81
+ include config[:processor]
82
+ storage config[:storage]
83
+
84
+ config[:versions].each do |v|
85
+ case v.class.name
86
+ when "Array"
87
+ process :resize_to_fit => v
88
+ when "Hash"
89
+ version v.keys.first do
90
+ process :resize_to_fit => v.values.first
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def store_dir
97
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
98
+ end
99
+
100
+ def cache_dir
101
+ "tmp"
102
+ end
103
+
104
+ def extension_white_list
105
+ config[:extension_white_list]
106
+ end
107
+ end
108
+
109
+ avatar_uploader = Uploader.new_class(:config => config)
110
+ </pre>
111
+
112
+ A much cleaner solution right? Try out @NewClass@ right now and spread the word if you like it! ^^
113
+
114
+ h2. Installation
115
+
116
+ h3. Using Bundler
117
+
118
+ Add NewClass in @Gemfile@ as a gem dependency:
119
+
120
+ <pre>
121
+ gem "new_class"
122
+ </pre>
123
+
124
+ Run the following in your console to install with Bundler:
125
+
126
+ <pre>
127
+ bundle install
128
+ </pre>
129
+
130
+ h2. Last remarks
131
+
132
+ Please check out "test/carrierwave_test.rb":https://github.com/archan937/new_class/blob/master/test/carrierwave_test.rb and "test/class_test.rb":https://github.com/archan937/new_class/blob/master/test/class_test.rb for most of the tests available. You can run the unit tests with @rake@ within the terminal.
133
+
134
+ Also, the NewClass repo is provided with @script/console@ which you can run for testing purposes.
135
+
136
+ Note: *NewClass is successfully tested using Ruby 1.8.7 and Ruby 1.9.2*
137
+
138
+ h2. Contact me
139
+
140
+ For support, remarks and requests please mail me at "paul.engel@holder.nl":mailto:paul.engel@holder.nl.
141
+
142
+ h2. License
143
+
144
+ Copyright (c) 2011 Paul Engel, released under the MIT license
145
+
146
+ "http://holder.nl":http://holder.nl – "http://codehero.es":http://codehero.es – "http://gettopup.com":http://gettopup.com – "http://github.com/archan937":http://github.com/archan937 – "http://twitter.com/archan937":http://twitter.com/archan937 – "paul.engel@holder.nl":mailto:paul.engel@holder.nl
147
+
148
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
149
+
150
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
151
+
152
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |test|
8
+ test.pattern = "test/**/*_test.rb"
9
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,50 @@
1
+ require "active_support/core_ext/class/attribute"
2
+ require "active_support/concern"
3
+ require "new_class/version" unless defined?(NewClass::VERSION)
4
+
5
+ module NewClass
6
+
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+ def new_class(variables = {}, name = nil)
13
+ Class.new(self).tap do |klass|
14
+ klass.send :include, Concerns, MethodMissing
15
+ klass.extend MethodMissing
16
+ klass._name = name || self.name
17
+ klass._variables = variables.inject({}){|h, (k, v)| h.merge k.to_sym => v}
18
+ klass.defined if klass.respond_to?(:defined)
19
+ end
20
+ end
21
+
22
+ module Concerns
23
+ extend ActiveSupport::Concern
24
+ included do
25
+ class_attribute :_name, :_variables
26
+ end
27
+ module ClassMethods
28
+ def name
29
+ _name || super
30
+ end
31
+ alias :to_s :name
32
+ end
33
+ module InstanceMethods
34
+ def _variables
35
+ self.class._variables
36
+ end
37
+ end
38
+ end
39
+
40
+ module MethodMissing
41
+ def method_missing(method, *args)
42
+ _variables.include?(key = method.to_sym) ? define_method(key){ _variables[key] }.call : super
43
+ end
44
+ def respond_to?(symbol, include_private = false)
45
+ _variables.include?(symbol.to_sym) || super
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,7 @@
1
+ module NewClass
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ TINY = 0
5
+
6
+ VERSION = [MAJOR, MINOR, TINY].join(".")
7
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/new_class/version", __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Paul Engel"]
6
+ gem.email = ["paul.engel@holder.nl"]
7
+ gem.description = %q{Define variable dependent classes without using class_eval}
8
+ gem.summary = %q{Define variable dependent classes without using class_eval}
9
+ gem.homepage = "https://github.com/archan937/new_class"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "new_class"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = NewClass::VERSION
17
+
18
+ gem.add_dependency "activesupport", ">= 3.0.0"
19
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler"
5
+ Bundler.require :gem_default, :gem_development
6
+
7
+ Pry.start
@@ -0,0 +1,105 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ require "carrierwave/orm/activerecord"
4
+
5
+ CarrierWave.root = File.expand_path("../carrierwave_test", __FILE__)
6
+ ActiveRecord::Base.logger = nil
7
+ ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
8
+ ActiveRecord::Schema.define do
9
+ create_table :users do |table|
10
+ table.column :avatar, :string
11
+ end
12
+ end
13
+
14
+ class CarrierWaveTest < Test::Unit::TestCase
15
+
16
+ context "A variable dependent CarrierWave Uploader class" do
17
+ setup do
18
+ class Uploader < CarrierWave::Uploader::Base
19
+ include NewClass
20
+
21
+ def self.defined
22
+ include config[:processor]
23
+ storage config[:storage]
24
+
25
+ config[:versions].each do |v|
26
+ case v.class.name
27
+ when "Array"
28
+ process :resize_to_fit => v
29
+ when "Hash"
30
+ version v.keys.first do
31
+ process :resize_to_fit => v.values.first
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def store_dir
38
+ "uploads/#{model.class.name.match(/[^:]+$/).to_s.underscore}/#{mounted_as}/#{model.id}"
39
+ end
40
+
41
+ def cache_dir
42
+ "tmp"
43
+ end
44
+
45
+ def extension_white_list
46
+ config[:extension_white_list]
47
+ end
48
+ end
49
+
50
+ @Uploader = Uploader.new_class({
51
+ :config => {
52
+ :processor => CarrierWave::RMagick,
53
+ :storage => :file,
54
+ :versions => [[800, 800], {:thumb => [200, 200]}, {:icon => [100, 100]}],
55
+ :extension_white_list => %w(jpg jpeg gif png)
56
+ }
57
+ })
58
+ end
59
+
60
+ should "have respond to image processing methods" do
61
+ assert @Uploader.respond_to?(:resize_to_fill)
62
+ assert @Uploader.respond_to?(:resize_to_fit)
63
+ end
64
+
65
+ should "have the expected storage" do
66
+ assert_equal CarrierWave::Storage::File, @Uploader.storage
67
+ end
68
+
69
+ context "used within an ActiveRecord::Base class" do
70
+ setup do
71
+ class User < ActiveRecord::Base; end
72
+ User.mount_uploader :avatar, @Uploader
73
+ @user = User.new
74
+ @user.avatar = File.open(File.expand_path("../carrierwave_test/archan937.png", __FILE__))
75
+ end
76
+
77
+ teardown do
78
+ FileUtils.rm_rf File.expand_path("../carrierwave_test/uploads", __FILE__)
79
+ FileUtils.rm_rf File.expand_path("../carrierwave_test/tmp", __FILE__)
80
+ end
81
+
82
+ should "have the expected cache urls" do
83
+ assert @user.avatar.url.match(/\/tmp\/[^\/]+\/archan937.png/)
84
+ assert @user.avatar.thumb.url.match(/\/tmp\/[^\/]+\/thumb_archan937.png/)
85
+ assert @user.avatar.icon.url.match(/\/tmp\/[^\/]+\/icon_archan937.png/)
86
+ end
87
+
88
+ should "have the expected store urls" do
89
+ @user.save
90
+ assert @user.avatar.url.match(/\/uploads\/user\/avatar\/1\/archan937.png/)
91
+ assert @user.avatar.thumb.url.match(/\/uploads\/user\/avatar\/1\/thumb_archan937.png/)
92
+ assert @user.avatar.icon.url.match(/\/uploads\/user\/avatar\/1\/icon_archan937.png/)
93
+ end
94
+
95
+ should "have the expected versions" do
96
+ assert_equal([:thumb, :icon], @Uploader.versions.keys)
97
+ end
98
+
99
+ should "have the expected extension white list" do
100
+ assert_equal %w(jpg jpeg gif png), @user.avatar.extension_white_list
101
+ end
102
+ end
103
+ end
104
+
105
+ end
@@ -0,0 +1,115 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class ClassTest < Test::Unit::TestCase
4
+
5
+ context "A class having included NewClass" do
6
+ setup do
7
+ class A
8
+ end
9
+ class B
10
+ include NewClass
11
+ end
12
+ end
13
+
14
+ should "respond to new_class" do
15
+ assert !A.respond_to?(:new_class)
16
+ assert B.respond_to?(:new_class)
17
+ end
18
+
19
+ should "be able to return a new class" do
20
+ assert_equal Class, B.new_class.class
21
+ end
22
+
23
+ context "the new class" do
24
+ setup do
25
+ @B = B.new_class
26
+ @C = B.new_class({}, "C")
27
+ end
28
+
29
+ should "respond to _name and _variables" do
30
+ assert !A.respond_to?(:_name)
31
+ assert !A.respond_to?(:_variables)
32
+
33
+ assert_equal(B.name, @B._name)
34
+ assert_equal({}, @B._variables)
35
+
36
+ assert_equal("C", @C._name)
37
+ assert_equal({}, @C._variables)
38
+ end
39
+
40
+ should "override method_missing and respond_to?" do
41
+ klass = B.new_class "foo" => "bar"
42
+
43
+ assert klass.respond_to?(:foo)
44
+ assert_equal "bar", klass.foo
45
+
46
+ assert klass.new.respond_to?(:foo)
47
+ assert_equal "bar", klass.new.foo
48
+
49
+ assert !klass.respond_to?(:bar)
50
+ assert_raises NoMethodError do
51
+ klass.bar
52
+ end
53
+
54
+ assert !klass.new.respond_to?(:bar)
55
+ assert_raises NoMethodError do
56
+ klass.new.bar
57
+ end
58
+ end
59
+
60
+ should "be able to create instances" do
61
+ assert @B.new.is_a?(B)
62
+ assert_equal B.name, @B.new.class.name
63
+ assert_equal B.name, @B.new.class.to_s
64
+
65
+ assert @C.new.is_a?(B)
66
+ assert_equal "C", @C.new.class.name
67
+ assert_equal "C", @C.new.class.to_s
68
+ end
69
+ end
70
+
71
+ context "and having a superclass" do
72
+ setup do
73
+ class Greeter
74
+ @@translations = {}
75
+
76
+ def self.translate(language, translation)
77
+ @@translations[language.to_sym] = translation
78
+ end
79
+ def self.translation(language)
80
+ @@translations[language.to_sym]
81
+ end
82
+ def greet(language = :english)
83
+ self.class.translation(language).gsub("{class}", self.class.name.match(/[^:]+$/).to_s)
84
+ end
85
+
86
+ translate :english, "Hi, I am a {class}"
87
+ end
88
+ class C < Greeter
89
+ include NewClass
90
+ def self.defined
91
+ translations.each do |language, translation|
92
+ translate language, translation
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ should "behave as expected" do
99
+ assert_equal "Hi, I am a C", C.new.greet
100
+ end
101
+
102
+ should "have defined called" do
103
+ @C = C.new_class({:translations => {:dutch => "Hallo, ik ben een {class}"}})
104
+ @D = C.new_class({:translations => {:dutch => "Hallo, ik ben een {class}"}}, "D")
105
+
106
+ assert_equal "Hi, I am a C", @C.new.greet
107
+ assert_equal "Hallo, ik ben een C", @C.new.greet(:dutch)
108
+
109
+ assert_equal "Hi, I am a D", @D.new.greet
110
+ assert_equal "Hallo, ik ben een D", @D.new.greet(:dutch)
111
+ end
112
+ end
113
+ end
114
+
115
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class NewClassTest < Test::Unit::TestCase
4
+
5
+ context "NewClass module" do
6
+ should "be defined" do
7
+ assert defined?(NewClass)
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,3 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.require :gem_default, :gem_test
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: new_class
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Paul Engel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-11-23 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 3.0.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: Define variable dependent classes without using class_eval
28
+ email:
29
+ - paul.engel@holder.nl
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .gitignore
38
+ - CHANGELOG.rdoc
39
+ - Gemfile
40
+ - MIT-LICENSE
41
+ - README.textile
42
+ - Rakefile
43
+ - VERSION
44
+ - lib/new_class.rb
45
+ - lib/new_class/version.rb
46
+ - new_class.gemspec
47
+ - script/console
48
+ - test/carrierwave_test.rb
49
+ - test/carrierwave_test/archan937.png
50
+ - test/class_test.rb
51
+ - test/new_class_test.rb
52
+ - test/test_helper.rb
53
+ has_rdoc: true
54
+ homepage: https://github.com/archan937/new_class
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.6.2
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Define variable dependent classes without using class_eval
81
+ test_files:
82
+ - test/carrierwave_test.rb
83
+ - test/carrierwave_test/archan937.png
84
+ - test/class_test.rb
85
+ - test/new_class_test.rb
86
+ - test/test_helper.rb