dao 0.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +35 -0
- data/Rakefile +6 -3
- data/TODO +37 -0
- data/dao.gemspec +30 -0
- data/db/dao.yml +8 -0
- data/lib/dao.rb +84 -5
- data/lib/dao/active_record.rb +76 -0
- data/lib/dao/api.rb +9 -0
- data/lib/dao/api/context.rb +38 -0
- data/lib/dao/api/dsl.rb +50 -0
- data/lib/dao/api/endpoints.rb +190 -0
- data/lib/dao/api/initializers.rb +71 -0
- data/lib/dao/api/modes.rb +85 -0
- data/lib/dao/blankslate.rb +5 -0
- data/lib/dao/data.rb +58 -0
- data/lib/dao/db.rb +183 -0
- data/lib/dao/endpoint.rb +16 -0
- data/lib/dao/engine.rb +7 -0
- data/lib/dao/errors.rb +238 -0
- data/lib/dao/exceptions.rb +2 -0
- data/lib/dao/form.rb +236 -0
- data/lib/dao/mode.rb +41 -0
- data/lib/dao/mongo_mapper.rb +70 -0
- data/lib/dao/params.rb +109 -0
- data/lib/dao/path.rb +149 -0
- data/lib/dao/rails.rb +15 -0
- data/lib/dao/rails/app/api.rb +55 -0
- data/lib/dao/rails/app/controllers/api_controller.rb +99 -0
- data/lib/dao/rails/lib/generators/dao/USAGE +9 -0
- data/lib/dao/rails/lib/generators/dao/api_generator.rb +3 -0
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +27 -0
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +55 -0
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +99 -0
- data/lib/dao/result.rb +87 -0
- data/lib/dao/slug.rb +11 -0
- data/lib/dao/status.rb +223 -0
- data/lib/dao/stdext.rb +10 -0
- data/lib/dao/support.rb +62 -0
- data/lib/dao/validations.rb +115 -0
- data/sample/rails_app/Gemfile +33 -0
- data/sample/rails_app/Gemfile.lock +88 -0
- data/sample/rails_app/README +1 -0
- data/sample/rails_app/Rakefile +7 -0
- data/sample/rails_app/app/api.rb +55 -0
- data/sample/rails_app/app/controllers/api_controller.rb +99 -0
- data/sample/rails_app/app/controllers/application_controller.rb +3 -0
- data/sample/rails_app/app/helpers/application_helper.rb +2 -0
- data/sample/rails_app/app/views/layouts/application.html.erb +14 -0
- data/sample/rails_app/config.ru +4 -0
- data/sample/rails_app/config/application.rb +51 -0
- data/sample/rails_app/config/boot.rb +13 -0
- data/sample/rails_app/config/database.yml +22 -0
- data/sample/rails_app/config/environment.rb +5 -0
- data/sample/rails_app/config/environments/development.rb +26 -0
- data/sample/rails_app/config/environments/production.rb +49 -0
- data/sample/rails_app/config/environments/test.rb +35 -0
- data/sample/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/sample/rails_app/config/initializers/inflections.rb +10 -0
- data/sample/rails_app/config/initializers/mime_types.rb +5 -0
- data/sample/rails_app/config/initializers/secret_token.rb +7 -0
- data/sample/rails_app/config/initializers/session_store.rb +8 -0
- data/sample/rails_app/config/locales/en.yml +5 -0
- data/sample/rails_app/config/routes.rb +62 -0
- data/sample/rails_app/db/development.sqlite3 +0 -0
- data/sample/rails_app/db/seeds.rb +7 -0
- data/sample/rails_app/doc/README_FOR_APP +2 -0
- data/sample/rails_app/log/development.log +27 -0
- data/sample/rails_app/log/production.log +0 -0
- data/sample/rails_app/log/server.log +0 -0
- data/sample/rails_app/log/test.log +0 -0
- data/sample/rails_app/public/404.html +26 -0
- data/sample/rails_app/public/422.html +26 -0
- data/sample/rails_app/public/500.html +26 -0
- data/sample/rails_app/public/favicon.ico +0 -0
- data/sample/rails_app/public/images/rails.png +0 -0
- data/sample/rails_app/public/index.html +239 -0
- data/sample/rails_app/public/javascripts/application.js +2 -0
- data/sample/rails_app/public/javascripts/controls.js +965 -0
- data/sample/rails_app/public/javascripts/dragdrop.js +974 -0
- data/sample/rails_app/public/javascripts/effects.js +1123 -0
- data/sample/rails_app/public/javascripts/prototype.js +6001 -0
- data/sample/rails_app/public/javascripts/rails.js +175 -0
- data/sample/rails_app/public/robots.txt +5 -0
- data/sample/rails_app/script/rails +6 -0
- data/sample/rails_app/test/performance/browsing_test.rb +9 -0
- data/sample/rails_app/test/test_helper.rb +13 -0
- data/test/dao_test.rb +271 -0
- data/test/helper.rb +15 -0
- data/test/testing.rb +74 -0
- metadata +137 -9
data/README
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
NAME
|
2
|
+
dao
|
3
|
+
|
4
|
+
SYNOPSIS
|
5
|
+
a library for structuring rails applications using the 'data access object' pattern
|
6
|
+
|
7
|
+
DESCRITPION
|
8
|
+
stay tuned...
|
9
|
+
|
10
|
+
ISSUES ADRESSED
|
11
|
+
. testability
|
12
|
+
. documentability
|
13
|
+
. being able to reason about code
|
14
|
+
. reasoning about caching
|
15
|
+
. avoiding duplicating logic in html/api
|
16
|
+
. avoid N+1 in view
|
17
|
+
. avoid design issues with REST (delete(1,2,3))
|
18
|
+
. nested attributes trivally
|
19
|
+
. lack of domain (current_user)
|
20
|
+
. easily optimize queries later
|
21
|
+
. magic
|
22
|
+
|
23
|
+
READING
|
24
|
+
http://www.paperplanes.de/2010/5/7/activerecord_callbacks_ruined_my_life.html
|
25
|
+
http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/dao.html
|
26
|
+
http://www.codefutures.com/data-access-object/
|
27
|
+
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
|
28
|
+
|
29
|
+
INSTALL
|
30
|
+
gem 'dao', :path => File.expand_path('..') ### Gemfile
|
31
|
+
rails generate dao api
|
32
|
+
vim -o app/api.rb app/controllers/api_controller.rb
|
33
|
+
curl --silent http://0.0.0.0:3000/api
|
34
|
+
curl --silent http://0.0.0.0:3000/api/ping
|
35
|
+
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
This.rubyforge_project = 'codeforpeople'
|
2
2
|
This.author = "Ara T. Howard"
|
3
3
|
This.email = "ara.t.howard@gmail.com"
|
4
|
-
This.homepage = "http://github.com/ahoward/#{ This.lib }
|
4
|
+
This.homepage = "http://github.com/ahoward/#{ This.lib }"
|
5
5
|
|
6
6
|
|
7
7
|
task :default do
|
@@ -26,7 +26,7 @@ def run_tests!(which = nil)
|
|
26
26
|
|
27
27
|
div = ('=' * 119)
|
28
28
|
line = ('-' * 119)
|
29
|
-
helper = "-r ./test/helper.rb" if test(?e, "./test/helper.rb")
|
29
|
+
helper = nil #"-r ./test/helper.rb" if test(?e, "./test/helper.rb")
|
30
30
|
|
31
31
|
test_rbs.each_with_index do |test_rb, index|
|
32
32
|
testno = index + 1
|
@@ -126,6 +126,9 @@ task :gemspec do
|
|
126
126
|
spec.test_files = #{ test_files.inspect }
|
127
127
|
|
128
128
|
# spec.add_dependency 'lib', '>= version'
|
129
|
+
spec.add_dependency 'map'
|
130
|
+
spec.add_dependency 'tagz'
|
131
|
+
spec.add_dependency 'yajl-ruby'
|
129
132
|
|
130
133
|
spec.extensions.push(*#{ extensions.inspect })
|
131
134
|
|
@@ -139,7 +142,7 @@ task :gemspec do
|
|
139
142
|
end
|
140
143
|
|
141
144
|
Fu.mkdir_p(This.pkgdir)
|
142
|
-
This.gemspec = File.join(This.pkgdir, "gemspec.rb")
|
145
|
+
This.gemspec = File.join(This.dir, "#{ This.lib }.gemspec") #File.join(This.pkgdir, "gemspec.rb")
|
143
146
|
open("#{ This.gemspec }", "w"){|fd| fd.puts(template)}
|
144
147
|
end
|
145
148
|
|
data/TODO
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
todo:
|
2
|
+
|
3
|
+
route -> path??
|
4
|
+
|
5
|
+
- nested AR conversions
|
6
|
+
to_dao(:a, :b => [:foo, :bar])
|
7
|
+
|
8
|
+
- AR as_dao contains the model type/class.name
|
9
|
+
|
10
|
+
|
11
|
+
done:
|
12
|
+
- engine-ify the rails stuff? or what
|
13
|
+
- generator api
|
14
|
+
- controller
|
15
|
+
- helper
|
16
|
+
- json/pretty fix baked in? (yajl might simply this...)
|
17
|
+
- description/doc logic
|
18
|
+
- api.index
|
19
|
+
- to_alpo -> to_dao
|
20
|
+
- tests!
|
21
|
+
- #call auto parses data iff appropriate
|
22
|
+
@result = api.read.call('/posts/new(', params) ### check keys for '/posts/new'
|
23
|
+
- re-visit how parameters are parsed, perhaps we just use rack?
|
24
|
+
- next 'data' => {}
|
25
|
+
- check the db layer
|
26
|
+
- make sure one can call route based method (/foo/:bar) methods with params...
|
27
|
+
- Path==Route ??
|
28
|
+
- add result.rb, params.rb
|
29
|
+
- name -> path
|
30
|
+
- tagz for html methods?
|
31
|
+
- data aquires the name/path of the method?
|
32
|
+
- endpoints are objects that respond to call. namespaces respond to call.
|
33
|
+
api responds to call
|
34
|
+
- nested namespaces
|
35
|
+
- routing for path_info + mode
|
36
|
+
route(path_info, :mode => mode, :params => params)
|
37
|
+
- call() on namespaces AND endpoints... why?
|
data/dao.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
## dao.gemspec
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
spec.name = "dao"
|
6
|
+
spec.version = "2.0.0"
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
8
|
+
spec.summary = "dao"
|
9
|
+
spec.description = "description: dao kicks the ass"
|
10
|
+
|
11
|
+
spec.files = ["dao.gemspec", "db", "db/dao.yml", "lib", "lib/dao", "lib/dao/active_record.rb", "lib/dao/api", "lib/dao/api/context.rb", "lib/dao/api/dsl.rb", "lib/dao/api/endpoints.rb", "lib/dao/api/initializers.rb", "lib/dao/api/modes.rb", "lib/dao/api.rb", "lib/dao/blankslate.rb", "lib/dao/data.rb", "lib/dao/db.rb", "lib/dao/endpoint.rb", "lib/dao/engine.rb", "lib/dao/errors.rb", "lib/dao/exceptions.rb", "lib/dao/form.rb", "lib/dao/mode.rb", "lib/dao/mongo_mapper.rb", "lib/dao/params.rb", "lib/dao/path.rb", "lib/dao/rails", "lib/dao/rails/app", "lib/dao/rails/app/api.rb", "lib/dao/rails/app/controllers", "lib/dao/rails/app/controllers/api_controller.rb", "lib/dao/rails/engine", "lib/dao/rails/lib", "lib/dao/rails/lib/generators", "lib/dao/rails/lib/generators/dao", "lib/dao/rails/lib/generators/dao/api_generator.rb", "lib/dao/rails/lib/generators/dao/dao_generator.rb", "lib/dao/rails/lib/generators/dao/templates", "lib/dao/rails/lib/generators/dao/templates/api.rb", "lib/dao/rails/lib/generators/dao/templates/api_controller.rb", "lib/dao/rails/lib/generators/dao/USAGE", "lib/dao/rails.rb", "lib/dao/result.rb", "lib/dao/slug.rb", "lib/dao/status.rb", "lib/dao/stdext.rb", "lib/dao/support.rb", "lib/dao/validations.rb", "lib/dao.rb", "Rakefile", "README", "sample", "sample/rails_app", "sample/rails_app/app", "sample/rails_app/app/api.rb", "sample/rails_app/app/controllers", "sample/rails_app/app/controllers/api_controller.rb", "sample/rails_app/app/controllers/application_controller.rb", "sample/rails_app/app/helpers", "sample/rails_app/app/helpers/application_helper.rb", "sample/rails_app/app/mailers", "sample/rails_app/app/models", "sample/rails_app/app/views", "sample/rails_app/app/views/layouts", "sample/rails_app/app/views/layouts/application.html.erb", "sample/rails_app/config", "sample/rails_app/config/application.rb", "sample/rails_app/config/boot.rb", "sample/rails_app/config/database.yml", "sample/rails_app/config/environment.rb", "sample/rails_app/config/environments", "sample/rails_app/config/environments/development.rb", "sample/rails_app/config/environments/production.rb", "sample/rails_app/config/environments/test.rb", "sample/rails_app/config/initializers", "sample/rails_app/config/initializers/backtrace_silencers.rb", "sample/rails_app/config/initializers/inflections.rb", "sample/rails_app/config/initializers/mime_types.rb", "sample/rails_app/config/initializers/secret_token.rb", "sample/rails_app/config/initializers/session_store.rb", "sample/rails_app/config/locales", "sample/rails_app/config/locales/en.yml", "sample/rails_app/config/routes.rb", "sample/rails_app/config.ru", "sample/rails_app/db", "sample/rails_app/db/development.sqlite3", "sample/rails_app/db/seeds.rb", "sample/rails_app/doc", "sample/rails_app/doc/README_FOR_APP", "sample/rails_app/Gemfile", "sample/rails_app/Gemfile.lock", "sample/rails_app/lib", "sample/rails_app/lib/tasks", "sample/rails_app/log", "sample/rails_app/log/development.log", "sample/rails_app/log/production.log", "sample/rails_app/log/server.log", "sample/rails_app/log/test.log", "sample/rails_app/public", "sample/rails_app/public/404.html", "sample/rails_app/public/422.html", "sample/rails_app/public/500.html", "sample/rails_app/public/favicon.ico", "sample/rails_app/public/images", "sample/rails_app/public/images/rails.png", "sample/rails_app/public/index.html", "sample/rails_app/public/javascripts", "sample/rails_app/public/javascripts/application.js", "sample/rails_app/public/javascripts/controls.js", "sample/rails_app/public/javascripts/dragdrop.js", "sample/rails_app/public/javascripts/effects.js", "sample/rails_app/public/javascripts/prototype.js", "sample/rails_app/public/javascripts/rails.js", "sample/rails_app/public/robots.txt", "sample/rails_app/public/stylesheets", "sample/rails_app/Rakefile", "sample/rails_app/README", "sample/rails_app/script", "sample/rails_app/script/rails", "sample/rails_app/test", "sample/rails_app/test/fixtures", "sample/rails_app/test/functional", "sample/rails_app/test/integration", "sample/rails_app/test/performance", "sample/rails_app/test/performance/browsing_test.rb", "sample/rails_app/test/test_helper.rb", "sample/rails_app/test/unit", "sample/rails_app/tmp/cache", "sample/rails_app/tmp/pids", "sample/rails_app/tmp/sessions", "sample/rails_app/tmp/sockets", "sample/rails_app/vendor", "sample/rails_app/vendor/plugins", "test", "test/dao_test.rb", "test/helper.rb", "test/testing.rb", "test/units", "TODO"]
|
12
|
+
spec.executables = []
|
13
|
+
|
14
|
+
spec.require_path = "lib"
|
15
|
+
|
16
|
+
spec.has_rdoc = true
|
17
|
+
spec.test_files = nil
|
18
|
+
|
19
|
+
# spec.add_dependency 'lib', '>= version'
|
20
|
+
spec.add_dependency 'map'
|
21
|
+
spec.add_dependency 'tagz'
|
22
|
+
spec.add_dependency 'yajl-ruby'
|
23
|
+
|
24
|
+
spec.extensions.push(*[])
|
25
|
+
|
26
|
+
spec.rubyforge_project = "codeforpeople"
|
27
|
+
spec.author = "Ara T. Howard"
|
28
|
+
spec.email = "ara.t.howard@gmail.com"
|
29
|
+
spec.homepage = "http://github.com/ahoward/dao"
|
30
|
+
end
|
data/db/dao.yml
ADDED
data/lib/dao.rb
CHANGED
@@ -1,9 +1,88 @@
|
|
1
|
-
|
1
|
+
# built-ins
|
2
|
+
#
|
3
|
+
require 'enumerator'
|
4
|
+
#require 'fileutils'
|
5
|
+
#require 'pathname'
|
6
|
+
#require 'yaml'
|
7
|
+
#require 'yaml/store'
|
2
8
|
|
3
|
-
|
4
|
-
|
9
|
+
# gems
|
10
|
+
#
|
11
|
+
begin
|
12
|
+
require 'rubygems'
|
13
|
+
rescue LoadError
|
14
|
+
nil
|
5
15
|
end
|
6
16
|
|
7
|
-
|
17
|
+
if defined?(gem)
|
18
|
+
gem('map', '~> 2.2.2')
|
19
|
+
gem('tagz', '~> 8.0')
|
20
|
+
gem('yajl-ruby', '~> 0.7.9')
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'map'
|
24
|
+
require 'tagz'
|
25
|
+
require 'yajl'
|
26
|
+
|
27
|
+
# dao libs
|
28
|
+
#
|
29
|
+
module Dao
|
30
|
+
Version = '2.0.0' unless defined?(Version)
|
31
|
+
|
32
|
+
def version
|
33
|
+
Dao::Version
|
34
|
+
end
|
35
|
+
|
36
|
+
def libdir(*args, &block)
|
37
|
+
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
38
|
+
args.empty? ? @libdir : File.join(@libdir, *args)
|
39
|
+
ensure
|
40
|
+
if block
|
41
|
+
begin
|
42
|
+
$LOAD_PATH.unshift(@libdir)
|
43
|
+
block.call()
|
44
|
+
ensure
|
45
|
+
$LOAD_PATH.shift()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load(*libs)
|
51
|
+
libs = libs.join(' ').scan(/[^\s+]+/)
|
52
|
+
Dao.libdir{ libs.each{|lib| Kernel.load(lib) } }
|
53
|
+
end
|
54
|
+
|
55
|
+
extend(Dao)
|
56
|
+
end
|
8
57
|
|
9
|
-
|
58
|
+
Dao.load %w[
|
59
|
+
blankslate.rb
|
60
|
+
exceptions.rb
|
61
|
+
support.rb
|
62
|
+
slug.rb
|
63
|
+
stdext.rb
|
64
|
+
|
65
|
+
result.rb
|
66
|
+
params.rb
|
67
|
+
status.rb
|
68
|
+
data.rb
|
69
|
+
form.rb
|
70
|
+
errors.rb
|
71
|
+
validations.rb
|
72
|
+
|
73
|
+
mode.rb
|
74
|
+
path.rb
|
75
|
+
endpoint.rb
|
76
|
+
api.rb
|
77
|
+
|
78
|
+
|
79
|
+
rails.rb
|
80
|
+
active_record.rb
|
81
|
+
mongo_mapper.rb
|
82
|
+
]
|
83
|
+
|
84
|
+
Dao.autoload(:Db, Dao.libdir('db.rb'))
|
85
|
+
|
86
|
+
unless defined?(D)
|
87
|
+
D = Dao
|
88
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
begin
|
2
|
+
ActiveRecord
|
3
|
+
ActiveRecord::Base
|
4
|
+
rescue NameError
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
if defined?(ActiveRecord)
|
9
|
+
|
10
|
+
module ActiveRecord
|
11
|
+
module ToDao
|
12
|
+
module ClassMethods
|
13
|
+
def to_dao(*args)
|
14
|
+
|
15
|
+
@to_dao ||= (
|
16
|
+
column_names # + reflect_on_all_associations.map(&:name)
|
17
|
+
).map{|name| name.to_s}
|
18
|
+
|
19
|
+
unless args.empty?
|
20
|
+
@to_dao.clear
|
21
|
+
args.flatten.compact.each do |arg|
|
22
|
+
@to_dao.push(arg.to_s)
|
23
|
+
end
|
24
|
+
@to_dao.uniq!
|
25
|
+
@to_dao.map!{|name| name.to_s}
|
26
|
+
end
|
27
|
+
|
28
|
+
@to_dao
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_dao=(*args)
|
32
|
+
to_dao(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
def to_dao(*args)
|
38
|
+
hash = Dao.hash
|
39
|
+
model = self.class
|
40
|
+
|
41
|
+
attrs = args.empty? ? model.to_dao : args
|
42
|
+
|
43
|
+
attrs.each do |attr|
|
44
|
+
value = send(attr)
|
45
|
+
|
46
|
+
if value.respond_to?(:to_dao)
|
47
|
+
hash[attr] = value.to_dao
|
48
|
+
next
|
49
|
+
end
|
50
|
+
|
51
|
+
if value.is_a?(Array)
|
52
|
+
hash[attr] = value.map{|val| val.respond_to?(:to_dao) ? val.to_dao : val}
|
53
|
+
next
|
54
|
+
end
|
55
|
+
|
56
|
+
hash[attr] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
if hash.has_key?(:_id) and not hash.has_key?(:id)
|
60
|
+
hash[:id] = hash[:_id]
|
61
|
+
end
|
62
|
+
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
alias_method 'to_h', 'to_dao'
|
66
|
+
#alias_method 'to_map', 'to_dao' ### HACK
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if defined?(ActiveRecord::Base)
|
71
|
+
ActiveRecord::Base.send(:extend, ToDao::ClassMethods)
|
72
|
+
ActiveRecord::Base.send(:include, ToDao::InstanceMethods)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/lib/dao/api.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Dao
|
2
|
+
class Context
|
3
|
+
Attrs = %w( api endpoint params result method args )
|
4
|
+
Attrs.each{|attr| attr_accessor(attr)}
|
5
|
+
|
6
|
+
def initialize(*args, &block)
|
7
|
+
options = Dao.options_for!(args)
|
8
|
+
|
9
|
+
api = options[:api]
|
10
|
+
endpoint = options[:endpoint]
|
11
|
+
params = options[:params]
|
12
|
+
|
13
|
+
params = Params.for(:api => api, :endpoint => endpoint, :params => params)
|
14
|
+
result = Result.new(:api => api, :endpoint => endpoint, :params => params)
|
15
|
+
params.result = result
|
16
|
+
|
17
|
+
method = endpoint.method.bind(api)
|
18
|
+
args = [params, result].slice(0, method.arity)
|
19
|
+
|
20
|
+
self.api = api
|
21
|
+
self.endpoint = endpoint
|
22
|
+
self.params = params
|
23
|
+
self.result = result
|
24
|
+
self.method = method
|
25
|
+
self.args = args
|
26
|
+
end
|
27
|
+
|
28
|
+
def call()
|
29
|
+
method.call(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(options = {})
|
33
|
+
options.each do |key, val|
|
34
|
+
send("#{ key }=", val)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/dao/api/dsl.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Dao
|
2
|
+
class Api
|
3
|
+
class DSL < BlankSlate
|
4
|
+
attr_accessor :api
|
5
|
+
|
6
|
+
def initialize(api)
|
7
|
+
@api = api
|
8
|
+
#@evaluate = Object.instance_method(:instance_eval).bind(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def evaluate(&block)
|
12
|
+
#@evaluate.call(&block)
|
13
|
+
@api.module_eval(&block)
|
14
|
+
ensure
|
15
|
+
#no_docs_left_on_stack!
|
16
|
+
end
|
17
|
+
|
18
|
+
def no_docs_left_on_stack!
|
19
|
+
raise "no endpoint for #{ docs.inspect }" unless docs.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
%w( endpoint doc docs description desc ).each do |method|
|
23
|
+
module_eval <<-__, __FILE__, __LINE__ - 1
|
24
|
+
|
25
|
+
def #{ method }(*args, &block)
|
26
|
+
api.#{ method }(*args, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
__
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << Api
|
34
|
+
def evaluate(&block)
|
35
|
+
@dsl ||= DSL.new(api=self)
|
36
|
+
@dsl.evaluate(&block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def Dao.api(&block)
|
42
|
+
if block
|
43
|
+
api = Class.new(Api)
|
44
|
+
api.evaluate(&block)
|
45
|
+
api
|
46
|
+
else
|
47
|
+
Api
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module Dao
|
2
|
+
class Api
|
3
|
+
class << Api
|
4
|
+
def endpoints
|
5
|
+
@endpoints ||= Map.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def endpoint(path, &block)
|
9
|
+
api = self
|
10
|
+
path = Path.new(path)
|
11
|
+
|
12
|
+
method =
|
13
|
+
module_eval{
|
14
|
+
define_method(path + '/endpoint', &block)
|
15
|
+
instance_method(path + '/endpoint')
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
endpoint = Endpoint.new(
|
20
|
+
'api' => api,
|
21
|
+
'path' => path,
|
22
|
+
'method' => method,
|
23
|
+
'doc' => docs.pop
|
24
|
+
)
|
25
|
+
|
26
|
+
endpoints[path] = endpoint
|
27
|
+
end
|
28
|
+
|
29
|
+
def description(string)
|
30
|
+
doc(:description => Dao.unindent(string))
|
31
|
+
end
|
32
|
+
alias_method('desc', 'description')
|
33
|
+
|
34
|
+
def doc(*args)
|
35
|
+
docs.push(Map[:description, nil]) if docs.empty?
|
36
|
+
doc = docs.last
|
37
|
+
options = Dao.options_for!(args)
|
38
|
+
if options.empty?
|
39
|
+
options[:description] = args.join(' ')
|
40
|
+
end
|
41
|
+
doc.update(options)
|
42
|
+
doc
|
43
|
+
end
|
44
|
+
|
45
|
+
def docs
|
46
|
+
@docs ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def index
|
50
|
+
index = Map.new
|
51
|
+
endpoints.each do |path, endpoint|
|
52
|
+
index[path] = endpoint.doc || {'description' => path}
|
53
|
+
end
|
54
|
+
index
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(path = '/index', params = {})
|
59
|
+
api = self
|
60
|
+
path = Path.new(path)
|
61
|
+
endpoint = endpoints[path]
|
62
|
+
raise(NameError, path) unless endpoint
|
63
|
+
|
64
|
+
params = parse_params(params, path)
|
65
|
+
|
66
|
+
context = Context.new(
|
67
|
+
:api => api,
|
68
|
+
:endpoint => endpoint,
|
69
|
+
:params => params
|
70
|
+
)
|
71
|
+
|
72
|
+
callstack(context) do
|
73
|
+
catching(:result){ context.call() }
|
74
|
+
end
|
75
|
+
|
76
|
+
context.result
|
77
|
+
end
|
78
|
+
|
79
|
+
def index
|
80
|
+
self.class.index
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_params(params, path)
|
84
|
+
return params if params.is_a?(Params)
|
85
|
+
re = %r/^#{ Regexp.escape(path) }/
|
86
|
+
params.each do |key, val|
|
87
|
+
return Params.parse(path, params) if key =~ re
|
88
|
+
end
|
89
|
+
return params
|
90
|
+
end
|
91
|
+
|
92
|
+
def endpoints
|
93
|
+
self.class.endpoints
|
94
|
+
end
|
95
|
+
|
96
|
+
def context
|
97
|
+
callstack.last
|
98
|
+
end
|
99
|
+
|
100
|
+
def result
|
101
|
+
context.result
|
102
|
+
end
|
103
|
+
|
104
|
+
def status(*args, &block)
|
105
|
+
result.status(*args, &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
def data
|
109
|
+
result.data
|
110
|
+
end
|
111
|
+
|
112
|
+
def errors
|
113
|
+
result.errors
|
114
|
+
end
|
115
|
+
|
116
|
+
def params
|
117
|
+
result.params
|
118
|
+
end
|
119
|
+
|
120
|
+
def validations
|
121
|
+
result.validations
|
122
|
+
end
|
123
|
+
|
124
|
+
def validates(*args, &block)
|
125
|
+
result.validates(*args, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def validate
|
129
|
+
result.validate
|
130
|
+
end
|
131
|
+
|
132
|
+
def valid?
|
133
|
+
result.valid?
|
134
|
+
end
|
135
|
+
|
136
|
+
def validate!
|
137
|
+
result.validate!
|
138
|
+
end
|
139
|
+
|
140
|
+
def valid!
|
141
|
+
result.valid!
|
142
|
+
end
|
143
|
+
|
144
|
+
def callstack(context = nil, &block)
|
145
|
+
@callstack ||= []
|
146
|
+
|
147
|
+
if block and context
|
148
|
+
begin
|
149
|
+
@callstack.push(context)
|
150
|
+
return block.call()
|
151
|
+
ensure
|
152
|
+
@callstack.pop
|
153
|
+
end
|
154
|
+
else
|
155
|
+
@callstack
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def catching(label = :result, &block)
|
160
|
+
@catching ||= []
|
161
|
+
|
162
|
+
if block
|
163
|
+
begin
|
164
|
+
@catching.push(label)
|
165
|
+
catch(label, &block)
|
166
|
+
ensure
|
167
|
+
@catching.pop
|
168
|
+
end
|
169
|
+
else
|
170
|
+
@catching.last
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def catching_results(&block)
|
175
|
+
catching(:result, &block)
|
176
|
+
end
|
177
|
+
|
178
|
+
def catching?
|
179
|
+
catching
|
180
|
+
end
|
181
|
+
|
182
|
+
def catching_results?
|
183
|
+
catching == :result
|
184
|
+
end
|
185
|
+
|
186
|
+
def respond_to?(*args)
|
187
|
+
super(*args) || super(Path.absolute_path_for(*args))
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|