dao 0.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/README +35 -0
  2. data/Rakefile +6 -3
  3. data/TODO +37 -0
  4. data/dao.gemspec +30 -0
  5. data/db/dao.yml +8 -0
  6. data/lib/dao.rb +84 -5
  7. data/lib/dao/active_record.rb +76 -0
  8. data/lib/dao/api.rb +9 -0
  9. data/lib/dao/api/context.rb +38 -0
  10. data/lib/dao/api/dsl.rb +50 -0
  11. data/lib/dao/api/endpoints.rb +190 -0
  12. data/lib/dao/api/initializers.rb +71 -0
  13. data/lib/dao/api/modes.rb +85 -0
  14. data/lib/dao/blankslate.rb +5 -0
  15. data/lib/dao/data.rb +58 -0
  16. data/lib/dao/db.rb +183 -0
  17. data/lib/dao/endpoint.rb +16 -0
  18. data/lib/dao/engine.rb +7 -0
  19. data/lib/dao/errors.rb +238 -0
  20. data/lib/dao/exceptions.rb +2 -0
  21. data/lib/dao/form.rb +236 -0
  22. data/lib/dao/mode.rb +41 -0
  23. data/lib/dao/mongo_mapper.rb +70 -0
  24. data/lib/dao/params.rb +109 -0
  25. data/lib/dao/path.rb +149 -0
  26. data/lib/dao/rails.rb +15 -0
  27. data/lib/dao/rails/app/api.rb +55 -0
  28. data/lib/dao/rails/app/controllers/api_controller.rb +99 -0
  29. data/lib/dao/rails/lib/generators/dao/USAGE +9 -0
  30. data/lib/dao/rails/lib/generators/dao/api_generator.rb +3 -0
  31. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +27 -0
  32. data/lib/dao/rails/lib/generators/dao/templates/api.rb +55 -0
  33. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +99 -0
  34. data/lib/dao/result.rb +87 -0
  35. data/lib/dao/slug.rb +11 -0
  36. data/lib/dao/status.rb +223 -0
  37. data/lib/dao/stdext.rb +10 -0
  38. data/lib/dao/support.rb +62 -0
  39. data/lib/dao/validations.rb +115 -0
  40. data/sample/rails_app/Gemfile +33 -0
  41. data/sample/rails_app/Gemfile.lock +88 -0
  42. data/sample/rails_app/README +1 -0
  43. data/sample/rails_app/Rakefile +7 -0
  44. data/sample/rails_app/app/api.rb +55 -0
  45. data/sample/rails_app/app/controllers/api_controller.rb +99 -0
  46. data/sample/rails_app/app/controllers/application_controller.rb +3 -0
  47. data/sample/rails_app/app/helpers/application_helper.rb +2 -0
  48. data/sample/rails_app/app/views/layouts/application.html.erb +14 -0
  49. data/sample/rails_app/config.ru +4 -0
  50. data/sample/rails_app/config/application.rb +51 -0
  51. data/sample/rails_app/config/boot.rb +13 -0
  52. data/sample/rails_app/config/database.yml +22 -0
  53. data/sample/rails_app/config/environment.rb +5 -0
  54. data/sample/rails_app/config/environments/development.rb +26 -0
  55. data/sample/rails_app/config/environments/production.rb +49 -0
  56. data/sample/rails_app/config/environments/test.rb +35 -0
  57. data/sample/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  58. data/sample/rails_app/config/initializers/inflections.rb +10 -0
  59. data/sample/rails_app/config/initializers/mime_types.rb +5 -0
  60. data/sample/rails_app/config/initializers/secret_token.rb +7 -0
  61. data/sample/rails_app/config/initializers/session_store.rb +8 -0
  62. data/sample/rails_app/config/locales/en.yml +5 -0
  63. data/sample/rails_app/config/routes.rb +62 -0
  64. data/sample/rails_app/db/development.sqlite3 +0 -0
  65. data/sample/rails_app/db/seeds.rb +7 -0
  66. data/sample/rails_app/doc/README_FOR_APP +2 -0
  67. data/sample/rails_app/log/development.log +27 -0
  68. data/sample/rails_app/log/production.log +0 -0
  69. data/sample/rails_app/log/server.log +0 -0
  70. data/sample/rails_app/log/test.log +0 -0
  71. data/sample/rails_app/public/404.html +26 -0
  72. data/sample/rails_app/public/422.html +26 -0
  73. data/sample/rails_app/public/500.html +26 -0
  74. data/sample/rails_app/public/favicon.ico +0 -0
  75. data/sample/rails_app/public/images/rails.png +0 -0
  76. data/sample/rails_app/public/index.html +239 -0
  77. data/sample/rails_app/public/javascripts/application.js +2 -0
  78. data/sample/rails_app/public/javascripts/controls.js +965 -0
  79. data/sample/rails_app/public/javascripts/dragdrop.js +974 -0
  80. data/sample/rails_app/public/javascripts/effects.js +1123 -0
  81. data/sample/rails_app/public/javascripts/prototype.js +6001 -0
  82. data/sample/rails_app/public/javascripts/rails.js +175 -0
  83. data/sample/rails_app/public/robots.txt +5 -0
  84. data/sample/rails_app/script/rails +6 -0
  85. data/sample/rails_app/test/performance/browsing_test.rb +9 -0
  86. data/sample/rails_app/test/test_helper.rb +13 -0
  87. data/test/dao_test.rb +271 -0
  88. data/test/helper.rb +15 -0
  89. data/test/testing.rb +74 -0
  90. 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 }/tree/master"
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
@@ -0,0 +1,8 @@
1
+ ---
2
+ "42": {}
3
+
4
+ users:
5
+ "42":
6
+ a: :b
7
+ time: 2011-01-26 13:27:36.996654 -07:00
8
+ id: 42
data/lib/dao.rb CHANGED
@@ -1,9 +1,88 @@
1
- module Dao
1
+ # built-ins
2
+ #
3
+ require 'enumerator'
4
+ #require 'fileutils'
5
+ #require 'pathname'
6
+ #require 'yaml'
7
+ #require 'yaml/store'
2
8
 
3
- def Dao.version
4
- '0.0.0'
9
+ # gems
10
+ #
11
+ begin
12
+ require 'rubygems'
13
+ rescue LoadError
14
+ nil
5
15
  end
6
16
 
7
- end
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
- DAO = Dao
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,9 @@
1
+ module Dao
2
+ class Api
3
+ Dao.load 'api/initializers.rb'
4
+ Dao.load 'api/modes.rb'
5
+ Dao.load 'api/context.rb'
6
+ Dao.load 'api/endpoints.rb'
7
+ Dao.load 'api/dsl.rb'
8
+ end
9
+ end
@@ -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
@@ -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