appkernel 0.1.1 → 0.1.2
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/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
|