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 CHANGED
@@ -1,4 +1,20 @@
1
- == 0.1.0 2009-06-29
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
@@ -1,5 +1,6 @@
1
1
  History.txt
2
2
  Manifest.txt
3
+ PostInstall.txt
3
4
  README.rdoc
4
5
  Rakefile
5
6
  lib/appkernel.rb
@@ -12,4 +13,3 @@ spec/appkernel/function_spec.rb
12
13
  spec/appkernel/validation_spec.rb
13
14
  spec/spec.opts
14
15
  spec/spec_helper.rb
15
- tasks/rspec.rake
data/PostInstall.txt ADDED
@@ -0,0 +1,6 @@
1
+
2
+ This gem brought to you by The Frontside Software, Inc.
3
+ http://www.thefrontside.net
4
+
5
+
6
+
data/Rakefile CHANGED
@@ -1,29 +1,28 @@
1
- require 'rubygems' unless ENV['NO_RUBYGEMS']
2
- %w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
3
- $:.unshift(File.dirname(__FILE__) + '/lib') unless $:.member?(File.dirname(__FILE__) + '/lib')
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.new('appkernel', AppKernel::VERSION) do |p|
9
- p.developer('Charles Lowell', 'cowboyd@thefrontside.net')
10
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
11
- p.rubyforge_name = p.name # TODO this is default value
12
- # p.extra_deps = [
13
- # ['activesupport','>= 2.0.2'],
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' # load /tasks/*.rake
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
@@ -1,6 +1,6 @@
1
1
 
2
2
  class AppKernel
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.2"
4
4
  require 'appkernel/function'
5
5
  require 'appkernel/validation'
6
6
  end
@@ -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, *args)
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 :#{@name}"
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 '#{@name}'"
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
- if resolved = opt.resolve(@app, value)
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, *args)
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
- @type = params[:type]
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 @type
171
- if value.is_a?(@type)
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 FunctionDefinitionError, "Don't know how to convert #{value.class}:#{value} -> #{@type}"
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
 
@@ -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
- TakesInt(5).should == 5
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)
@@ -25,7 +25,7 @@ describe AppKernel::Validation do
25
25
  end
26
26
 
27
27
  def validate(vars = {}, &block)
28
- v = AppKernel::Validation::Validator.new(&block)
28
+ v = AppKernel::Validation::Validator.new(stub(:Fun, :mod => Module.new),&block)
29
29
  v.validate vars
30
30
  end
31
31
 
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.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-06-29 00:00:00 -05:00
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: 1.8.0
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
- - README.rdoc
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.4
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