clone 1.0.0.alpha → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +1 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +22 -1
  6. data/VERSION +1 -1
  7. data/bin/clone +7 -0
  8. data/clone.gemspec +4 -0
  9. data/docs/readme +16 -0
  10. data/docs/readme.txt +5 -0
  11. data/lib/clone.rb +13 -2
  12. data/lib/clone/config/config.rb +19 -0
  13. data/lib/clone/config/config.yml +22 -0
  14. data/lib/clone/config/default_config.yml +20 -0
  15. data/lib/clone/config/require.rb +3 -0
  16. data/lib/clone/config/version.rb +10 -0
  17. data/lib/clone/config/yml.rb +22 -0
  18. data/lib/clone/config/z_defaults.rb +28 -0
  19. data/lib/clone/generator/engine.rb +581 -0
  20. data/lib/clone/generator/terminal.rb +104 -0
  21. data/lib/clone/helpers/helper_methods.rb +251 -0
  22. data/lib/clone/helpers/local_methods.rb +150 -0
  23. data/lib/clone/helpers/require.rb +37 -0
  24. data/module/Marshal.4.8 +0 -0
  25. data/module/Marshal.4.8.Z +0 -0
  26. data/module/gems/commander-4.1.3/.gitignore +6 -0
  27. data/module/gems/commander-4.1.3/.travis.yml +11 -0
  28. data/module/gems/commander-4.1.3/DEVELOPMENT +15 -0
  29. data/module/gems/commander-4.1.3/Gemfile +3 -0
  30. data/module/gems/commander-4.1.3/History.rdoc +345 -0
  31. data/module/gems/commander-4.1.3/Manifest +38 -0
  32. data/module/gems/commander-4.1.3/README.rdoc +375 -0
  33. data/module/gems/commander-4.1.3/Rakefile +10 -0
  34. data/module/gems/commander-4.1.3/bin/commander +55 -0
  35. data/module/gems/commander-4.1.3/commander.gemspec +26 -0
  36. data/module/gems/commander-4.1.3/lib/commander.rb +32 -0
  37. data/module/gems/commander-4.1.3/lib/commander/blank.rb +8 -0
  38. data/module/gems/commander-4.1.3/lib/commander/command.rb +213 -0
  39. data/module/gems/commander-4.1.3/lib/commander/core_ext.rb +3 -0
  40. data/module/gems/commander-4.1.3/lib/commander/core_ext/array.rb +26 -0
  41. data/module/gems/commander-4.1.3/lib/commander/core_ext/object.rb +11 -0
  42. data/module/gems/commander-4.1.3/lib/commander/delegates.rb +13 -0
  43. data/module/gems/commander-4.1.3/lib/commander/help_formatters.rb +8 -0
  44. data/module/gems/commander-4.1.3/lib/commander/help_formatters/base.rb +18 -0
  45. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal.rb +20 -0
  46. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal/command_help.erb +35 -0
  47. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal/help.erb +36 -0
  48. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal_compact.rb +12 -0
  49. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal_compact/command_help.erb +27 -0
  50. data/module/gems/commander-4.1.3/lib/commander/help_formatters/terminal_compact/help.erb +29 -0
  51. data/module/gems/commander-4.1.3/lib/commander/import.rb +10 -0
  52. data/module/gems/commander-4.1.3/lib/commander/platform.rb +8 -0
  53. data/module/gems/commander-4.1.3/lib/commander/runner.rb +411 -0
  54. data/module/gems/commander-4.1.3/lib/commander/user_interaction.rb +521 -0
  55. data/module/gems/commander-4.1.3/lib/commander/version.rb +3 -0
  56. data/module/gems/commander-4.1.3/spec/command_spec.rb +157 -0
  57. data/module/gems/commander-4.1.3/spec/core_ext/array_spec.rb +20 -0
  58. data/module/gems/commander-4.1.3/spec/core_ext/object_spec.rb +21 -0
  59. data/module/gems/commander-4.1.3/spec/help_formatters/terminal_spec.rb +67 -0
  60. data/module/gems/commander-4.1.3/spec/runner_spec.rb +526 -0
  61. data/module/gems/commander-4.1.3/spec/spec_helper.rb +59 -0
  62. data/module/gems/commander-4.1.3/spec/ui_spec.rb +30 -0
  63. data/module/gems/hello.rb +1 -0
  64. data/module/gems/highline-1.6.19/.gitignore +2 -0
  65. data/module/gems/highline-1.6.19/AUTHORS +3 -0
  66. data/module/gems/highline-1.6.19/CHANGELOG +346 -0
  67. data/module/gems/highline-1.6.19/COPYING +340 -0
  68. data/module/gems/highline-1.6.19/INSTALL +55 -0
  69. data/module/gems/highline-1.6.19/LICENSE +7 -0
  70. data/module/gems/highline-1.6.19/README.rdoc +63 -0
  71. data/module/gems/highline-1.6.19/Rakefile +50 -0
  72. data/module/gems/highline-1.6.19/TODO +6 -0
  73. data/module/gems/highline-1.6.19/examples/ansi_colors.rb +38 -0
  74. data/module/gems/highline-1.6.19/examples/asking_for_arrays.rb +18 -0
  75. data/module/gems/highline-1.6.19/examples/basic_usage.rb +75 -0
  76. data/module/gems/highline-1.6.19/examples/color_scheme.rb +32 -0
  77. data/module/gems/highline-1.6.19/examples/get_character.rb +12 -0
  78. data/module/gems/highline-1.6.19/examples/limit.rb +12 -0
  79. data/module/gems/highline-1.6.19/examples/menus.rb +65 -0
  80. data/module/gems/highline-1.6.19/examples/overwrite.rb +19 -0
  81. data/module/gems/highline-1.6.19/examples/page_and_wrap.rb +322 -0
  82. data/module/gems/highline-1.6.19/examples/password.rb +7 -0
  83. data/module/gems/highline-1.6.19/examples/repeat_entry.rb +21 -0
  84. data/module/gems/highline-1.6.19/examples/trapping_eof.rb +22 -0
  85. data/module/gems/highline-1.6.19/examples/using_readline.rb +17 -0
  86. data/module/gems/highline-1.6.19/highline.gemspec +37 -0
  87. data/module/gems/highline-1.6.19/lib/highline.rb +1012 -0
  88. data/module/gems/highline-1.6.19/lib/highline/color_scheme.rb +134 -0
  89. data/module/gems/highline-1.6.19/lib/highline/compatibility.rb +16 -0
  90. data/module/gems/highline-1.6.19/lib/highline/import.rb +41 -0
  91. data/module/gems/highline-1.6.19/lib/highline/menu.rb +398 -0
  92. data/module/gems/highline-1.6.19/lib/highline/question.rb +475 -0
  93. data/module/gems/highline-1.6.19/lib/highline/simulate.rb +48 -0
  94. data/module/gems/highline-1.6.19/lib/highline/string_extensions.rb +131 -0
  95. data/module/gems/highline-1.6.19/lib/highline/style.rb +181 -0
  96. data/module/gems/highline-1.6.19/lib/highline/system_extensions.rb +222 -0
  97. data/module/gems/highline-1.6.19/setup.rb +1360 -0
  98. data/module/gems/highline-1.6.19/site/.cvsignore +1 -0
  99. data/module/gems/highline-1.6.19/site/highline.css +65 -0
  100. data/module/gems/highline-1.6.19/site/images/logo.png +0 -0
  101. data/module/gems/highline-1.6.19/site/index.html +58 -0
  102. data/module/gems/highline-1.6.19/test/string_methods.rb +32 -0
  103. data/module/gems/highline-1.6.19/test/tc_color_scheme.rb +96 -0
  104. data/module/gems/highline-1.6.19/test/tc_highline.rb +1128 -0
  105. data/module/gems/highline-1.6.19/test/tc_import.rb +52 -0
  106. data/module/gems/highline-1.6.19/test/tc_menu.rb +439 -0
  107. data/module/gems/highline-1.6.19/test/tc_string_extension.rb +20 -0
  108. data/module/gems/highline-1.6.19/test/tc_string_highline.rb +38 -0
  109. data/module/gems/highline-1.6.19/test/tc_style.rb +567 -0
  110. data/module/gems/highline-1.6.19/test/ts_all.rb +16 -0
  111. data/module/latest_specs.4.8 +0 -0
  112. data/module/latest_specs.4.8.gz +0 -0
  113. data/module/prerelease_specs.4.8 +0 -0
  114. data/module/prerelease_specs.4.8.gz +0 -0
  115. data/module/specs.4.8 +0 -0
  116. data/module/specs.4.8.gz +0 -0
  117. data/samples/blather/restlike/Gemfile +4 -0
  118. data/samples/blather/restlike/cmd.yml +1 -0
  119. data/samples/blather/restlike/lib/blather.rb +9 -0
  120. data/samples/blather/restlike/lib/blather/dsl/api.rb +78 -0
  121. data/samples/blather/restlike/lib/blather/dsl/call.rb +13 -0
  122. data/samples/blather/restlike/lib/blather/dsl/client.rb +58 -0
  123. data/samples/blather/restlike/lib/blather/dsl/config.rb +11 -0
  124. data/samples/blather/restlike/lib/blather/dsl/extraDSL.rb +163 -0
  125. data/samples/blather/restlike/lib/blather/meta/require.rb +8 -0
  126. data/samples/blather/restlike/lib/blather/meta/xmpp.yml +5 -0
  127. data/samples/blather/restlike/lib/blather/vendors/xmpp_default.rb +27 -0
  128. data/samples/blather/restlike/readme +2 -0
  129. data/samples/grape/init/Gemfile +2 -0
  130. data/samples/grape/init/cmd.yml +3 -0
  131. data/samples/grape/init/config.ru +2 -0
  132. data/samples/grape/init/docs/grape/documentation.txt +939 -0
  133. data/samples/grape/init/docs/grape/generate_rest_routes.rb +37 -0
  134. data/samples/grape/init/docs/grape/ls_routes.rb +31 -0
  135. data/samples/grape/init/lib/grape.rb +4 -0
  136. data/samples/grape/init/lib/grape/meta/subclasses.rb +20 -0
  137. data/samples/grape/init/lib/grape/xpath/app.rb +30 -0
  138. data/samples/grape/init/lib/grape/xpath/ruotes.rb +6 -0
  139. data/samples/grape/init/readme +1 -0
  140. data/samples/grape/readme +29 -0
  141. data/samples/grape/vendor/lib/grape/vendors/v1/rest.rb +57 -0
  142. data/samples/mongoid/cmd.yml +1 -0
  143. data/samples/mongoid/init/Gemfile +3 -0
  144. data/samples/mongoid/init/cmd.yml +2 -0
  145. data/samples/mongoid/init/docs/mongoid/ModelsRelations.rb +11 -0
  146. data/samples/mongoid/init/docs/mongoid/documents.xls +0 -0
  147. data/samples/mongoid/init/docs/mongoid/generate_modelsToDocs.rb +25 -0
  148. data/samples/mongoid/init/docs/mongoid/modelsToDocs.rb +25 -0
  149. data/samples/mongoid/init/docs/mongoid/relations.txt +1354 -0
  150. data/samples/mongoid/init/lib/mongoid.rb +44 -0
  151. data/samples/mongoid/init/lib/mongoid/dsl/extraDSL_CRUD.rb +446 -0
  152. data/samples/mongoid/init/lib/mongoid/dsl/extraDSL_MP.rb +517 -0
  153. data/samples/mongoid/init/lib/mongoid/dsl/init.rb +37 -0
  154. data/samples/mongoid/init/lib/mongoid/dsl/params.rb +67 -0
  155. data/samples/mongoid/init/lib/mongoid/meta/banned.rb +147 -0
  156. data/samples/mongoid/init/lib/mongoid/meta/control.yml +13 -0
  157. data/samples/mongoid/init/lib/mongoid/meta/mongoid.yml +6 -0
  158. data/samples/mongoid/init/lib/mongoid/meta/mpatch.rb +14 -0
  159. data/samples/mongoid/model/lib/mongoid/models/model.rb +28 -0
  160. data/samples/mongoid/readme +33 -0
  161. data/samples/rack/init/Gemfile +10 -0
  162. data/samples/rack/init/cmd.yml +2 -0
  163. data/samples/rack/init/config.ru +1 -0
  164. data/samples/rack/init/docs/rack/rake introducing.txt +60 -0
  165. data/samples/rack/init/docs/rack/webservers/Thin +43 -0
  166. data/samples/rack/init/docs/rack/webservers/ebb +72 -0
  167. data/samples/rack/init/docs/rack/webservers/fcgi +103 -0
  168. data/samples/rack/init/docs/rack/webservers/mongrel +74 -0
  169. data/samples/rack/init/docs/rack/webservers/passenger +37 -0
  170. data/samples/rack/init/docs/rack/webservers/scgi +188 -0
  171. data/samples/rack/init/lib/rack.rb +1 -0
  172. data/samples/rack/init/lib/rack/meta/webserver/thin.rb +45 -0
  173. data/samples/rack/init/lib/rack/meta/webserver/thin.yml +6 -0
  174. data/samples/rack/init/server.rb +0 -0
  175. data/samples/rack/readme +13 -0
  176. data/samples/rest_client/init/Gemfile +5 -0
  177. data/samples/rest_client/init/boot.rb +2 -0
  178. data/samples/rest_client/init/cmd.yml +1 -0
  179. data/samples/rest_client/init/config/rest_client/defaults.rb +16 -0
  180. data/samples/rest_client/init/docs/rest_client/simple overlook +251 -0
  181. data/samples/rest_client/init/test/rest_client/rest_dsl.rb +5 -0
  182. data/samples/rest_client/readme +7 -0
  183. data/samples/scripts/lines_counter/lines_number.rb +32 -0
  184. data/samples/scripts/lines_counter/readme +5 -0
  185. data/samples/scripts/readme +1 -0
  186. metadata +197 -7
  187. data/lib/clone/cms.rb +0 -56
  188. data/lib/clone/ext.rb +0 -77
  189. data/sample/test.rb +0 -30
@@ -0,0 +1,16 @@
1
+ # ts_all.rb
2
+ #
3
+ # Created by James Edward Gray II on 2005-04-26.
4
+ # Copyright 2005 Gray Productions. All rights reserved.
5
+ #
6
+ # This is Free Software. See LICENSE and COPYING for details.
7
+
8
+ require "test/unit"
9
+
10
+ require "tc_highline"
11
+ require "tc_import"
12
+ require "tc_menu"
13
+ require "tc_style"
14
+ require "tc_color_scheme"
15
+ require "tc_string_highline"
16
+ require "tc_string_extension"
Binary file
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ ### easy XMPP client gem based on EventMachine
2
+ gem 'blather', '~> 0.8.5'
3
+ ### JSON
4
+ gem 'json'
@@ -0,0 +1 @@
1
+ helper: init
@@ -0,0 +1,9 @@
1
+ ### Load Blather config
2
+ require_relative_directory File.join "blather","meta"
3
+ ### Load the blather extraDSL
4
+ require_relative_directory File.join "blather","dsl"
5
+ ### Load the blather Snippet Vendors!
6
+ require_relative_directory File.join "blather","vendors"
7
+
8
+ ### Fire up Blather Engine
9
+ XMPP::Client.start
@@ -0,0 +1,78 @@
1
+ module XMPP
2
+ class API
3
+ ### class var
4
+ begin
5
+ @@route_path ||= Array.new.push(nil)
6
+ @@route_version ||= "v0"
7
+ end
8
+ ### syntax sugar
9
+ begin
10
+ ### main class dsl
11
+ begin
12
+ class << self
13
+ def version(version)
14
+ if !version.nil?
15
+ @@route_version= version.to_s
16
+ end
17
+ end
18
+ def namespace(space, &block)
19
+ @@route_path.push space.to_s.downcase
20
+ index_of_space= (@@route_path.count-1)
21
+ yield
22
+ @@route_path.delete_at index_of_space
23
+ return true
24
+ end
25
+ def rout_path(join='/')
26
+ return @@route_path.join(join)
27
+ end
28
+ def route(method, paths, options, &block)
29
+
30
+ ### generate method path
31
+ begin
32
+ full_path= rout_path('_')
33
+ if !paths.nil?
34
+ full_path+= '_'+paths.to_s
35
+ end
36
+ end
37
+
38
+ ### generate name
39
+ begin
40
+ name_hash= Hash.new
41
+ name_hash['method']= method.to_s
42
+ name_hash['path']= full_path
43
+ name_hash['version']= @@route_version
44
+ method_name= XMPP.generate_method_name(name_hash)
45
+ end
46
+
47
+ ### generate new xmpp path/method
48
+ begin
49
+ XMPP::Call.create_singleton_method method_name, &block
50
+ end
51
+
52
+ end
53
+ def get(paths = nil, options = {}, &block); route(:get, paths, options, &block) end
54
+ def post(paths = nil, options = {}, &block); route(:post, paths, options, &block) end
55
+ def put(paths = nil, options = {}, &block); route(:put, paths, options, &block) end
56
+ def head(paths = nil, options = {}, &block); route(:head, paths, options, &block) end
57
+ def delete(paths = nil, options = {}, &block); route(:delete, paths, options, &block) end
58
+ def self.inherited(subclass)
59
+ @classes ||= Array.new
60
+ @classes.push subclass.name.constantize
61
+ end
62
+ def self.classes
63
+ @classes
64
+ end
65
+ end
66
+ end
67
+ ### sugar alias
68
+ begin
69
+ class << self
70
+ alias_method :group, :namespace
71
+ alias_method :resource, :namespace
72
+ alias_method :resources, :namespace
73
+ alias_method :segment, :namespace
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,13 @@
1
+ module XMPP
2
+ module Call
3
+ ### meta defs
4
+ begin
5
+ def self.create_singleton_method(method,&block)
6
+ define_singleton_method method, &block
7
+ end
8
+ def self.method_missing (method_name)
9
+ return {:error => "invalid method or version"}
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ module XMPP
2
+ class Client
3
+ include Blather::DSL
4
+ class << self
5
+ attr_accessor :engine,
6
+ :ready,
7
+ :answers
8
+
9
+ def start
10
+
11
+ thread = Thread.new do
12
+ ### config
13
+ begin
14
+ XMPP::Client.engine ||= XMPP::Client.new
15
+ XMPP::Client.answers ||= Hash.new
16
+ jid = XMPP::CONFIG::JID
17
+ pwd = XMPP::CONFIG::PWD
18
+ host = XMPP::CONFIG::HOST
19
+ port = XMPP::CONFIG::PORT
20
+ end
21
+ ### settings
22
+ begin
23
+ XMPP::Client.engine.setup jid,pwd,host,port
24
+ XMPP::Client.engine.when_ready do
25
+ XMPP::Client.ready= true
26
+ end
27
+ XMPP::Client.engine.subscription :request? do |s|
28
+ XMPP::Client.engine.write_to_stream s.approve!
29
+ end
30
+ XMPP::Client.engine.disconnected do
31
+ begin
32
+ EM.run do
33
+ XMPP::Client.engine.run
34
+ end
35
+ rescue Exception
36
+ retry
37
+ end
38
+ end
39
+ XMPP::Client.engine.message :chat?, :body do |m|
40
+ XMPP::Client.engine.say m.from, XMPP.receive(m.body)
41
+ end
42
+ end
43
+ ### launch
44
+ begin
45
+ EM.run do
46
+ XMPP::Client.engine.run
47
+ end
48
+ rescue Exception
49
+ retry
50
+ end
51
+ end
52
+ thread.abort_on_exception = true
53
+ return true
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ module XMPP
2
+ class CONFIG
3
+ JID = BLATHER_CONFIG['XMPP']['JID']
4
+ PWD = BLATHER_CONFIG['XMPP']['PWD']
5
+ HOST = BLATHER_CONFIG['XMPP']['HOST'] || '0.0.0.0'
6
+ PORT = BLATHER_CONFIG['XMPP']['PORT'] || '5222'
7
+ TIMEOUT= BLATHER_CONFIG['XMPP']['TIMEOUT'] || 30
8
+ SLEEP_TIME= BLATHER_CONFIG['XMPP']['SLEEP_TIME'] || 0.2
9
+ MSG= BLATHER_CONFIG['XMPP']['MSG'] || {'version'=>'v0','path'=>'/default','method'=>'GET'}
10
+ end
11
+ end
@@ -0,0 +1,163 @@
1
+ module XMPP
2
+ ### extraDSL
3
+ begin
4
+ class << self
5
+ def send(to,what)
6
+ ### hold thread
7
+ if XMPP::Client.ready.nil? && XMPP::Client.engine.nil?
8
+ timeout= 30
9
+ while XMPP::Client.ready.nil?
10
+ sleep 1
11
+ timeout-= 1
12
+ if timeout <= 0
13
+ break
14
+ end
15
+ end
16
+ end
17
+ XMPP::Client.engine.say to, what
18
+ return true
19
+ end
20
+ def ask(to,what)
21
+ ### wait for engine
22
+ begin
23
+ if XMPP::Client.ready.nil? && XMPP::Client.engine.nil?
24
+ timeout= 30
25
+ while XMPP::Client.ready.nil?
26
+ sleep 1
27
+ timeout-= 1
28
+ if timeout <= 0
29
+ break
30
+ end
31
+ end
32
+ end
33
+ end
34
+ ### generate sample form
35
+ begin
36
+ ### check msg structure
37
+ begin
38
+ if what['body'].nil?
39
+ tmp_hash= Hash.new
40
+ begin
41
+ XMPP::CONFIG::MSG.each do |key,value|
42
+ if !what[key].nil?
43
+ tmp_hash[key]= what[key]
44
+ what.delete key
45
+ else
46
+ tmp_hash[key]= value
47
+ end
48
+ end
49
+ end
50
+ tmp_hash['body']= what
51
+ tmp_hash['token']= self.token
52
+ what= tmp_hash
53
+ else
54
+ begin
55
+ XMPP::CONFIG::MSG.each do |key,value|
56
+ if what[key].nil?
57
+ what[key]= value
58
+ end
59
+ end
60
+ end
61
+ what['token']= self.token
62
+ end
63
+ end
64
+ end
65
+ ### mark token
66
+ begin
67
+ XMPP::Client.answers[what['token']]=nil
68
+ end
69
+ ### send msg
70
+ begin
71
+ XMPP::Client.engine.say to, what.to_json
72
+ rescue Exception
73
+ XMPP::Client.engine.say to, what
74
+ end
75
+ ### after msg
76
+ begin
77
+ ### hold process
78
+ begin
79
+ sleep_time= XMPP::CONFIG::SLEEP_TIME
80
+ timeout = XMPP::CONFIG::TIMEOUT.to_f / sleep_time
81
+ while XMPP::Client.answers[what['token']].nil?
82
+ sleep sleep_time
83
+ timeout -= 1
84
+ if timeout <= 0
85
+ #logger
86
+ puts "time is out for the answer"
87
+ break
88
+ end
89
+ end
90
+ end
91
+ ### finis process with answer
92
+ begin
93
+ answer_msg = XMPP::Client.answers[what['token']]
94
+ XMPP::Client.answers.delete what['token']
95
+ end
96
+ ### return answer
97
+ begin
98
+ return answer_msg
99
+ end
100
+ end
101
+ end
102
+ def receive(msg)
103
+ if XMPP.json? msg
104
+ message= JSON.parse(msg)
105
+ XMPP::CONFIG::MSG.each do |key,value|
106
+ if message[key].nil?
107
+ message[key]= value
108
+ end
109
+ end
110
+ if XMPP::Client.answers.keys.include?(message['token'])
111
+ XMPP::Client.answers[message['token']]= message['body']
112
+ else
113
+ call_method= XMPP.generate_method_name(message)
114
+ message['body']= XMPP::Call.__send__(call_method,message['body'])
115
+ return message.to_json
116
+ end
117
+ end
118
+ end
119
+ def token
120
+ begin
121
+ token_string= Time.now.to_s
122
+ [' ',':','-','+'].each do |one_sym|
123
+ token_string.gsub!(one_sym,'')
124
+ end
125
+ token_string+= Random.srand(Time.now.to_i).to_s
126
+ token_string+= XMPP::CONFIG::JID.to_s.gsub('@','').gsub('.','').upcase
127
+ end
128
+ return token_string
129
+ end
130
+ def json? json
131
+ begin
132
+ JSON.parse(json)
133
+ return true
134
+ rescue Exception => e
135
+ return false
136
+ end
137
+ end
138
+ def routes
139
+ XMPP::Call.singleton_methods - [:create_singleton_method,:method_missing]
140
+ end
141
+ def generate_method_name(hash)
142
+
143
+ call_method= String.new
144
+ call_method+= hash['method'].to_s.downcase
145
+ call_method+= '_'
146
+ call_method+= hash['version']
147
+ call_method+= '_'
148
+ if hash['path'].include? '/'
149
+ call_method+= hash['path'].to_s.gsub('/','_')
150
+ elsif hash['path'].include? '\\'
151
+ call_method+= hash['path'].to_s.gsub('\\','_')
152
+ elsif hash['path'].include? '.'
153
+ call_method+= hash['path'].to_s.gsub('.','_')
154
+ else
155
+ call_method+= hash['path']
156
+ end
157
+
158
+ return call_method
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,8 @@
1
+ yml_file = (Dir.entries(File.join(File.dirname(__FILE__))).reject{|x| x[-4,4] != ".yml"})[0].to_s
2
+ BLATHER_CONFIG = YAML.load_file(File.expand_path(File.join(File.dirname(__FILE__),yml_file)))
3
+
4
+ module XMPP
5
+ ### require
6
+ require 'blather/client/dsl'
7
+ require 'json'
8
+ end
@@ -0,0 +1,5 @@
1
+ XMPP:
2
+ JID: arthur@wonderland.lit
3
+ PWD: secr3t
4
+ HOST: 0.0.0.0
5
+ PORT: 5222
@@ -0,0 +1,27 @@
1
+ module Vendors
2
+ class Default < XMPP::API
3
+
4
+ #version :prototype #> default is v0
5
+
6
+ resource :default do
7
+
8
+ get do |params|
9
+
10
+ end
11
+
12
+ post do |params|
13
+
14
+ end
15
+
16
+ put do |params|
17
+
18
+ end
19
+
20
+ delete do |params|
21
+
22
+ end
23
+
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ this is a RESTlike (grape) XMPP client based on blather dsl pkg
2
+ main format is JSON
@@ -0,0 +1,2 @@
1
+ #A Ruby framework for rapid API development with great conventions.
2
+ gem "grape"
@@ -0,0 +1,3 @@
1
+ helper: init
2
+ rack: init
3
+ grape: vendor
@@ -0,0 +1,2 @@
1
+ ###load_Grape_API
2
+ Rack::Handler::Thin.run REST::API,:Port => get_port(THIN_CONFIG['tcp']['min'],THIN_CONFIG['tcp']['max'])
@@ -0,0 +1,939 @@
1
+ Grape is a REST-like API micro-framework for Ruby.
2
+ It's designed to run on Rack or complement
3
+ existing web application frameworks such as Rails
4
+ and Sinatra by providing a simple DSL to easily develop RESTful APIs.
5
+ It has built-in support for common conventions,
6
+ including multiple formats, subdomain/prefix restriction,
7
+ content negotiation, versioning and much more.
8
+
9
+ Grape APIs are Rack applications that are created by
10
+ subclassing Grape::API.
11
+
12
+ Below is a simple example showing some
13
+ of the more common features of Grape in
14
+ the context of recreating parts of the Twitter API.
15
+
16
+ module Twitter
17
+ class API < Grape::API
18
+
19
+ version 'v1', using: :header, vendor: 'twitter'
20
+ format :json
21
+
22
+ helpers do
23
+ def current_user
24
+ @current_user ||= User.authorize!(env)
25
+ end
26
+
27
+ def authenticate!
28
+ error!('401 Unauthorized', 401) unless current_user
29
+ end
30
+ end
31
+
32
+ resource :statuses do
33
+
34
+ desc "Return a public timeline."
35
+ get :public_timeline do
36
+ Status.limit(20)
37
+ end
38
+
39
+ desc "Return a personal timeline."
40
+ get :home_timeline do
41
+ authenticate!
42
+ current_user.statuses.limit(20)
43
+ end
44
+
45
+ desc "Return a status."
46
+ params do
47
+ requires :id, type: Integer, desc: "Status id."
48
+ end
49
+ route_param :id do
50
+ get do
51
+ Status.find(params[:id])
52
+ end
53
+ end
54
+
55
+ desc "Create a status."
56
+ params do
57
+ requires :status, type: String, desc: "Your status."
58
+ end
59
+ post do
60
+ authenticate!
61
+ Status.create!({
62
+ user: current_user,
63
+ text: params[:status]
64
+ })
65
+ end
66
+
67
+ desc "Update a status."
68
+ params do
69
+ requires :id, type: String, desc: "Status ID."
70
+ requires :status, type: String, desc: "Your status."
71
+ end
72
+ put ':id' do
73
+ authenticate!
74
+ current_user.statuses.find(params[:id]).update({
75
+ user: current_user,
76
+ text: params[:status]
77
+ })
78
+ end
79
+
80
+ desc "Delete a status."
81
+ params do
82
+ requires :id, type: String, desc: "Status ID."
83
+ end
84
+ delete ':id' do
85
+ authenticate!
86
+ current_user.statuses.find(params[:id]).destroy
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ Mounting
95
+
96
+ Rack
97
+
98
+ The above sample creates a Rack application that can be run from a rackup config.ru file with rackup:
99
+ run Twitter::API
100
+
101
+
102
+ And would respond to the following routes:
103
+ GET /statuses/public_timeline(.json)
104
+ GET /statuses/home_timeline(.json)
105
+ GET /statuses/:id(.json)
106
+ POST /statuses(.json)
107
+ PUT /statuses/:id(.json)
108
+ DELETE /statuses/:id(.json)
109
+
110
+
111
+ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
112
+
113
+ Alongside Sinatra (or other frameworks)
114
+
115
+ If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using Rack::Cascade:
116
+ # Example config.ru
117
+
118
+ require 'sinatra'
119
+ require 'grape'
120
+
121
+ class API < Grape::API
122
+ get :hello do
123
+ {hello: "world"}
124
+ end
125
+ end
126
+
127
+ class Web < Sinatra::Base
128
+ get '/' do
129
+ "Hello world."
130
+ end
131
+ end
132
+
133
+ use Rack::Session::Cookie
134
+ run Rack::Cascade.new [API, Web]
135
+
136
+
137
+ Rails
138
+
139
+ Place API files into app/api and modify application.rb.
140
+ config.paths.add "app/api", glob: "**/*.rb"
141
+ config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
142
+
143
+
144
+ Modify config/routes:
145
+ mount Twitter::API => '/'
146
+
147
+
148
+ See below for additional code that enables reloading of API changes in development.
149
+
150
+ Modules
151
+
152
+ You can mount multiple API implementations inside another one. These don't have to be different versions, but may be components of the same API.
153
+ class Twitter::API < Grape::API
154
+ mount Twitter::APIv1
155
+ mount Twitter::APIv2
156
+ end
157
+
158
+
159
+ You can also mount on a path, which is similar to using prefix inside the mounted API itself.
160
+ class Twitter::API < Grape::API
161
+ mount Twitter::APIv1 => '/v1'
162
+ end
163
+
164
+
165
+ Versioning
166
+
167
+ There are four strategies in which clients can reach your API's endpoints: :path, :header, :accept_version_header and :param. The default strategy is :path.
168
+
169
+ Path
170
+ version 'v1', using: :path
171
+
172
+
173
+ Using this versioning strategy, clients should pass the desired version in the URL.
174
+ curl -H http://localhost:9292/v1/statuses/public_timeline
175
+
176
+
177
+ Header
178
+ version 'v1', using: :header, vendor: 'twitter'
179
+
180
+
181
+ Using this versioning strategy, clients should pass the desired version in the HTTP Accept head.
182
+ curl -H Accept=application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
183
+
184
+
185
+ By default, the first matching version is used when no Accept header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the :strict option. When this option is set to true, a 406 Not Acceptable error is returned when no correct Accept header is supplied.
186
+
187
+ Accept-Version Header
188
+ version 'v1', using: :accept_version_header
189
+
190
+
191
+ Using this versioning strategy, clients should pass the desired version in the HTTP Accept-Version header.
192
+ curl -H "Accept-Version=v1" http://localhost:9292/statuses/public_timeline
193
+
194
+
195
+ By default, the first matching version is used when no Accept-Version header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the :strict option. When this option is set to true, a 406 Not Acceptable error is returned when no correct Accept header is supplied.
196
+
197
+ Param
198
+ version 'v1', using: :param
199
+
200
+
201
+ Using this versioning strategy, clients should pass the desired version as a request parameter, either in the URL query string or in the request body.
202
+ curl -H http://localhost:9292/statuses/public_timeline?apiver=v1
203
+
204
+
205
+ The default name for the query parameter is 'apiver' but can be specified using the :parameter option.
206
+ version 'v1', using: :param, parameter: "v"
207
+
208
+ curl -H http://localhost:9292/statuses/public_timeline?v=v1
209
+
210
+
211
+ Describing Methods
212
+
213
+ You can add a description to API methods and namespaces.
214
+ desc "Returns your public timeline."
215
+ get :public_timeline do
216
+ Status.limit(20)
217
+ end
218
+
219
+
220
+ Parameters
221
+
222
+ Request parameters are available through the params hash object. This includes GET, POST and PUT parameters, along with any named parameters you specify in your route strings.
223
+ get :public_timeline do
224
+ Status.order(params[:sort_by])
225
+ end
226
+
227
+
228
+ Parameters are automatically populated from the request body on POST and PUT for form input, JSON and XML content-types.
229
+
230
+ The request:
231
+ curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
232
+
233
+
234
+ The Grape endpoint:
235
+ post '/statuses' do
236
+ Status.create!({ text: params[:text] })
237
+ end
238
+
239
+
240
+ Multipart POSTs and PUTs are supported as well.
241
+
242
+ The request:
243
+ curl --form image_file=image.jpg http://localhost:9292/upload
244
+
245
+
246
+ The Grape endpoint:
247
+ post "upload" do
248
+ # file in params[:image_file]
249
+ end
250
+
251
+
252
+ Parameter Validation and Coercion
253
+
254
+ You can define validations and coercion options for your parameters using a params block.
255
+ params do
256
+ requires :id, type: Integer
257
+ optional :text, type: String, regexp: /^[a-z]+$/
258
+ group :media do
259
+ requires :url
260
+ end
261
+ end
262
+ put ':id' do
263
+ # params[:id] is an Integer
264
+ end
265
+
266
+
267
+ When a type is specified an implicit validation is done after the coercion to ensure the output type is the one declared.
268
+
269
+ Optional parameters can have a default value.
270
+ params do
271
+ optional :color, type: String, default: 'blue'
272
+ end
273
+
274
+
275
+ Parameters can be nested using group. In the above example, this means params[:media][:url] is required along with params[:id].
276
+
277
+ Namespace Validation and Coercion
278
+
279
+ Namespaces allow parameter definitions and apply to every method within the namespace.
280
+ namespace :statuses do
281
+ params do
282
+ requires :user_id, type: Integer, desc: "A user ID."
283
+ end
284
+ namespace ":user_id" do
285
+ desc "Retrieve a user's status."
286
+ params do
287
+ requires :status_id, type: Integer, desc: "A status ID."
288
+ end
289
+ get ":status_id" do
290
+ User.find(params[:user_id]).statuses.find(params[:status_id])
291
+ end
292
+ end
293
+ end
294
+
295
+
296
+ The namespace method has a number of aliases, including: group, resource, resources, and segment. Use whichever reads the best for your API.
297
+
298
+ Custom Validators
299
+ class AlphaNumeric < Grape::Validations::Validator
300
+ def validate_param!(attr_name, params)
301
+ unless params[attr_name] =~ /^[[:alnum:]]+$/
302
+ throw :error, status: 400, message: "#{attr_name}: must consist of alpha-numeric characters"
303
+ end
304
+ end
305
+ end
306
+
307
+ params do
308
+ requires :text, alpha_numeric: true
309
+ end
310
+
311
+
312
+ You can also create custom classes that take parameters.
313
+ class Length < Grape::Validations::SingleOptionValidator
314
+ def validate_param!(attr_name, params)
315
+ unless params[attr_name].length <= @option
316
+ throw :error, status: 400, message: "#{attr_name}: must be at the most #{@option} characters long"
317
+ end
318
+ end
319
+ end
320
+
321
+ params do
322
+ requires :text, length: 140
323
+ end
324
+
325
+
326
+ Validation Errors
327
+
328
+ When validation and coercion errors occur an exception of type Grape::Exceptions::Validation is raised. If the exception goes uncaught it will respond with a status of 400 and an error message. You can rescue a Grape::Exceptions::Validation and respond with a custom response.
329
+ rescue_from Grape::Exceptions::Validation do |e|
330
+ Rack::Response.new({
331
+ 'status' => e.status,
332
+ 'message' => e.message,
333
+ 'param' => e.param
334
+ }.to_json, e.status)
335
+ end
336
+
337
+
338
+ Headers
339
+
340
+ Request headers are available through the headers helper or from env in their original form.
341
+ get do
342
+ error!('Unauthorized', 401) unless headers['Secret-Password'] == 'swordfish'
343
+ end
344
+
345
+ get do
346
+ error!('Unauthorized', 401) unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
347
+ end
348
+
349
+
350
+ You can set a response header with header inside an API.
351
+ header "X-Robots-Tag", "noindex"
352
+
353
+
354
+ Routes
355
+
356
+ Optionally, you can define requirements for your named route parameters using regular expressions on namespace or endpoint. The route will match only if all requirements are met.
357
+ get ':id', requirements: { id: /[0-9]*/ } do
358
+ Status.find(params[:id])
359
+ end
360
+
361
+ namespace :outer, requirements: { id: /[0-9]*/ } do
362
+ get :id do
363
+ end
364
+
365
+ get ":id/edit" do
366
+ end
367
+ end
368
+
369
+
370
+ Helpers
371
+
372
+ You can define helper methods that your endpoints can use with the helpers macro by either giving a block or a module.
373
+ module StatusHelpers
374
+ def user_info(user)
375
+ "#{user} has statused #{user.statuses} status(s)"
376
+ end
377
+ end
378
+
379
+ class API < Grape::API
380
+ # define helpers with a block
381
+ helpers do
382
+ def current_user
383
+ User.find(params[:user_id])
384
+ end
385
+ end
386
+
387
+ # or mix in a module
388
+ helpers StatusHelpers
389
+
390
+ get 'info' do
391
+ # helpers available in your endpoint and filters
392
+ user_info(current_user)
393
+ end
394
+ end
395
+
396
+
397
+ Cookies
398
+
399
+ You can set, get and delete your cookies very simply using cookies method.
400
+ class API < Grape::API
401
+
402
+ get 'status_count' do
403
+ cookies[:status_count] ||= 0
404
+ cookies[:status_count] += 1
405
+ { status_count: cookies[:status_count] }
406
+ end
407
+
408
+ delete 'status_count' do
409
+ { status_count: cookies.delete(:status_count) }
410
+ end
411
+
412
+ end
413
+
414
+
415
+ Use a hash-based syntax to set more than one value.
416
+ cookies[:status_count] = {
417
+ value: 0,
418
+ expires: Time.tomorrow,
419
+ domain: '.twitter.com',
420
+ path: '/'
421
+ }
422
+
423
+ cookies[:status_count][:value] +=1
424
+
425
+
426
+ Delete a cookie with delete.
427
+ cookies.delete :status_count
428
+
429
+
430
+ Specify an optional path.
431
+ cookies.delete :status_count, path: '/'
432
+
433
+
434
+ Redirecting
435
+
436
+ You can redirect to a new url temporarily (302) or permanently (301).
437
+ redirect "/statuses"
438
+
439
+ redirect "/statuses", permanent: true
440
+
441
+
442
+ Allowed Methods
443
+
444
+ When you add a GET route for a resource, a route for the HEAD method will also be added automatically. You can disable this behavior with do_not_route_head!.
445
+ class API < Grape::API
446
+
447
+ do_not_route_head!
448
+
449
+ get '/example' do
450
+ # only responds to GET
451
+ end
452
+
453
+ end
454
+
455
+
456
+ When you add a route for a resource, a route for the OPTIONS method will also be added. The response to an OPTIONS request will include an "Allow" header listing the supported methods.
457
+ class API < Grape::API
458
+
459
+ get '/rt_count' do
460
+ { rt_count: current_user.rt_count }
461
+ end
462
+
463
+ params do
464
+ requires :value, type: Integer, desc: 'Value to add to the rt count.'
465
+ end
466
+ put '/rt_count' do
467
+ current_user.rt_count += params[:value].to_i
468
+ { rt_count: current_user.rt_count }
469
+ end
470
+
471
+ end
472
+
473
+ curl -v -X OPTIONS http://localhost:3000/rt_count
474
+
475
+ > OPTIONS /rt_count HTTP/1.1
476
+ >
477
+ < HTTP/1.1 204 No Content
478
+ < Allow: OPTIONS, GET, PUT
479
+
480
+
481
+ You can disable this behavior with do_not_route_options!.
482
+
483
+ If a request for a resource is made with an unsupported HTTP method, an HTTP 405 (Method Not Allowed) response will be returned.
484
+ curl -X DELETE -v http://localhost:3000/rt_count/
485
+
486
+ > DELETE /rt_count/ HTTP/1.1
487
+ > Host: localhost:3000
488
+ >
489
+ < HTTP/1.1 405 Method Not Allowed
490
+ < Allow: OPTIONS, GET, PUT
491
+
492
+
493
+ Raising Exceptions
494
+
495
+ You can abort the execution of an API method by raising errors with error!.
496
+ error! "Access Denied", 401
497
+
498
+
499
+ You can also return JSON formatted objects by raising error! and passing a hash instead of a message.
500
+ error! { "error" => "unexpected error", "detail" => "missing widget" }, 500
501
+
502
+
503
+ Exception Handling
504
+
505
+ Grape can be told to rescue all exceptions and return them in the API format.
506
+ class Twitter::API < Grape::API
507
+ rescue_from :all
508
+ end
509
+
510
+
511
+ You can also rescue specific exceptions.
512
+ class Twitter::API < Grape::API
513
+ rescue_from ArgumentError, NotImplementedError
514
+ end
515
+
516
+
517
+ The error format will match the request format. See "Content-Types" below.
518
+
519
+ Custom error formatters for existing and additional types can be defined with a proc.
520
+ class Twitter::API < Grape::API
521
+ error_formatter :txt, lambda { |message, backtrace, options, env|
522
+ "error: #{message} from #{backtrace}"
523
+ }
524
+ end
525
+
526
+
527
+ You can also use a module or class.
528
+ module CustomFormatter
529
+ def self.call(message, backtrace, options, env)
530
+ { message: message, backtrace: backtrace }
531
+ end
532
+ end
533
+
534
+ class Twitter::API < Grape::API
535
+ error_formatter :custom, CustomFormatter
536
+ end
537
+
538
+
539
+ You can rescue all exceptions with a code block. The rack_response wrapper automatically sets the default error code and content-type.
540
+ class Twitter::API < Grape::API
541
+ rescue_from :all do |e|
542
+ rack_response({ message: "rescued from #{e.class.name}" })
543
+ end
544
+ end
545
+
546
+
547
+ You can also rescue specific exceptions with a code block and handle the Rack response at the lowest level.
548
+ class Twitter::API < Grape::API
549
+ rescue_from :all do |e|
550
+ Rack::Response.new([ e.message ], 500, { "Content-type" => "text/error" }).finish
551
+ end
552
+ end
553
+
554
+
555
+ Or rescue specific exceptions.
556
+ class Twitter::API < Grape::API
557
+ rescue_from ArgumentError do |e|
558
+ Rack::Response.new([ "ArgumentError: #{e.message}" ], 500)
559
+ end
560
+ rescue_from NotImplementedError do |e|
561
+ Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500)
562
+ end
563
+ end
564
+
565
+
566
+ Rails 3.x
567
+
568
+ When mounted inside containers, such as Rails 3.x, errors like "404 Not Found" or "406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance, accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately be translated to an ActionController::RoutingError, which most likely will get rendered to a HTML error page.
569
+
570
+ Most APIs will enjoy preventing downstream handlers from handling errors. You may set the :cascade option to false for the entire API or separately on specific version definitions, which will remove the X-Cascade: true header from API responses.
571
+ cascade false
572
+
573
+ version 'v1', using: :header, vendor: 'twitter', cascade: false
574
+
575
+
576
+ Logging
577
+
578
+ Grape::API provides a logger method which by default will return an instance of the Logger class from Ruby's standard library.
579
+
580
+ To log messages from within an endpoint, you need to define a helper to make the logger available in the endpoint context.
581
+ class API < Grape::API
582
+ helpers do
583
+ def logger
584
+ API.logger
585
+ end
586
+ end
587
+ post '/statuses' do
588
+ # ...
589
+ logger.info "#{current_user} has statused"
590
+ end
591
+ end
592
+
593
+
594
+ You can also set your own logger.
595
+ class MyLogger
596
+ def warning(message)
597
+ puts "this is a warning: #{message}"
598
+ end
599
+ end
600
+
601
+ class API < Grape::API
602
+ logger MyLogger.new
603
+ helpers do
604
+ def logger
605
+ API.logger
606
+ end
607
+ end
608
+ get '/statuses' do
609
+ logger.warning "#{current_user} has statused"
610
+ end
611
+ end
612
+
613
+
614
+ API Formats
615
+
616
+ By default, Grape supports XML, JSON, and TXT content-types. The default format is :txt.
617
+
618
+ Serialization takes place automatically. For example, you do not have to call to_json in each JSON API implementation.
619
+
620
+ Your API can declare which types to support by using content_type. Response format is determined by the request's extension, an explicit format parameter in the query string, or Accept header.
621
+
622
+ The following API will only respond to the JSON content-type and will not parse any other input than application/json, application/x-www-form-urlencoded, multipart/form-data, multipart/related and multipart/mixed. All other requests will fail with an HTTP 406 error code.
623
+ class Twitter::API < Grape::API
624
+ format :json
625
+ end
626
+
627
+
628
+ When the content-type is omitted, Grape will return a 406 error code unless default_format is specified. The following API will try to parse any data without a content-type using a JSON parser.
629
+ class Twitter::API < Grape::API
630
+ format :json
631
+ default_format :json
632
+ end
633
+
634
+
635
+ If you combine format with rescue_from :all, errors will be rendered using the same format. If you do not want this behavior, set the default error formatter with default_error_formatter.
636
+ class Twitter::API < Grape::API
637
+ format :json
638
+ content_type :txt, "text/plain"
639
+ default_error_formatter :txt
640
+ end
641
+
642
+
643
+ Custom formatters for existing and additional types can be defined with a proc.
644
+ class Twitter::API < Grape::API
645
+ content_type :xls, "application/vnd.ms-excel"
646
+ formatter :xls, lambda { |object, env| object.to_xls }
647
+ end
648
+
649
+
650
+ You can also use a module or class.
651
+ module XlsFormatter
652
+ def self.call(object, env)
653
+ object.to_xls
654
+ end
655
+ end
656
+
657
+ class Twitter::API < Grape::API
658
+ content_type :xls, "application/vnd.ms-excel"
659
+ formatter :xls, XlsFormatter
660
+ end
661
+
662
+
663
+ Built-in formats are the following.
664
+ •:json: use object's to_json when available, otherwise call MultiJson.dump
665
+ •:xml: use object's to_xml when available, usually via MultiXml, otherwise call to_s
666
+ •:txt: use object's to_txt when available, otherwise to_s
667
+ •:serializable_hash: use object's serializable_hash when available, otherwise fallback to :json
668
+
669
+ Use default_format to set the fallback format when the format could not be determined from the Accept header. See below for the order for choosing the API format.
670
+ class Twitter::API < Grape::API
671
+ default_format :json
672
+ end
673
+
674
+
675
+ The order for choosing the format is the following.
676
+ •Use the file extension, if specified. If the file is .json, choose the JSON format.
677
+ •Use the value of the format parameter in the query string, if specified.
678
+ •Use the format set by the format option, if specified.
679
+ •Attempt to find an acceptable format from the Accept header.
680
+ •Use the default format, if specified by the default_format option.
681
+ •Default to :txt.
682
+
683
+ JSONP
684
+
685
+ Grape suports JSONP via Rack::JSONP, part of the rack-contrib gem. Add rack-contrib to your Gemfile.
686
+ require 'rack/contrib'
687
+
688
+ class API < Grape::API
689
+ use Rack::JSONP
690
+ format :json
691
+ get '/' do
692
+ 'Hello World'
693
+ end
694
+ end
695
+
696
+
697
+ CORS
698
+
699
+ Grape supports CORS via Rack::CORS, part of the rack-cors gem. Add rack-cors to your Gemfile, then use the middleware in your config.ru file.
700
+ require 'rack/cors'
701
+
702
+ use Rack::Cors do
703
+ allow do
704
+ origins '*'
705
+ resource '*', headers: :any, methods: :get
706
+ end
707
+ end
708
+
709
+ run Twitter::API
710
+
711
+
712
+
713
+ Content-type
714
+
715
+ Content-type is set by the formatter. You can override the content-type of the response at runtime by setting the Content-Type header.
716
+ class API < Grape::API
717
+ get '/home_timeline_js' do
718
+ content_type "application/javascript"
719
+ "var statuses = ...;"
720
+ end
721
+ end
722
+
723
+
724
+ API Data Formats
725
+
726
+ Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters section above. It also supports custom data formats. You must declare additional content-types via content_type and optionally supply a parser via parser unless a parser is already available within Grape to enable a custom format. Such a parser can be a function or a class.
727
+
728
+ With a parser, parsed data is available "as-is" in env['api.request.body']. Without a parser, data is available "as-is" and in env['api.request.input'].
729
+
730
+ The following example is a trivial parser that will assign any input with the "text/custom" content-type to :value. The parameter will be available via params[:value] inside the API call.
731
+ module CustomParser
732
+ def self.call(object, env)
733
+ { value: object.to_s }
734
+ end
735
+ end
736
+
737
+ content_type :txt, "text/plain"
738
+ content_type :custom, "text/custom"
739
+ parser :custom, CustomParser
740
+
741
+ put "value" do
742
+ params[:value]
743
+ end
744
+
745
+
746
+ You can invoke the above API as follows.
747
+ curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
748
+
749
+
750
+ You can disable parsing for a content-type with nil. For example, parser :json, nil will disable JSON parsing altogether. The request data is then available as-is in env['api.request.body'].
751
+
752
+ RESTful Model Representations
753
+
754
+ Grape supports a range of ways to present your data with some help from a generic present method, which accepts two arguments: the object to be presented and the options associated with it. The options hash may include :with, which defines the entity to expose.
755
+
756
+ Grape Entities
757
+
758
+ Add the grape-entity gem to your Gemfile. Please refer to the grape-entity documentation for more details.
759
+
760
+ The following example exposes statuses.
761
+ module API
762
+
763
+ module Entities
764
+ class Status < Grape::Entity
765
+ expose :user_name
766
+ expose :text, documentation: { type: "string", desc: "Status update text." }
767
+ expose :ip, if: { type: :full }
768
+ expose :user_type, user_id, if: lambda{ |status, options| status.user.public? }
769
+ expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
770
+ expose :replies, using: API::Status, as: :replies
771
+ end
772
+ end
773
+
774
+ class Statuses < Grape::API
775
+ version 'v1'
776
+
777
+ desc 'Statuses index', {
778
+ object_fields: API::Entities::Status.documentation
779
+ }
780
+ get '/statuses' do
781
+ statuses = Status.all
782
+ type = current_user.admin? ? :full : :default
783
+ present statuses, with: API::Entities::Status, type: type
784
+ end
785
+ end
786
+ end
787
+
788
+
789
+ You can present with multiple entities using an optional Symbol argument.
790
+ get '/statuses' do
791
+ statuses = Status.all.page(1).per(20)
792
+ present :total_page, 10
793
+ present :per_page, 20
794
+ present :statuses, statuses, with: API::Entities::Status
795
+ end
796
+
797
+
798
+ The response will be
799
+ {
800
+ total_page: 10,
801
+ per_page: 20,
802
+ statuses: []
803
+ }
804
+
805
+
806
+ In addition to separately organizing entities, it may be useful to put them as namespaced classes underneath the model they represent.
807
+ class Status
808
+ def entity
809
+ Status.new(self)
810
+ end
811
+
812
+ class Entity < Grape::Entity
813
+ expose :text, :user_id
814
+ end
815
+ end
816
+
817
+
818
+ If you organize your entities this way, Grape will automatically detect the Entity class and use it to present your models. In this example, if you added present Status.new to your endpoint, Grape will automatically detect that there is a Status::Entity class and use that as the representative entity. This can still be overridden by using the :with option or an explicit represents call.
819
+
820
+ Hypermedia
821
+
822
+ You can use any Hypermedia representer, including Roar. Roar renders JSON and works with the built-in Grape JSON formatter. Add Roar::Representer::JSON into your models or call to_json explicitly in your API implementation.
823
+
824
+ Rabl
825
+
826
+ You can use Rabl templates with the help of the grape-rabl gem, which defines a custom Grape Rabl formatter.
827
+
828
+ Authentication
829
+
830
+ Basic and Digest Auth
831
+
832
+ Grape has built-in Basic and Digest authentication.
833
+ http_basic do |username, password|
834
+ # verify user's password here
835
+ { 'test' => 'password1' }[username] == password
836
+ end
837
+
838
+ http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
839
+ # lookup the user's password here
840
+ { 'user1' => 'password1' }[username]
841
+ end
842
+
843
+
844
+ Use warden-oauth2 or rack-oauth2 for OAuth2 support.
845
+
846
+ Describing and Inspecting an API
847
+
848
+ Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
849
+
850
+ Grape exposes arrays of API versions and compiled routes. Each route contains a route_prefix, route_version, route_namespace, route_method, route_path and route_params. The description and the optional hash that follows the API path may contain any number of keys and its values are also accessible via dynamically-generated route_[name] functions.
851
+ TwitterAPI::versions # yields [ 'v1', 'v2' ]
852
+ TwitterAPI::routes # yields an array of Grape::Route objects
853
+ TwitterAPI::routes[0].route_version # yields 'v1'
854
+ TwitterAPI::routes[0].route_description # etc.
855
+
856
+
857
+ Current Route and Endpoint
858
+
859
+ It's possible to retrieve the information about the current route from within an API call with route.
860
+ class MyAPI < Grape::API
861
+ desc "Returns a description of a parameter."
862
+ params do
863
+ requires :id, type: Integer, desc: "Identity."
864
+ end
865
+ get "params/:id" do
866
+ route.route_params[params[:id]] # yields the parameter description
867
+ end
868
+ end
869
+
870
+
871
+ The current endpoint responding to the request is self within the API block or env['api.endpoint'] elsewhere. The endpoint has some interesting properties, such as source which gives you access to the original code block of the API implementation. This can be particularly useful for building a logger middleware.
872
+ class ApiLogger < Grape::Middleware::Base
873
+ def before
874
+ file = env['api.endpoint'].source.source_location[0]
875
+ line = env['api.endpoint'].source.source_location[1]
876
+ logger.debug "[api] #{file}:#{line}"
877
+ end
878
+ end
879
+
880
+
881
+ Before and After
882
+
883
+ Execute a block before or after every API call with before and after.
884
+ before do
885
+ header "X-Robots-Tag", "noindex"
886
+ end
887
+
888
+
889
+ Anchoring
890
+
891
+ Grape by default anchors all request paths, which means that the request URL should match from start to end to match, otherwise a 404 Not Found is returned. However, this is sometimes not what you want, because it is not always known upfront what can be expected from the call. This is because Rack-mount by default anchors requests to match from the start to the end, or not at all. Rails solves this problem by using a anchor: false option in your routes. In Grape this option can be used as well when a method is defined.
892
+
893
+ For instance when you're API needs to get part of an URL, for instance:
894
+ class TwitterAPI < Grape::API
895
+ namespace :statuses do
896
+ get '/(*:status)', anchor: false do
897
+
898
+ end
899
+ end
900
+ end
901
+
902
+
903
+ This will match all paths starting with '/statuses/'. There is one caveat though: the params[:status] parameter only holds the first part of the request url. Luckily this can be circumvented by using the described above syntax for path specification and using the PATH_INFO Rack environment variable, using env["PATH_INFO"]. This will hold everything that comes after the '/statuses/' part.
904
+
905
+ Writing Tests
906
+
907
+ You can test a Grape API with RSpec by making HTTP requests and examining the response.
908
+
909
+ Writing Tests with Rack
910
+
911
+ Use rack-test and define your API as app.
912
+ require 'spec_helper'
913
+
914
+ describe Twitter::API do
915
+ include Rack::Test::Methods
916
+
917
+ def app
918
+ Twitter::API
919
+ end
920
+
921
+ describe Twitter::API do
922
+ describe "GET /api/v1/statuses" do
923
+ it "returns an empty array of statuses" do
924
+ get "/api/v1/statuses"
925
+ last_response.status.should == 200
926
+ JSON.parse(last_response.body).should == []
927
+ end
928
+ end
929
+ describe "GET /api/v1/statuses/:id" do
930
+ it "returns a status by id" do
931
+ status = Status.create!
932
+ get "/api/v1/statuses/#{status.id}"
933
+ last_response.body.should == status.to_json
934
+ end
935
+ end
936
+ end
937
+ end
938
+
939
+