api_sketch 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +7 -2
  4. data/README.md +63 -9
  5. data/api_sketch.gemspec +0 -1
  6. data/examples/api_project/config/initializers/shared_blocks.rb +15 -0
  7. data/examples/api_project/resources/places.rb +138 -0
  8. data/examples/api_project/resources/users/points/stats.rb +30 -0
  9. data/examples/api_project/resources/users/points.rb +32 -0
  10. data/examples/api_project/resources/users.rb +341 -0
  11. data/lib/api_sketch/config.rb +1 -1
  12. data/lib/api_sketch/dsl/attribute_parser.rb +19 -0
  13. data/lib/api_sketch/dsl/attributes.rb +45 -0
  14. data/lib/api_sketch/dsl/base.rb +8 -0
  15. data/lib/api_sketch/dsl/complex_attribute_parser.rb +11 -0
  16. data/lib/api_sketch/dsl/headers.rb +17 -0
  17. data/lib/api_sketch/dsl/parameters.rb +31 -0
  18. data/lib/api_sketch/dsl/responses.rb +22 -0
  19. data/lib/api_sketch/dsl.rb +27 -165
  20. data/lib/api_sketch/examples_server.rb +1 -1
  21. data/lib/api_sketch/generators/base.rb +42 -0
  22. data/lib/api_sketch/{generators.rb → generators/bootstrap.rb} +2 -46
  23. data/lib/api_sketch/model/attribute.rb +42 -0
  24. data/lib/api_sketch/model/base.rb +17 -0
  25. data/lib/api_sketch/model/header.rb +7 -0
  26. data/lib/api_sketch/model/parameters.rb +50 -0
  27. data/lib/api_sketch/model/resource.rb +99 -0
  28. data/lib/api_sketch/model/response.rb +12 -0
  29. data/lib/api_sketch/model/shared_block.rb +17 -0
  30. data/lib/api_sketch/model.rb +0 -242
  31. data/lib/api_sketch/{renderers.rb → response_renderer.rb} +0 -0
  32. data/lib/api_sketch/version.rb +1 -1
  33. data/lib/api_sketch.rb +20 -3
  34. data/spec/lib/api_sketch/dsl/attribute_parser_spec.rb +14 -0
  35. data/spec/lib/api_sketch/dsl/attributes_spec.rb +14 -0
  36. data/spec/lib/api_sketch/dsl/complex_attribute_parser_spec.rb +14 -0
  37. data/spec/lib/api_sketch/dsl/headers_spec.rb +14 -0
  38. data/spec/lib/api_sketch/dsl/parameters_spec.rb +14 -0
  39. data/spec/lib/api_sketch/dsl/responses_spec.rb +14 -0
  40. data/spec/lib/api_sketch/dsl_spec.rb +5 -3
  41. data/spec/lib/api_sketch/model/parameters_spec.rb +28 -0
  42. data/spec/lib/api_sketch/{renderers_spec.rb → response_renderer_spec.rb} +51 -1
  43. data/spec/spec_helper.rb +3 -1
  44. data/spec/support/shared_examples.rb +9 -0
  45. metadata +43 -9
  46. data/.ruby-gemset +0 -1
  47. data/.ruby-version +0 -1
@@ -0,0 +1,341 @@
1
+ resource "Update user profile" do
2
+ action "update"
3
+ description "Authenticated user could update his profile fields and password"
4
+ path "/api/users/me.json"
5
+ http_method "PUT"
6
+ format "json"
7
+
8
+ headers do
9
+ add "Authorization" do
10
+ value "Token token=:token_value"
11
+ description ":token_value - is an authorization token value"
12
+ example { (:A..:z).to_a.shuffle[0,16].join }
13
+ required true
14
+ end
15
+
16
+ add "X-Test" do
17
+ value "Test=:perform_test"
18
+ description ":perform_test - test boolean value"
19
+ example true
20
+ end
21
+ end
22
+
23
+ parameters do
24
+ query :document do
25
+ string "hello_message" do
26
+ description "some message"
27
+ end
28
+
29
+ integer "repeat_times" do
30
+ description "times to repeat hello message"
31
+ end
32
+
33
+ integer "page" do
34
+ description "page number"
35
+ required false
36
+ default 1
37
+ end
38
+
39
+ integer "per_page" do
40
+ description "items per page amount"
41
+ required false
42
+ default 25
43
+ end
44
+
45
+ string "name" do
46
+ description "place name"
47
+ required true
48
+ end
49
+
50
+ float "range" do
51
+ description "search range in km"
52
+ required false
53
+ example { rand(100) + rand.round(2) }
54
+ end
55
+
56
+ datetime "start_at" do
57
+ description "start at datetime"
58
+ required false
59
+ example { Time.now.to_s }
60
+ end
61
+
62
+ timestamp "seconds" do
63
+ description "seconds today"
64
+ example { Time.now.to_i }
65
+ end
66
+
67
+ array "place_ids" do
68
+ description "user's places ids"
69
+ required false
70
+ content do
71
+ integer do
72
+ description "hello number"
73
+ end
74
+ string do
75
+ description "more text here"
76
+ end
77
+ document do
78
+ content do
79
+ boolean "is_it_true" do
80
+ end
81
+ end
82
+ end
83
+ document do
84
+ description "some useless data :)"
85
+ content do
86
+ string "test" do
87
+ description "test string"
88
+ end
89
+ document "keys" do
90
+ content do
91
+ integer "sum" do
92
+ end
93
+ string "details text" do
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ body :document do
105
+ document "user" do
106
+ description "user's parameters fields"
107
+ required true
108
+ content do
109
+ string "email" do
110
+ description "user's email value"
111
+ end
112
+ string "password" do
113
+ description "user's profile password"
114
+ end
115
+ string "first_name" do
116
+ description "user's first name"
117
+ end
118
+ string "last_name" do
119
+ description "user's last name"
120
+ end
121
+ string "country_locode" do
122
+ example { ["US", "UA"].sample }
123
+ description "Country location code"
124
+ end
125
+
126
+ document "stats" do
127
+ content do
128
+ timestamp "login_at" do
129
+ description "last login timestamp"
130
+ example { Time.now.to_i }
131
+ end
132
+
133
+ integer "login_count" do
134
+ description "login count"
135
+ example { rand(10000) }
136
+ end
137
+
138
+ string "rank" do
139
+ description "users rank"
140
+ example { ["Junior", "Middle", "Senior"].sample }
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ responses do
150
+ context "Success" do
151
+ http_status :ok # 200
152
+
153
+ parameters do
154
+ body :document do
155
+ document "user" do
156
+ content do
157
+ integer "id" do
158
+ description "User's ID"
159
+ end
160
+ string "email" do
161
+ description "user's email value"
162
+ example { "user#{rand(100)}@email.com" }
163
+ end
164
+ string "first_name" do
165
+ description "user's first name"
166
+ example { "First name #{rand(100)}" }
167
+ end
168
+ string "last_name" do
169
+ description "user's last name"
170
+ example { "Last name #{rand(100)}" }
171
+ end
172
+ document "country" do
173
+ content do
174
+ string "name" do
175
+ description "Country name"
176
+ example { ["USA", "Ukraine", "Poland"].sample }
177
+ end
178
+ string "id" do
179
+ example :location_code
180
+ description "Country ID (Location code)"
181
+ example { ["US", "UA", "PL"].sample }
182
+ end
183
+ end
184
+ end
185
+ array "values" do
186
+ content do
187
+ string do
188
+ example { ["A", "B", "C"].sample }
189
+ end
190
+ integer do
191
+ example { rand(100) }
192
+ end
193
+ document do
194
+ content do
195
+ string "key" do
196
+ example { "Test #{rand(100)}" }
197
+ end
198
+ timestamp "time" do
199
+ example { Time.now.to_i }
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ array "authentications" do
206
+ content do
207
+ document do
208
+ content do
209
+ string "uid" do
210
+ description "user's id at social network"
211
+ example { 5.times.map { 4.times.map { rand(10).to_s }.join }.join("-") }
212
+ end
213
+ string "provider" do
214
+ example { ["facebook", "twitter", "google_plus"].sample }
215
+ description "user's social network type"
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ context "Failure" do
228
+ http_status :bad_request # 400
229
+
230
+ parameters do
231
+ body :document do
232
+ document "error" do
233
+ content do
234
+ string "message" do
235
+ description "Error description"
236
+ example { "Epic fail at your parameters" }
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ resource "Get user profile" do
247
+ action "show"
248
+ description "Authenticated user could get data from his profile"
249
+ path "/api/users/me.json"
250
+ http_method "GET"
251
+ format "json"
252
+
253
+ headers do
254
+ add "Authorization" do
255
+ value "Token token=:token_value"
256
+ description ":token_value - is an authorization token value"
257
+ example { (:A..:z).to_a.shuffle[0,16].join }
258
+ end
259
+ end
260
+
261
+ responses do
262
+ context "Success" do
263
+ http_status :ok # 200
264
+
265
+ parameters do
266
+ body :document do
267
+ document "user" do
268
+ content do
269
+ integer "id" do
270
+ description "User's ID"
271
+ end
272
+ string "email" do
273
+ description "user's email value"
274
+ example { "user#{rand(100)}@email.com" }
275
+ end
276
+ string "first_name" do
277
+ description "user's first name"
278
+ example { "First name #{rand(100)}" }
279
+ end
280
+ string "last_name" do
281
+ description "user's last name"
282
+ example { "Last name #{rand(100)}" }
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ end
292
+
293
+ resource "Create user profile" do
294
+ action "create"
295
+ description "User could register his profile"
296
+ path "/api/users.json"
297
+ http_method "POST"
298
+ format "json"
299
+
300
+ parameters do
301
+ body :document do
302
+ document "user" do
303
+ description "user's parameters fields"
304
+ required true
305
+ content do
306
+ string "email" do
307
+ description "user's email value"
308
+ end
309
+ string "password" do
310
+ description "user's profile password"
311
+ end
312
+ string "first_name" do
313
+ description "user's first name"
314
+ end
315
+ string "last_name" do
316
+ description "user's last name"
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ responses do
324
+ context "Success" do
325
+ http_status :ok # 200
326
+
327
+ parameters do
328
+ body :document do
329
+ string :token do
330
+ description "Auth token"
331
+ end
332
+
333
+ string :email do
334
+ description "User email"
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end
340
+
341
+ end
@@ -4,4 +4,4 @@ require 'mixlib/config'
4
4
  # See https://github.com/opscode/mixlib-config
5
5
  class ::ApiSketch::Config
6
6
  extend Mixlib::Config
7
- end
7
+ end
@@ -0,0 +1,19 @@
1
+ class ApiSketch::DSL::AttributeParser < ApiSketch::DSL::Base
2
+
3
+ def initialize(container_type, &block)
4
+ @attribute_values = {}
5
+ @container_type = container_type
6
+ # INFO: Such long method name is used to ensure that we are would not have such value as key at hash
7
+ define_singleton_method(:set_attributes_as_hash_value_format, block)
8
+ set_attributes_as_hash_value_format
9
+ end
10
+
11
+ def method_missing(method_name, *arguments, &block)
12
+ @attribute_values[method_name] = arguments.first || block
13
+ end
14
+
15
+ def to_h
16
+ @attribute_values
17
+ end
18
+
19
+ end
@@ -0,0 +1,45 @@
1
+ class ApiSketch::DSL::Attributes < ApiSketch::DSL::Base
2
+
3
+ TYPES = [:integer, :string, :float, :boolean, :datetime, :timestamp, :document, :array]
4
+
5
+ def initialize(container_type, &block)
6
+ @container_type = container_type
7
+ @params = []
8
+ define_singleton_method(:initialize_attributes, block)
9
+ initialize_attributes
10
+ end
11
+
12
+ def to_a
13
+ @params
14
+ end
15
+
16
+ TYPES.each do |type_name|
17
+ define_method(type_name) do |*args, &block|
18
+ name = args.first
19
+ if @container_type == :document
20
+ if name.nil? || name.empty? # key name is not provided
21
+ raise ::ApiSketch::Error.new, "Key inside document should have name"
22
+ end
23
+ elsif @container_type == :array
24
+ if (!name.nil? && !name.empty?) # key name is provided
25
+ raise ::ApiSketch::Error.new, "Array element can't have name"
26
+ end
27
+ end
28
+ @params << self.class.build_by(type_name, name, &block)
29
+ end
30
+ end
31
+
32
+ class << self
33
+ def build_by(data_type, attribute_name, &block)
34
+ options = {data_type: data_type}
35
+ options[:name] = attribute_name if attribute_name
36
+ case data_type
37
+ when :document, :array
38
+ ::ApiSketch::Model::Attribute.new(::ApiSketch::DSL::ComplexAttributeParser.new(data_type, &block).to_h.merge(options))
39
+ else
40
+ ::ApiSketch::Model::Attribute.new(::ApiSketch::DSL::AttributeParser.new(data_type, &block).to_h.merge(options))
41
+ end
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,8 @@
1
+ # All DSL clases should inherit this Base class
2
+ class ApiSketch::DSL::Base
3
+
4
+ def use_shared_block(name)
5
+ self.instance_eval(&::ApiSketch::Model::SharedBlock.find(name))
6
+ end
7
+
8
+ end
@@ -0,0 +1,11 @@
1
+ class ApiSketch::DSL::ComplexAttributeParser < ApiSketch::DSL::AttributeParser
2
+
3
+ def method_missing(method_name, *arguments, &block)
4
+ if method_name == :content
5
+ @attribute_values[:content] = ApiSketch::DSL::Attributes.new(@container_type, &block).to_a
6
+ else
7
+ super(method_name, *arguments, &block)
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,17 @@
1
+ class ApiSketch::DSL::Headers < ApiSketch::DSL::Base
2
+
3
+ def initialize(&block)
4
+ @list = []
5
+ define_singleton_method(:initialize_headers_list, block)
6
+ initialize_headers_list
7
+ end
8
+
9
+ def to_a
10
+ @list
11
+ end
12
+
13
+ def add(name, &block)
14
+ @list << ::ApiSketch::Model::Header.new(::ApiSketch::DSL::AttributeParser.new(:document, &block).to_h.merge(name: name))
15
+ end
16
+
17
+ end
@@ -0,0 +1,31 @@
1
+ class ApiSketch::DSL::Parameters < ApiSketch::DSL::Base
2
+
3
+ def initialize(&block)
4
+ @query = []
5
+ @body = []
6
+ @query_container_type = nil
7
+ @body_container_type = nil
8
+ define_singleton_method(:initialize_parameters_list, block)
9
+ initialize_parameters_list
10
+ end
11
+
12
+ def to_h
13
+ {
14
+ query: @query,
15
+ body: @body,
16
+ query_container_type: @query_container_type,
17
+ body_container_type: @body_container_type
18
+ }
19
+ end
20
+
21
+ def query(container_type, &block)
22
+ @query_container_type = container_type
23
+ @query += ::ApiSketch::DSL::Attributes.new(container_type, &block).to_a
24
+ end
25
+
26
+ def body(container_type, &block)
27
+ @body_container_type = container_type
28
+ @body += ::ApiSketch::DSL::Attributes.new(container_type, &block).to_a
29
+ end
30
+
31
+ end
@@ -0,0 +1,22 @@
1
+ class ApiSketch::DSL::Responses < ApiSketch::DSL::Base
2
+
3
+ def initialize(&block)
4
+ @list = []
5
+ define_singleton_method(:initialize_responses_list, block)
6
+ initialize_responses_list
7
+ end
8
+
9
+ def to_a
10
+ @list
11
+ end
12
+
13
+ def context(name, &block)
14
+ attributes = ::ApiSketch::DSL::AttributeParser.new(:root, &block).to_h
15
+ if attributes[:parameters]
16
+ params = ::ApiSketch::DSL::Parameters.new(&attributes[:parameters]).to_h
17
+ attributes[:parameters] = ::ApiSketch::Model::Parameters.new(params)
18
+ end
19
+ @list << ::ApiSketch::Model::Response.new(attributes.merge(name: name))
20
+ end
21
+
22
+ end