dao 0.0.0 → 2.0.0
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/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
|