appkernel 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +17 -1
- data/Manifest.txt +1 -1
- data/PostInstall.txt +6 -0
- data/Rakefile +18 -19
- data/lib/appkernel.rb +1 -1
- data/lib/appkernel/function.rb +47 -19
- data/lib/appkernel/validation.rb +19 -1
- data/spec/appkernel/function_spec.rb +77 -2
- data/spec/appkernel/validation_spec.rb +1 -1
- metadata +7 -17
- data/tasks/rspec.rake +0 -21
data/History.txt
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
-
== 0.1.
|
1
|
+
== 0.1.3 YYY-MM-DD
|
2
|
+
|
3
|
+
* x major enhancements
|
4
|
+
* functions defined in the same scope can call themselves
|
5
|
+
* validations can call function in the same module
|
6
|
+
* ability to provide defaults for function arguments.
|
7
|
+
* allow multiple classes in the :type option attribute
|
8
|
+
|
9
|
+
== 0.1.2 2009-07-01
|
10
|
+
|
11
|
+
* 1 major enhancment:
|
12
|
+
* can't remember for the life of me what it was. Are you really reading this?
|
13
|
+
|
14
|
+
* 1 major enhancement:
|
15
|
+
* functioning prototype which allows you to create portable, self-validating functions
|
16
|
+
|
17
|
+
== 0.1.1 2009-06-30
|
2
18
|
|
3
19
|
* 1 major enhancement:
|
4
20
|
* functioning prototype which allows you to create portable, self-validating functions
|
data/Manifest.txt
CHANGED
data/PostInstall.txt
ADDED
data/Rakefile
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
|
3
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__) + '/lib'
|
4
7
|
require 'appkernel'
|
5
8
|
|
9
|
+
Hoe.plugin :newgem
|
10
|
+
# Hoe.plugin :website
|
11
|
+
# Hoe.plugin :cucumber_features
|
12
|
+
|
6
13
|
# Generate all the Rake tasks
|
7
14
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
8
|
-
$hoe = Hoe.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
# ]
|
15
|
-
p.extra_dev_deps = [
|
16
|
-
['newgem', ">= #{::Newgem::VERSION}"]
|
17
|
-
]
|
18
|
-
|
19
|
-
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
20
|
-
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
21
|
-
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
22
|
-
p.rsync_args = '-av --delete --ignore-errors'
|
15
|
+
$hoe = Hoe.spec 'appkernel' do
|
16
|
+
self.developer 'Charles Lowell', 'cowboyd@thefrontside.net'
|
17
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
18
|
+
self.rubyforge_name = self.name # TODO this is default value
|
19
|
+
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
20
|
+
|
23
21
|
end
|
24
22
|
|
25
|
-
require 'newgem/tasks'
|
23
|
+
require 'newgem/tasks'
|
26
24
|
Dir['tasks/**/*.rake'].each { |t| load t }
|
27
25
|
|
28
26
|
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# remove_task :default
|
29
28
|
# task :default => [:spec, :features]
|
data/lib/appkernel.rb
CHANGED
data/lib/appkernel/function.rb
CHANGED
@@ -10,7 +10,7 @@ class AppKernel
|
|
10
10
|
def self.included(mod)
|
11
11
|
class << mod
|
12
12
|
def function(symbol, &definition)
|
13
|
-
fun = ::AppKernel::FunctionDefinition.new(symbol, definition)
|
13
|
+
fun = ::AppKernel::FunctionDefinition.new(symbol, self, definition)
|
14
14
|
self.const_set(symbol, fun)
|
15
15
|
self.send(:define_method, symbol) do |*args|
|
16
16
|
FunctionApplication.apply_or_die(fun, *args)
|
@@ -39,7 +39,7 @@ class AppKernel
|
|
39
39
|
@function = fun
|
40
40
|
@errors = {}
|
41
41
|
@args = Arguments.new(self, *args)
|
42
|
-
@return_value = self.class.do_apply(self
|
42
|
+
@return_value = self.class.do_apply(self)
|
43
43
|
end
|
44
44
|
|
45
45
|
def successful?
|
@@ -67,22 +67,36 @@ class AppKernel
|
|
67
67
|
if opt = fun.options[k.to_sym]
|
68
68
|
set opt, v
|
69
69
|
else
|
70
|
-
raise FunctionCallError, "unknown option :#{
|
70
|
+
raise FunctionCallError, "#{fun.name}: unknown option :#{k}"
|
71
71
|
end
|
72
72
|
end
|
73
73
|
elsif opt = @optorder.shift
|
74
74
|
set opt, arg
|
75
75
|
end
|
76
76
|
end
|
77
|
+
for opt in fun.options.values
|
78
|
+
if @canonical[opt.name].nil? && !opt.default.nil?
|
79
|
+
@canonical[opt.name] = opt.default
|
80
|
+
@required.delete opt
|
81
|
+
end
|
82
|
+
end
|
77
83
|
for opt in @required
|
78
|
-
app.errors[opt.name] = "missing required option '#{
|
84
|
+
app.errors[opt.name] = "missing required option '#{opt.name}'"
|
79
85
|
end
|
86
|
+
for name in fun.options.keys
|
87
|
+
@canonical[name] = nil if @canonical[name].nil?
|
88
|
+
end
|
80
89
|
end
|
81
90
|
|
82
|
-
def set(opt, value)
|
83
|
-
|
91
|
+
def set(opt, value)
|
92
|
+
resolved = opt.resolve(@app, value)
|
93
|
+
if !resolved.nil?
|
84
94
|
@canonical[opt.name] = resolved
|
85
95
|
@required.delete opt
|
96
|
+
elsif !value.nil? && opt.required?
|
97
|
+
@required.delete opt
|
98
|
+
@required.delete opt
|
99
|
+
@app.errors[opt.name] = "no such value '#{value}' for required option '#{opt.name}'"
|
86
100
|
end
|
87
101
|
end
|
88
102
|
end
|
@@ -98,11 +112,12 @@ class AppKernel
|
|
98
112
|
end
|
99
113
|
end
|
100
114
|
|
101
|
-
def do_apply(app
|
115
|
+
def do_apply(app)
|
102
116
|
fun = app.function
|
103
117
|
app.errors.merge! fun.validation.validate(app.options) if app.successful?
|
104
118
|
if app.successful?
|
105
119
|
scope = Object.new
|
120
|
+
scope.extend fun.mod
|
106
121
|
for k,v in app.options do
|
107
122
|
scope.instance_variable_set("@#{k}", v)
|
108
123
|
end
|
@@ -114,13 +129,14 @@ class AppKernel
|
|
114
129
|
|
115
130
|
class FunctionDefinition
|
116
131
|
|
117
|
-
attr_reader :impl, :options, :validation
|
132
|
+
attr_reader :name, :mod, :impl, :options, :validation
|
118
133
|
|
119
|
-
def initialize(name, definition)
|
134
|
+
def initialize(name, mod, definition)
|
120
135
|
@name = name
|
136
|
+
@mod = mod
|
121
137
|
@options = {}
|
122
138
|
@impl = lambda {}
|
123
|
-
@validation = ::AppKernel::Validation::Validator.new
|
139
|
+
@validation = ::AppKernel::Validation::Validator.new(self)
|
124
140
|
self.instance_eval &definition
|
125
141
|
end
|
126
142
|
|
@@ -138,7 +154,7 @@ class AppKernel
|
|
138
154
|
end
|
139
155
|
|
140
156
|
def validate(&checks)
|
141
|
-
@validation = AppKernel::Validation::Validator.new(&checks)
|
157
|
+
@validation = AppKernel::Validation::Validator.new(self, &checks)
|
142
158
|
end
|
143
159
|
|
144
160
|
def to_s
|
@@ -147,13 +163,25 @@ class AppKernel
|
|
147
163
|
|
148
164
|
class Option
|
149
165
|
ID = lambda {|o| o}
|
150
|
-
attr_reader :name, :index
|
166
|
+
attr_reader :name, :index, :default
|
151
167
|
def initialize(name, params)
|
152
168
|
@name = name.to_sym
|
153
169
|
@index = params[:index]
|
154
170
|
@required = params[:required] == true
|
155
171
|
@finder = params[:find]
|
156
|
-
@
|
172
|
+
@types = params[:type] ? [params[:type]].flatten : nil
|
173
|
+
@default = params[:default]
|
174
|
+
validate!
|
175
|
+
end
|
176
|
+
|
177
|
+
def validate!
|
178
|
+
if @default
|
179
|
+
if @types
|
180
|
+
raise OptionError, "#{@default} is not a kind of #{@types.join('|')}" unless @types.detect {|t| @default.kind_of?(t)}
|
181
|
+
elsif @required
|
182
|
+
Kernel.warn "option '#{@name}' unecessarily marked as required. It has a default value"
|
183
|
+
end
|
184
|
+
end
|
157
185
|
end
|
158
186
|
|
159
187
|
def required?
|
@@ -167,13 +195,13 @@ class AppKernel
|
|
167
195
|
def resolve(app, value)
|
168
196
|
if value.nil?
|
169
197
|
nil
|
170
|
-
elsif @
|
171
|
-
if value.is_a?(
|
198
|
+
elsif @types
|
199
|
+
if @types.detect {|t| value.is_a?(t)}
|
172
200
|
value
|
173
201
|
elsif @finder
|
174
202
|
lookup(app, value)
|
175
203
|
else
|
176
|
-
raise
|
204
|
+
raise OptionError, "Don't know how to convert #{value.class}:#{value} -> #{@type}"
|
177
205
|
end
|
178
206
|
elsif @finder
|
179
207
|
lookup(app, value)
|
@@ -186,8 +214,7 @@ class AppKernel
|
|
186
214
|
result = @finder.call(value)
|
187
215
|
app.errors[@name] = "couldn't find '#{@name}': #{value}" if result.nil?
|
188
216
|
result
|
189
|
-
end
|
190
|
-
|
217
|
+
end
|
191
218
|
end
|
192
219
|
|
193
220
|
class Validator
|
@@ -195,6 +222,7 @@ class AppKernel
|
|
195
222
|
end
|
196
223
|
|
197
224
|
class FunctionCallError < StandardError; end
|
225
|
+
class OptionError < StandardError; end
|
198
226
|
|
199
227
|
class ValidationError < StandardError
|
200
228
|
def initialize(application)
|
@@ -202,7 +230,7 @@ class AppKernel
|
|
202
230
|
end
|
203
231
|
|
204
232
|
def message
|
205
|
-
@app.errors.values.first
|
233
|
+
"#{@app.function.name}: #{@app.errors.values.first}"
|
206
234
|
end
|
207
235
|
end
|
208
236
|
|
data/lib/appkernel/validation.rb
CHANGED
@@ -4,13 +4,15 @@ module AppKernel::Validation
|
|
4
4
|
|
5
5
|
attr_reader :errors
|
6
6
|
|
7
|
-
def initialize(&block)
|
7
|
+
def initialize(fun, &block)
|
8
|
+
@fun = fun
|
8
9
|
@body = block
|
9
10
|
end
|
10
11
|
|
11
12
|
def validate(vars = {})
|
12
13
|
errors = {}
|
13
14
|
scope = Object.new
|
15
|
+
scope.extend @fun.mod
|
14
16
|
for k,v in vars do
|
15
17
|
val = case v
|
16
18
|
when nil
|
@@ -19,6 +21,10 @@ module AppKernel::Validation
|
|
19
21
|
FixnumValue.new(v)
|
20
22
|
when Symbol
|
21
23
|
v
|
24
|
+
when FalseClass
|
25
|
+
FalseValue.new
|
26
|
+
when TrueClass
|
27
|
+
TrueValue.new
|
22
28
|
else
|
23
29
|
v.dup
|
24
30
|
end
|
@@ -56,6 +62,18 @@ module AppKernel::Validation
|
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
65
|
+
class FalseValue < DelegateClass(FalseClass)
|
66
|
+
def initialize
|
67
|
+
super(false)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class TrueValue < DelegateClass(TrueClass)
|
72
|
+
def initialize
|
73
|
+
super(true)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
59
77
|
class FixnumValue < DelegateClass(Fixnum)
|
60
78
|
def initialize(val)
|
61
79
|
super(val)
|
@@ -70,6 +70,22 @@ describe AppKernel::Function do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
it "allows functions in the same module to be accessible without namespacing" do
|
74
|
+
function :First do
|
75
|
+
execute do
|
76
|
+
Second()
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
function :Second do
|
81
|
+
execute do
|
82
|
+
"Hello"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
First().should == "Hello"
|
87
|
+
end
|
88
|
+
|
73
89
|
it "can be called by using the apply method instead of invoking it directly" do
|
74
90
|
function :FiveAlive do
|
75
91
|
option :desc, :index => 1
|
@@ -139,13 +155,62 @@ describe AppKernel::Function do
|
|
139
155
|
TakesInt("5").should == 5
|
140
156
|
end
|
141
157
|
|
158
|
+
describe "Default Values" do
|
159
|
+
it "allows for any option to have a default value" do
|
160
|
+
function :HasDefault do
|
161
|
+
option :value, :default => 5
|
162
|
+
|
163
|
+
execute{@value}
|
164
|
+
end
|
165
|
+
|
166
|
+
HasDefault().should == 5
|
167
|
+
end
|
168
|
+
|
169
|
+
it "requires that the default value be the same as the option type if that is specified" do
|
170
|
+
lambda {
|
171
|
+
function :InvalidDefault do
|
172
|
+
option :value, :type => Integer, :default => "NOT_INT"
|
173
|
+
end
|
174
|
+
}.should raise_error
|
175
|
+
end
|
176
|
+
|
177
|
+
it "warns if an option has a default and is also required" do
|
178
|
+
Kernel.should_receive(:warn)
|
179
|
+
function :QuestionableDefault do
|
180
|
+
option :value, :required => true, :default => 5
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it "sets a default option even if that option is explicitly passed in as nil" do
|
185
|
+
function :ExplicitNil do
|
186
|
+
option :value, :default => 'fun'
|
187
|
+
execute{@value}
|
188
|
+
end
|
189
|
+
|
190
|
+
ExplicitNil(:value => nil).should == 'fun'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
142
194
|
it "doesn't do an argument conversion if the argument is already of the correct type" do
|
143
195
|
function :TakesInt do
|
144
196
|
option :num, :index => 1, :type => Integer, :find => proc {|s| raise StandardError, "Hey, don't call me!"}
|
145
197
|
execute {@num}
|
146
198
|
end
|
147
|
-
|
148
|
-
|
199
|
+
lambda {
|
200
|
+
TakesInt(5).should == 5
|
201
|
+
}.should_not raise_error
|
202
|
+
end
|
203
|
+
|
204
|
+
it "an option can have multiple valid types" do
|
205
|
+
function :MultipleValidOptionTypes do
|
206
|
+
option :bool, :index => 1, :type => [TrueClass, FalseClass], :find => proc {|s| s == "true"}
|
207
|
+
execute {@bool}
|
208
|
+
end
|
209
|
+
|
210
|
+
MultipleValidOptionTypes("true").should be(true)
|
211
|
+
MultipleValidOptionTypes("false").should be(false)
|
212
|
+
MultipleValidOptionTypes(true).should be(true)
|
213
|
+
MultipleValidOptionTypes(false).should be(false)
|
149
214
|
end
|
150
215
|
|
151
216
|
it "raises an exception if it can't tell how to find a complex type" do
|
@@ -185,6 +250,16 @@ describe AppKernel::Function do
|
|
185
250
|
Noop(:foo => 'bar')
|
186
251
|
}.should raise_error(AppKernel::FunctionCallError)
|
187
252
|
end
|
253
|
+
|
254
|
+
it "allows false as a default value" do
|
255
|
+
function :FalseDefault do
|
256
|
+
option :bool, :default => false
|
257
|
+
execute {@bool}
|
258
|
+
end
|
259
|
+
|
260
|
+
FalseDefault().should be(false)
|
261
|
+
end
|
262
|
+
|
188
263
|
end
|
189
264
|
|
190
265
|
def function(sym, &body)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appkernel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Lowell
|
@@ -9,19 +9,9 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-13 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: newgem
|
17
|
-
type: :development
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.4.1
|
24
|
-
version:
|
25
15
|
- !ruby/object:Gem::Dependency
|
26
16
|
name: hoe
|
27
17
|
type: :development
|
@@ -30,7 +20,7 @@ dependencies:
|
|
30
20
|
requirements:
|
31
21
|
- - ">="
|
32
22
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
23
|
+
version: 2.3.3
|
34
24
|
version:
|
35
25
|
description: |-
|
36
26
|
AppKernel is a microframework for capturing your application in terms of minute, self-validating functions.
|
@@ -44,10 +34,11 @@ extensions: []
|
|
44
34
|
extra_rdoc_files:
|
45
35
|
- History.txt
|
46
36
|
- Manifest.txt
|
47
|
-
-
|
37
|
+
- PostInstall.txt
|
48
38
|
files:
|
49
39
|
- History.txt
|
50
40
|
- Manifest.txt
|
41
|
+
- PostInstall.txt
|
51
42
|
- README.rdoc
|
52
43
|
- Rakefile
|
53
44
|
- lib/appkernel.rb
|
@@ -60,12 +51,11 @@ files:
|
|
60
51
|
- spec/appkernel/validation_spec.rb
|
61
52
|
- spec/spec.opts
|
62
53
|
- spec/spec_helper.rb
|
63
|
-
- tasks/rspec.rake
|
64
54
|
has_rdoc: true
|
65
55
|
homepage: http://github.com/cowboyd/appkernel
|
66
56
|
licenses: []
|
67
57
|
|
68
|
-
post_install_message:
|
58
|
+
post_install_message: PostInstall.txt
|
69
59
|
rdoc_options:
|
70
60
|
- --main
|
71
61
|
- README.rdoc
|
@@ -86,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
76
|
requirements: []
|
87
77
|
|
88
78
|
rubyforge_project: appkernel
|
89
|
-
rubygems_version: 1.3.
|
79
|
+
rubygems_version: 1.3.5
|
90
80
|
signing_key:
|
91
81
|
specification_version: 3
|
92
82
|
summary: AppKernel is a microframework for capturing your application in terms of minute, self-validating functions
|
data/tasks/rspec.rake
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'spec'
|
3
|
-
rescue LoadError
|
4
|
-
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
-
require 'spec'
|
6
|
-
end
|
7
|
-
begin
|
8
|
-
require 'spec/rake/spectask'
|
9
|
-
rescue LoadError
|
10
|
-
puts <<-EOS
|
11
|
-
To use rspec for testing you must install rspec gem:
|
12
|
-
gem install rspec
|
13
|
-
EOS
|
14
|
-
exit(0)
|
15
|
-
end
|
16
|
-
|
17
|
-
desc "Run the specs under spec/models"
|
18
|
-
Spec::Rake::SpecTask.new do |t|
|
19
|
-
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
-
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
-
end
|