defined-by 0.0.5
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/MIT-LICENSE.markdown +20 -0
- data/README.markdown +45 -0
- data/Rakefile +78 -0
- data/lib/defined-by.rb +3 -0
- data/lib/defined-by/define_class.rb +7 -0
- data/lib/defined-by/define_module.rb +11 -0
- data/lib/defined-by/dsl.rb +35 -0
- data/lib/defined-by/missing_constant.rb +51 -0
- data/test/lib/missing.rb +0 -0
- data/test/lib/widget.rb +27 -0
- data/test/test_dsl.rb +50 -0
- data/test/test_helper.rb +18 -0
- data/test/test_missing_constant.rb +62 -0
- metadata +79 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Lance Pollard (lancejpollard@gmail.com)
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# DefinedBy
|
2
|
+
|
3
|
+
> Advanced DSL and Dynamic class generation for Ruby
|
4
|
+
|
5
|
+
## When do I is this?
|
6
|
+
|
7
|
+
- If you want to use a DSL to cut down on your code base
|
8
|
+
- If you need that DSL to create classes
|
9
|
+
- If you have a base class (e.g. `Widget`) and want to subclass it dozens of times but it's overkill to create separate classes for it manually.
|
10
|
+
- Define template classes (default values for subclasses)
|
11
|
+
|
12
|
+
## All at once
|
13
|
+
|
14
|
+
class Widget
|
15
|
+
class << self
|
16
|
+
def define(&block)
|
17
|
+
DefinedBy::DSL(&block).map do |key, value, dsl_block|
|
18
|
+
define_class(key, self).new(:name => key.to_s, :description => value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :name, :description
|
24
|
+
|
25
|
+
def initialize(attributes = {})
|
26
|
+
attributes.each do |key, value|
|
27
|
+
self.send("#{key.to_s}=", value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def Widget(&block)
|
33
|
+
Widget.define(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
widgets = Widget do
|
37
|
+
widget_a "This widget does X"
|
38
|
+
widget_b "This does Y"
|
39
|
+
end
|
40
|
+
|
41
|
+
puts widgets.inspect
|
42
|
+
#=> [
|
43
|
+
#<WidgetA:0x5c3348 @description="This widget does X", @name="widget_a">,
|
44
|
+
#<WidgetB:0x5c2970 @description="This does Y", @name="widget_b">
|
45
|
+
]
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require "rake/rdoctask"
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
|
5
|
+
spec = Gem::Specification.new do |s|
|
6
|
+
s.name = "defined-by"
|
7
|
+
s.authors = ["Lance Pollard"]
|
8
|
+
s.version = "0.0.5"
|
9
|
+
s.summary = "Advanced DSL and Dynamic class generation for Ruby"
|
10
|
+
s.homepage = "http://github.com/viatropos/defined-by"
|
11
|
+
s.email = "lancejpollard@gmail.com"
|
12
|
+
s.description = "Advanced DSL and Dynamic class generation for Ruby"
|
13
|
+
s.has_rdoc = false
|
14
|
+
s.rubyforge_project = "defined-by"
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.files = %w(README.markdown Rakefile MIT-LICENSE.markdown) + Dir["{lib,test}/**/*"] - Dir["test/tmp"]
|
17
|
+
s.require_path = "lib"
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
21
|
+
pkg.gem_spec = spec
|
22
|
+
pkg.package_dir = "pkg"
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'run unit tests'
|
26
|
+
task :test do
|
27
|
+
Dir["test/**/*"].each do |file|
|
28
|
+
next unless File.basename(file) =~ /test_/
|
29
|
+
next unless File.extname(file) == ".rb"
|
30
|
+
system "ruby #{file}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Create .gemspec file (useful for github)"
|
35
|
+
task :gemspec do
|
36
|
+
File.open("pkg/#{spec.name}.gemspec", "w") do |f|
|
37
|
+
f.puts spec.to_ruby
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Build the gem into the current directory"
|
42
|
+
task :gem => :gemspec do
|
43
|
+
`gem build pkg/#{spec.name}.gemspec`
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Publish gem to rubygems"
|
47
|
+
task :publish => [:package] do
|
48
|
+
%x[gem push pkg/#{spec.name}-#{spec.version}.gem]
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Print a list of the files to be put into the gem"
|
52
|
+
task :manifest do
|
53
|
+
File.open("Manifest", "w") do |f|
|
54
|
+
spec.files.each do |file|
|
55
|
+
f.puts file
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Install the gem locally"
|
61
|
+
task :install => [:package] do
|
62
|
+
File.mkdir("pkg") unless File.exists?("pkg")
|
63
|
+
command = "gem install pkg/#{spec.name}-#{spec.version} --no-ri --no-rdoc"
|
64
|
+
command = "sudo #{command}" if ENV["SUDO"] == true
|
65
|
+
sh %{#{command}}
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Generate the rdoc"
|
69
|
+
Rake::RDocTask.new do |rdoc|
|
70
|
+
files = ["README.markdown", "lib/**/*.rb"]
|
71
|
+
rdoc.rdoc_files.add(files)
|
72
|
+
rdoc.main = "README.markdown"
|
73
|
+
rdoc.title = spec.summary
|
74
|
+
end
|
75
|
+
|
76
|
+
task :yank do
|
77
|
+
`gem yank #{spec.name} -v #{spec.version}`
|
78
|
+
end
|
data/lib/defined-by.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
def define_module(name, &block)
|
2
|
+
name = name.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
3
|
+
parts = name.split("::")
|
4
|
+
parts.each_with_index do |part, index|
|
5
|
+
sub_name = parts[0..index].join("::")
|
6
|
+
eval("module #{sub_name}; end")
|
7
|
+
end
|
8
|
+
clazz = eval(name)
|
9
|
+
clazz.class_eval(&block) if block_given?
|
10
|
+
clazz
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DefinedBy
|
2
|
+
class DSL
|
3
|
+
def initialize(&block)
|
4
|
+
@values = []
|
5
|
+
instance_eval(&block) if block_given?
|
6
|
+
end
|
7
|
+
|
8
|
+
def <<(array)
|
9
|
+
@values << array
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&block)
|
13
|
+
@values.each do |array|
|
14
|
+
yield(array[0], array[1], array[2])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def map(&block)
|
19
|
+
@values.map do |array|
|
20
|
+
yield(array[0], array[1], array[2])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args, &block)
|
25
|
+
args = *args
|
26
|
+
self << [method.to_sym, args, block]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def DSL(&block)
|
32
|
+
DSL.new(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module DefinedBy
|
2
|
+
module MissingConstant
|
3
|
+
class Definition
|
4
|
+
class << self
|
5
|
+
attr_accessor :definitions
|
6
|
+
|
7
|
+
def for(const_name)
|
8
|
+
definitions.detect do |definition|
|
9
|
+
definition.match(const_name)
|
10
|
+
end.constant rescue nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def definitions
|
14
|
+
@definitions ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def define!(&block)
|
18
|
+
DefinedBy::DSL(&block).each do |key, value, dsl_block|
|
19
|
+
definitions << Definition.new(value.first, value.last)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :pattern, :replacement, :constant
|
25
|
+
def initialize(pattern, replacement)
|
26
|
+
@pattern, @replacement = pattern, replacement
|
27
|
+
end
|
28
|
+
|
29
|
+
def match(const_name)
|
30
|
+
if const_name.to_s.match(pattern)
|
31
|
+
@constant = const_name.to_s.gsub(pattern, replacement).to_sym
|
32
|
+
else
|
33
|
+
@constant = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# def const_missing(const_name)
|
39
|
+
# if new_constant = Definition.for(const_name)
|
40
|
+
# super(new_constant)
|
41
|
+
# end
|
42
|
+
# super(const_name)
|
43
|
+
# end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.MissingConstants(&block)
|
47
|
+
DefinedBy::MissingConstant::Definition.define!(&block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#Class.instance_eval { include DefinedBy::MissingConstant }
|
data/test/lib/missing.rb
ADDED
File without changes
|
data/test/lib/widget.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
class Widget
|
2
|
+
class << self
|
3
|
+
def define(&block)
|
4
|
+
DefinedBy::DSL(&block).map do |key, value, dsl_block|
|
5
|
+
define_class(key.to_s.camelize, self).class_eval <<-EOF
|
6
|
+
def initialize(attributes = {})
|
7
|
+
attributes[:name] ||= #{key.to_s.inspect}
|
8
|
+
attributes[:description] ||= #{value.to_s.inspect}
|
9
|
+
super(attributes)
|
10
|
+
end
|
11
|
+
EOF
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :name, :description
|
17
|
+
|
18
|
+
def initialize(attributes = {})
|
19
|
+
attributes.each do |key, value|
|
20
|
+
self.send("#{key.to_s}=", value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def Widget(&block)
|
26
|
+
Widget.define(&block)
|
27
|
+
end
|
data/test/test_dsl.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class DSLTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
context "DSL" do
|
6
|
+
|
7
|
+
context "define widgets" do
|
8
|
+
setup do
|
9
|
+
widgets = Widget do
|
10
|
+
widget_a "This widget does X"
|
11
|
+
widget_b "This does Y"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should "have 2 widget classes" do
|
16
|
+
assert WidgetA
|
17
|
+
assert WidgetB
|
18
|
+
end
|
19
|
+
|
20
|
+
should "be able to instantiate the two widget classes" do
|
21
|
+
@a = WidgetA.new
|
22
|
+
@b = WidgetB.new
|
23
|
+
|
24
|
+
assert @a
|
25
|
+
assert @b
|
26
|
+
|
27
|
+
assert_equal "This widget does X", @a.description
|
28
|
+
assert_equal "This does Y", @b.description
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context "Module" do
|
35
|
+
setup do
|
36
|
+
define_module("Lance::Pollard") do
|
37
|
+
class << self
|
38
|
+
def acts_as_lance_pollard
|
39
|
+
"yep"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
should "have defined the modules" do
|
46
|
+
assert Lance::Pollard
|
47
|
+
assert_equal "yep", Lance::Pollard.acts_as_lance_pollard
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "ruby-debug"
|
3
|
+
gem 'test-unit'
|
4
|
+
require "test/unit"
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/test_case'
|
7
|
+
require 'shoulda'
|
8
|
+
require 'shoulda/active_record'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
this = File.expand_path(File.dirname(__FILE__))
|
12
|
+
|
13
|
+
require File.expand_path(File.join(this, '/../lib/defined-by'))
|
14
|
+
|
15
|
+
Dir["#{this}/lib/*"].each { |c| require c if File.extname(c) == ".rb" }
|
16
|
+
|
17
|
+
ActiveSupport::TestCase.class_eval do
|
18
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class MissingConstantTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
context "MissingConstant" do
|
6
|
+
|
7
|
+
should "not have the constant by default" do
|
8
|
+
assert ::UserProfile
|
9
|
+
end
|
10
|
+
|
11
|
+
context "define the base" do
|
12
|
+
setup do
|
13
|
+
class ::Object
|
14
|
+
class << self
|
15
|
+
def profile(&block)
|
16
|
+
define_class("::#{self.name}Profile", "::Profile") do
|
17
|
+
attr_accessor :profile
|
18
|
+
|
19
|
+
def profile
|
20
|
+
@profile ||= {}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ::Profile
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
should "have defined the profile" do
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "define the meat" do
|
37
|
+
setup do
|
38
|
+
class ::User
|
39
|
+
profile
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
should "have defined the constant now" do
|
44
|
+
assert ::UserProfile
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "DSL" do
|
50
|
+
setup do
|
51
|
+
DefinedBy::MissingConstants() do
|
52
|
+
match %r{\w+(Profile)$}, "\\1"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "define the dsl" do
|
57
|
+
assert_equal 1, DefinedBy::MissingConstant::Definition.definitions.length
|
58
|
+
assert_equal :Profile, DefinedBy::MissingConstant::Definition.for(:UserProfile)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: defined-by
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Lance Pollard
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-13 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Advanced DSL and Dynamic class generation for Ruby
|
23
|
+
email: lancejpollard@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- README.markdown
|
32
|
+
- Rakefile
|
33
|
+
- MIT-LICENSE.markdown
|
34
|
+
- lib/defined-by/define_class.rb
|
35
|
+
- lib/defined-by/define_module.rb
|
36
|
+
- lib/defined-by/dsl.rb
|
37
|
+
- lib/defined-by/missing_constant.rb
|
38
|
+
- lib/defined-by.rb
|
39
|
+
- test/lib/missing.rb
|
40
|
+
- test/lib/widget.rb
|
41
|
+
- test/test_dsl.rb
|
42
|
+
- test/test_helper.rb
|
43
|
+
- test/test_missing_constant.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/viatropos/defined-by
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 3
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project: defined-by
|
74
|
+
rubygems_version: 1.3.7
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Advanced DSL and Dynamic class generation for Ruby
|
78
|
+
test_files: []
|
79
|
+
|