strapi_ruby 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9903f53042e56a2738031589080d7708a0286ac979d5f4f4aba8e71a914dc63f
4
+ data.tar.gz: 2108880a59532797595fad52bd7461c0cc4e3f12708f0d16213655f86ed1097f
5
+ SHA512:
6
+ metadata.gz: 35b87fdf4069cbccf2fcf9988cf82fb0f46d2dbaedd73464a29cb8d95420608bcef064f77348ab7c8a9507405f1694161b6093eab3a56e671a4252284d29b70a
7
+ data.tar.gz: 4ca960cc893eeda6a890174f83036ab96f18aba94eca66b68febffa33330c713e361a56ece7db8c5a2401c0851caad7e126b842830d9d3824672d05e1598a1d2
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,40 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ Exclude:
4
+ - "spec/**/*"
5
+
6
+
7
+ Style/StringLiterals:
8
+ Enabled: true
9
+ EnforcedStyle: double_quotes
10
+
11
+ Style/StringLiteralsInInterpolation:
12
+ Enabled: true
13
+ EnforcedStyle: double_quotes
14
+
15
+ Layout/LineLength:
16
+ Max: 200
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Metrics/MethodLength:
25
+ Max: 18
26
+
27
+ Metrics/BlockLength:
28
+ Max: 50
29
+
30
+ Metrics/AbcSize:
31
+ Max: 23
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Max: 10
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Max: 10
38
+
39
+ Style/MissingRespondToMissing:
40
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-10-04
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Maxence Robinet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,582 @@
1
+ # StrapiRuby
2
+
3
+ <div align="center">
4
+ <img src="assets/strapi_ruby_logo.png" width="150px">
5
+ </div>
6
+
7
+ **StrapiRuby** is a Ruby wrapper around Strapi REST API, version 4. It has not been tested with previous versions.
8
+
9
+ **Strapi** is an open-source, Node.js based, Headless CMS to easily build customizable APIs.
10
+
11
+ I think it's one of the actual coolest solution for integrating a CMS into Rails for example, so let's dive in!
12
+
13
+ ## Table of contents
14
+
15
+ - [Installation](#installation)
16
+ - Usage:
17
+ - [API](#api):
18
+ - [get](#get)
19
+ - [post](#post)
20
+ - [put](#put)
21
+ - [delete](#delete)
22
+ - [Basic Example: Rails](#basic-example-rails)
23
+ - [Strapi Parameters](#strapi-parameters):
24
+ - [populate](#populate)
25
+ - [fields](#fields)
26
+ - [sort](#sort)
27
+ - [filters](#filters)
28
+ - [page, page_size](#pagination-by-page-page--page_size)
29
+ - [start, limit](#pagination-by-offset-start--limit)
30
+ - [locale](#locale)
31
+ - [publication_state](#publication_state)
32
+ - [Use raw query](#use-raw-query)
33
+ - [Configuration](#configuration):
34
+ - [DateTime conversion](#datetime-conversion)
35
+ - [Markdown conversion](#markdown-conversion)
36
+ - [Faraday block](#faraday-block)
37
+ - [Contributing](#contributing)
38
+ - [Tests](#tests)
39
+
40
+ ## Installation
41
+
42
+ Add this line to your application's Gemfile:
43
+
44
+ ```ruby
45
+ # Gemfile
46
+
47
+ gem "strapi_ruby"
48
+ ```
49
+
50
+ Then if you use Rails, run in your terminal to generate a config initializer. Otherwise copy paste and fill the config block.
51
+
52
+ ```bash
53
+ rake strapi_ruby:config
54
+ ```
55
+
56
+ ```ruby
57
+ # config/initializer/strapi_ruby.rb
58
+
59
+ # Don't
60
+ StrapiRuby.configure do |config|
61
+ config.strapi_server_uri = "http://localhost:1337/api"
62
+ config.strapi_token = "YOUR_TOKEN"
63
+ end
64
+
65
+ # Do
66
+ StrapiRuby.configure do |config|
67
+ config.strapi_server_uri = ENV["STRAPI_SERVER_URI"]
68
+ config.strapi_token = ENV["STRAPI_SERVER_TOKEN"]
69
+ end
70
+ ```
71
+
72
+ ##### IMPORTANT
73
+
74
+ - Always store sensible values in environment variables or Rails credentials
75
+ - Don't forget the trailing `/api` in your uri and don't finish it with a trailing slash.
76
+
77
+ And you're ready to fetch some data!
78
+
79
+ ```ruby
80
+ StrapiRuby.get(resource: :restaurants)
81
+ # => https://localhost:1337/api/restaurants
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### API
87
+
88
+ When passing most of the arguments and options, you can use either `Symbol` or `String` for single fields/items, and an `Array` of `Symbol` or `String`.
89
+
90
+ API methods will return an [OpenStruct](https://ruby-doc.org/stdlib-2.5.1/libdoc/ostruct/rdoc/OpenStruct.html) which is similar to a Hash but you can access keys with dot notation. All fields of the OpenStruct have been recursively converted to OpenStruct as well so it's easy to navigate, as seen below
91
+
92
+ ```ruby
93
+ # These are similar
94
+ answer = StrapiRuby.get(resource: :articles)
95
+ answer = StrapiRuby.get(resource: "articles")
96
+
97
+ # Grab data or meta
98
+ data = answer.data
99
+ meta = answer.meta
100
+
101
+ # Access a specific attribute
102
+ answer = StrapiRuby.get(resource: :articles, id: 2)
103
+ article = answer.data
104
+ title = article.attributes.title
105
+
106
+ # If an error occur, it will be raised to be rescued and displayed in the answer.
107
+ data = answer.data # => nil
108
+ meta = answer.meta # => nil
109
+ error = answer.error.message # => ErrorType:ErrorMessage
110
+ endpoint = answer.endpoint
111
+ # => "https://localhost:1337/api/restaurants?filters[title][$contains]=this+does+not+exists
112
+ ```
113
+
114
+ #### .get
115
+
116
+ ```ruby
117
+ # Display all items of a collection as an array
118
+ answer = StrapiRuby.get(resource: :restaurants)
119
+
120
+
121
+ # Get a specific element
122
+ StrapiRuby.get(resource: :restaurants, id: 1)
123
+ ```
124
+
125
+ #### .post
126
+
127
+ ```ruby
128
+ # Create an item of a collection, return item created
129
+ StrapiRuby.post(resource: :articles,
130
+ data: {title: "This is a brand article",
131
+ content: "created by a POST request"})
132
+
133
+ ```
134
+
135
+ #### .put
136
+
137
+ ```ruby
138
+ # Update a specific item, return item updated
139
+ StrapiRuby.put(resource: :articles,
140
+ id: 23,
141
+ data: {content: "'I've edited this article via a PUT request'"})
142
+ ```
143
+
144
+ #### .delete
145
+
146
+ ```ruby
147
+ # Delete an item, return item deleted
148
+ StrapiRuby.delete(resource: :articles, id: 12)
149
+
150
+ ```
151
+
152
+ #### .escape_empty_answer
153
+
154
+ See [`.escape_empty_answer`](#gracefuly-degrade-errors-when-they-happen)
155
+
156
+ ### Basic Example: Rails
157
+
158
+ ```ruby
159
+ # pages_controller.rb
160
+
161
+ def home
162
+ @articles = StrapiRuby.get(resource: :articles)
163
+ end
164
+ ```
165
+
166
+ ```ruby
167
+ # home.html.erb
168
+
169
+ <% StrapiRuby.escape_empty_answer(@articles) do %>
170
+ <ul>
171
+ <% @articles.data.each do |article| %>
172
+ <li>
173
+ <%= article.attributes.title %>
174
+ </li>
175
+ <% end %>
176
+ </ul>
177
+ <% end %>
178
+ ```
179
+
180
+ ### Strapi Parameters
181
+
182
+ `strapi_ruby`` API functions wraps all parameters offered by the Strapi REST Api V4.
183
+
184
+ The query is built using a transverse hash function similar to Javascript `qs` library used by Strapi.
185
+
186
+ Instead parameters should be passed as a hash to their key and you can use symbols instead of strings.
187
+
188
+ Only exceptions are for the `operators` of the filters used as keys. Also, Integers, eg. for ID, must be passed as strings.
189
+
190
+ Full parameters documentation from Strapi is available [here](https://docs.strapi.io/dev-docs/api/rest/parameters).
191
+
192
+ You can also use their interactive query builder. Just remember to convert the result correctly the resulting JS object to a hash with correct keys and values.
193
+
194
+ #### populate
195
+
196
+ ```ruby
197
+ # Populate one level deep all relations
198
+ StrapiRuby.get(resource: :articles, populate: :*)
199
+ # => /articles?populate=*
200
+
201
+ # --------------------------------
202
+
203
+ # Populate one level deep a specific field
204
+ StrapiRuby.get(resource: :articles, populate: [:categories])
205
+ # => /articles?populate[0]=categories
206
+
207
+ # --------------------------------
208
+
209
+ # Populate two level deep
210
+ StrapiRuby.get(resource: :articles, populate: { author: { populate: [:company] } })
211
+ # => /articles??populate[author][populate][0]=company
212
+
213
+ # --------------------------------
214
+
215
+ # Populate a 2-level component and its media
216
+ StrapiRuby.get(resource: :articles, populate: [
217
+ "seoData",
218
+ "seoData.sharedImage",
219
+ "seoData.sharedImage.media",
220
+ ])
221
+ # => articles?populate[0]=seoData&populate[1]=seoData.sharedImage&populate[2]=seoData.sharedImage.media
222
+
223
+ # --------------------------------
224
+
225
+ # Deeply populate a dynamic zone with 2 components
226
+ StrapiRuby.get(resource: :articles, populate: {
227
+ testDZ: {
228
+ populate: :*,
229
+ },
230
+ })
231
+ # => /articles?populate[testDZ][populate]=*
232
+
233
+ # Using detailed population strategy
234
+ StrapiRuby.get(resource: :articles, populate: {
235
+ testDz: {
236
+ on: {
237
+ "test.test-compo" => {
238
+ fields: [:testString],
239
+ populate: :*,
240
+ },
241
+ "test.test-compo2" => {
242
+ fields: [:testInt],
243
+ },
244
+ },
245
+ },
246
+ })
247
+ # => /articles?populate[testDz][on][test.test-compo][fields][0]=testString&populate[testDz][on][test.test-compo][populate]=*&populate[testDz][on][test.test-compo2][fields][0]=testInt
248
+ ```
249
+
250
+ #### fields
251
+
252
+ ```ruby
253
+ # Select one field
254
+ StrapiRuby.get(resource: :articles, fields: :title)
255
+ # => /articles?fields[0]=title
256
+
257
+ # --------------------------------
258
+
259
+ # Select multiple fields
260
+ StrapiRuby.get(resource: :articles, fields: [:title, :body])
261
+ # => /articles?fields[0]=title&fields[1]=body
262
+ ```
263
+
264
+ #### sort
265
+
266
+ ```ruby
267
+ # Sort by a single key
268
+ StrapiRuby.get(resource: :articles, sort: [])
269
+ # => articles?sort[0]=title&sort[1]=slug
270
+
271
+ # --------------------------------
272
+
273
+ # You can pass sort order and also sort by multiple keys
274
+ StrapiRuby.get(resource: :articles, sort: ["createdAt:desc", "title:asc"])
275
+ # => articles?sort[0]=created:desc&sort[1]=title:asc
276
+ ```
277
+
278
+ #### filters
279
+
280
+ **Use a `String` and not a `Symbol` when using `operator`.**
281
+
282
+ | Operator | Description |
283
+ | --------------- | ----------------------------------- |
284
+ | `$eq` | Equal |
285
+ | `$eqi` | Equal (case-insensitive) |
286
+ | `$ne` | Not Equal |
287
+ | `$nei` | Not Equal (case-insensitive) |
288
+ | `$lt` | Less than |
289
+ | `$lte` | Less than (case-insensitive) |
290
+ | `$gt` | Greater than |
291
+ | `$gte` | Greater than (case-insensitive) |
292
+ | `$in` | In |
293
+ | `$notIn` | Not in |
294
+ | `$contains` | Contains |
295
+ | `$notContains` | Does not contain |
296
+ | `$containsi` | Contains (case-insensitive) |
297
+ | `$notContainsi` | Does not contain (case-insensitive) |
298
+ | `$null` | Is null |
299
+ | `$notNull` | Is not Null |
300
+ | `$between` | Is between |
301
+ | `$startsWith` | Starts with |
302
+ | `$startsWithi` | Starts with (case-insensitive) |
303
+ | `$endsWith` | Ends with |
304
+ | `$endsWithi` | Ends with (case-insensitive) |
305
+ | `$or` | Or |
306
+ | `$and` | And |
307
+ | `$not` | Not |
308
+
309
+ ---
310
+
311
+ ```ruby
312
+ # Simple usage
313
+ StrapiRuby.get(resource: :users, filters: { username: { "$eq" => "John" } })
314
+ # => /users?filters[username][$eq]=John
315
+
316
+ # --------------------------------
317
+
318
+ # Using $in operator to match multiples values
319
+ StrapiRuby.get(resource: :restaurants,
320
+ filters: {
321
+ id: {
322
+ "$in" => ["3", "6", "8"],
323
+ },
324
+ })
325
+ # => /restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8
326
+
327
+ # --------------------------------
328
+
329
+ # Complex filtering with $and and $or
330
+ RubyStrapi.get(resource: :books,
331
+ filters: {
332
+ "$or" => [
333
+ {
334
+ date: {
335
+ "$eq" => "2020-01-01",
336
+ },
337
+ },
338
+ {
339
+ date: {
340
+ "$eq" => "2020-01-02",
341
+ },
342
+ },
343
+ ],
344
+ author: {
345
+ name: {
346
+ "$eq" => "Kai doe",
347
+ },
348
+ },
349
+ })
350
+ # => /books?filters[$or][0][date][$eq]=2020-01-01&filters[$or][1][date][$eq]=2020-01-02&filters[author][name][$eq]=Kai%20doe
351
+
352
+ # --------------------------------
353
+
354
+ # Deep filtering on relation's fields
355
+ StrapiRuby.get(resource: :restaurants,
356
+ filters: {
357
+ chef: {
358
+ restaurants: {
359
+ stars: {
360
+ "$eq" => 5,
361
+ },
362
+ },
363
+ },
364
+ })
365
+ # => /restaurants?filters[chef][restaurants][stars][$eq]=5
366
+ ```
367
+
368
+ #### Pagination by page: page & page_size
369
+
370
+ Only one pagination method is possible.
371
+
372
+ ```ruby
373
+ StrapiRuby.get(resource: :articles, page: 1, page_size: 10)
374
+ # => /articles?pagination[page]=1&pagination[pageSize]=10
375
+ ```
376
+
377
+ #### Pagination by offset: start & limit
378
+
379
+ Only one pagination method is possible.
380
+
381
+ ```ruby
382
+ StrapiRuby.get(resource: :articles, start: 0, limit: 10)
383
+ # => /articles?pagination[start]=0&pagination[limit]=10
384
+ ```
385
+
386
+ #### locale
387
+
388
+ I18n plugin should be installed.
389
+
390
+ ```ruby
391
+ StrapiRuby.get(resource: :articles, locale: :fr)
392
+ #=>?/articles?locale=fr
393
+ ```
394
+
395
+ #### publication_state
396
+
397
+ Use `preview` or `live`
398
+
399
+ ```ruby
400
+ StrapiRuby.get(resource: :articles, publication_state: :preview)
401
+ #=>?/articles?publicationState=preview
402
+ ```
403
+
404
+ ### Use raw query
405
+
406
+ If you wanna pass a raw query you decide to build, just use raw as an option.
407
+ It cannot be combined with any other Strapi parameters.
408
+
409
+ ```ruby
410
+ StrapiRuby.get(resource: articles:, raw: "?fields=title&sort=createdAt:desc")
411
+ # => /articles?fields=title&sort=createdAt:desc"
412
+ ```
413
+
414
+ ## Configuration
415
+
416
+ You can pass more options via the config block.
417
+
418
+ ### Show Endpoint
419
+
420
+ This option is for accessing the resulting endpoint in a **successful** error, ie. `strapi_server_uri` + its query.
421
+
422
+ It defaults to `false`.
423
+
424
+ ```ruby
425
+ # Pass this as a parameter to the config block
426
+ StrapiRuby.configure do |config|
427
+ #...
428
+ config.show_endpoint = true
429
+ #...
430
+ end
431
+
432
+ # Or as an option to one of the API functions
433
+ StrapiRuby.get(resource: :articles, show_endpoint: true)
434
+
435
+
436
+ # You can access it in the answer
437
+
438
+ StrapiRuby.get(resource: :articles, show_endpoint: true).endpoint
439
+ # => https://localhost:1337/api/restaurants
440
+ ```
441
+
442
+ Or directly in the options parameters
443
+
444
+ ### DateTime Conversion
445
+
446
+ By default, any `createdAt`, `publishedAt` and `updatedAt` fields in the answer will be recursively converted to `DateTime` instances, making it easy to use [`#strftime`](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime) method.
447
+
448
+ But if you don't want this conversion, pass it to the configure block.
449
+
450
+ ```ruby
451
+ StrapiRuby.configure do |config|
452
+ #...
453
+ config.convert_to_datetime = false
454
+ #...
455
+ end
456
+ ```
457
+
458
+ ### Markdown Conversion
459
+
460
+ Selected fields will automatically be converted to HTML using `redcarpet` gem. This is very useful to get data ready for the views.
461
+
462
+ ```ruby
463
+ # You can pass this in your config file:
464
+
465
+ StrapiRuby.configure do |config|
466
+ #...
467
+ config.convert_to_html = [:body]
468
+ #...
469
+ end
470
+
471
+ # Or as an option to one of the API functions
472
+ StrapiRuby.get(resource: :articles, fields: :body, convert_to_html: [:body])
473
+ ```
474
+
475
+ ### Faraday Block
476
+
477
+ #### Passing a Proc
478
+
479
+ You can pass a proc when configuring Strapi Ruby just as you'd pass a block when creating a new instance of a Faraday.
480
+ Check [Faraday documentation](https://lostisland.github.io/faraday/#/customization/connection-options)
481
+
482
+ ```ruby
483
+ StrapiRuby.configure do |config|
484
+ #...
485
+ config.faraday = Proc.new do |faraday|
486
+ faraday.headers['X-Custom-Header'] = 'Custom-Value'
487
+ end
488
+ #...
489
+ end
490
+ ```
491
+
492
+ #### Default Faraday::Connection used by the gem
493
+
494
+ Default options used by this gem are `url_encode` and `Faraday.default_adapter`, but you can override them.
495
+
496
+ #### Default Faraday::Connection headers
497
+
498
+ Default headers cannot be overriden but will be merged with your added configuration.
499
+
500
+ ```ruby
501
+ default_headers = { "Content-Type" => "application/json",
502
+ "Authorization" => "Bearer #{strapi_token}",
503
+ "User-Agent" => "StrapiRuby/#{StrapiRuby::VERSION}" }
504
+ ```
505
+
506
+ ### Handling Errors
507
+
508
+ Depending on your utilisation, there are multiple ways to handle errors.
509
+
510
+ #### Error Classes
511
+
512
+ ```ruby
513
+ # Config Error
514
+ class ConfigurationError < StandardError
515
+
516
+ # Client Error
517
+ class ClientError < StandardError
518
+
519
+ # Client Error Specific Error
520
+ class ConnectionError < ClientError
521
+ class UnauthorizedError < ClientError
522
+ class ForbiddenError < ClientError
523
+ class NotFoundError < ClientError
524
+ class UnprocessableEntityError < ClientError
525
+ class ServerError < ClientError
526
+ class BadRequestError < ClientError
527
+ class JSONParsingError < ClientError
528
+ ```
529
+
530
+ #### Gracefuly degrade errors when they happen
531
+
532
+ One way to handle errors and gracefuly degrade is using `.escape_empty_answer` and use a block to nest your data accessing code.
533
+
534
+ ##### Definition
535
+
536
+ ```ruby
537
+ # Definition
538
+ module StrapiRuby
539
+ def escape_empty_answer(answer)
540
+ return answer.error.message if answer.data.nil?
541
+ yield
542
+ end
543
+ end
544
+ ```
545
+
546
+ ##### Example : Usage in a Rails view
547
+
548
+ ```erb
549
+ <% StrapiRuby.escape_empty_answer(answer) do %>
550
+ <%= answer.title %>
551
+ <%= answer.body %>
552
+ <% end %>
553
+ ```
554
+
555
+ Or you may want to handle specific errors like this:
556
+
557
+ ```ruby
558
+ # some_controller.rb
559
+ begin
560
+ answer = StrapiRuby.get(resource: "articles")
561
+ rescue NotFoundError e =>
562
+ # Do something to avoid an embarassing situation
563
+ rescue ClientError e =>
564
+ # Do something to avoid an embarassing situation
565
+ end
566
+ ```
567
+
568
+ ## Contributing
569
+
570
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/saint-james-fr/strapi_ruby](https://github.com/saint-james-fr/strapi_ruby). This project is intended to be a safe, welcoming space for collaboration.
571
+
572
+ ## Tests
573
+
574
+ Run `bundle exec rspec` to run the tests.
575
+
576
+ Inside `spec/integration.rb` you'll have access to integration tests.
577
+ You'll need to configure environment variables within the repo and run a strapi server to run these tests sucessfully.
578
+ See Strapi documentation for more details about installing a Strapi Server [here](https://docs.strapi.io/dev-docs/quick-start)
579
+
580
+ ## License
581
+
582
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ # require "rubocop/rake_task"
9
+
10
+ # RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec]
Binary file
data/features.md ADDED
@@ -0,0 +1,7 @@
1
+ # Features
2
+
3
+ - I want to be able to configure using a YAML file
4
+ - I want to have a simple API to unpack and read fetched data
5
+ - I want to have easy support for SEO plugin
6
+ - I want to do all GET Operations
7
+ - I want to do all POST operations