fake_api 0.9.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 291b0a2acbe638fd0298f165f52361d40ed135aaae8ac5788879071810796573
4
+ data.tar.gz: ffd968738a8652262fbeeabc9ffda18c99139989a55ac22f4c7cdb0bdd7db564
5
+ SHA512:
6
+ metadata.gz: 32d03d9f654d063f9726cc944ebc4c380e0ddfca498a49a27a295f547c829cb6adfecd6ef92618021f53753dc3e85ba8b947bb31f6eb46a5ad27a52412e622bd
7
+ data.tar.gz: c2cac52ff859c0566d60d80ab448d528da024c06801f334dbb152bd225b78a962e731e45e7bb32ac6a4dc01ee19dd531ff5d9bef46008cc4ae9757bcd7573589
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Igor Kasyanchuk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,122 @@
1
+ # Fake API
2
+
3
+ The **fastest** way to prototype API with most real dummy data, and provide them to other team members.
4
+
5
+ Instead of creating many new controllers and actions, and sending random data like `"First name #{rand(100)}"` you'll get real data for developing/testing.
6
+
7
+ Gem has a syntax similar to rails routes, factory bot, and uses faker to generate dummy data.
8
+
9
+ ![Demo](/docs/fake_api_demo.gif)
10
+
11
+ Features:
12
+
13
+ * fast API prototyping
14
+ * clear and familiar syntax
15
+ * Faker for test data generation
16
+ * including links to images if needed
17
+ * manage cookies, session, headers with fake response
18
+ * executes your factories in real-time, so you always get fresh and random data
19
+ * has generator
20
+
21
+ ## Installation & Usage
22
+
23
+ Installation process is very simple.
24
+
25
+ - add `gem 'fake_api'` to the Gemfile
26
+ - run `bundle install`
27
+ - mount gem in routes.rb `mount FakeApi::Engine => '/api'`
28
+ - `rails g fake_api Product`. It will generate factory and routing files.
29
+ - edit `app/fake_api/*.rb`, define your routing and factories.
30
+ - open `localhost:300/api/projects.json` (see step 5) or `localhost:300/api/projects.xml` (to return XML in API response)
31
+ - profit :)
32
+
33
+ You can keep your routing and factories in many files.
34
+
35
+ ## Examples
36
+
37
+ Sample of the factory:
38
+
39
+ ```ruby
40
+ # app/fake_api/factory.rb
41
+ class Factory < FakeApi::Factoring
42
+
43
+ # Example of User object
44
+ # you can see that it will generate link to fake image
45
+ factory(:user) do
46
+ {
47
+ id: rand(100),
48
+ first_name: Faker::Name.first_name,
49
+ last_name: Faker::Name.first_name,
50
+ avatar_url: Faker::Avatar.image(size: '128x128'),
51
+ age: rand(100)
52
+ }
53
+ end
54
+
55
+ # heare you can put "relations" to other factories
56
+ # see "author" node
57
+ factory(:project) do
58
+ {
59
+ id: rand(1_000),
60
+ title: Faker::Company.name,
61
+ description: Faker::Company.catch_phrase,
62
+ type: Faker::Company.type,
63
+ author: object(:user)
64
+ }
65
+ end
66
+
67
+ # or have factory which contains from the list of Projects and a single user
68
+ factory(:complex) do
69
+ {
70
+ projects: create_list(:project, 2),
71
+ user: object(:user)
72
+ }
73
+ end
74
+
75
+ end
76
+ ```
77
+
78
+ And sample of routing:
79
+
80
+ ```ruby
81
+ # app/fake_api/app_routing.rb
82
+ class AppRouting < FakeApi::Routing
83
+ get('/projects').and_return { create_list(:project, 5) }.with_status(202).with_headers({TOKEN: "SECRET"})
84
+ get(%r{/projects/\d+$}).and_return { object(:project) }
85
+ post('/projects').and_return { object(:project).merge({created: 'ok'}) }
86
+ delete(%r{/projects/\d+$}).and_return { { result: :deleted } }.with_status(333)
87
+
88
+ post('/auth')
89
+ .and_return { { status: "OK" } }
90
+ .with_cookies({x: "A"})
91
+ .with_session({y: "B"})
92
+ .with_headers({token: "C"})
93
+ end
94
+ ```
95
+
96
+ ## Factory Methods
97
+
98
+ - `create_list(:factory_name, 10)` to create an array of 10 factories.
99
+ - `object(:factory_nane)` to return a factory
100
+
101
+ ## Routing Methods
102
+
103
+ - `get/post/put/patch/delete` to define route
104
+ - `and_return` to specify the response. This response will be converted to FORMAT (json, xml, js, csv(for arrays) etc)
105
+ - `with_cookies` to list returned cookies
106
+ - `with_session` to list changes in session
107
+ - `with_headers` to list returned headers
108
+
109
+ ## TODO
110
+
111
+ - CI (travis, github actions, etc)
112
+ - render ERB?
113
+ - exclude from code converage generator and dummy app
114
+ - make code coverage 100%
115
+
116
+ ## Contributing
117
+
118
+ You are welcome to contribute.
119
+
120
+ ## License
121
+
122
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'FakeApi'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
File without changes
@@ -0,0 +1,27 @@
1
+ class FakeController < ApplicationController
2
+
3
+ def data
4
+ result = FakeApi::Handler.handle(request.method, path: params[:path], params: params, headers: request.headers)
5
+
6
+ response.status = result.status
7
+
8
+ result.headers.each { |k, v| headers[k.to_s] = v }
9
+ result.cookies.each { |k, v| cookies[k.to_s] = v }
10
+ result.session.each { |k, v| session[k.to_s] = v }
11
+
12
+ respond_to do |format|
13
+ format.html { render plain: result.data.inspect }
14
+ format.xml { render xml: result.data }
15
+ format.json { render json: result.data }
16
+ format.js { render js: result.data }
17
+ format.csv {
18
+ require 'csv'
19
+ csv_string = CSV.generate(headers: false) do |csv|
20
+ result.data.each { |i| csv << i }
21
+ end
22
+ render plain: csv_string
23
+ }
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,6 @@
1
+ FakeApi::Engine.routes.draw do
2
+ match '/*path', to: 'fake#data', via: :all
3
+
4
+ # to get mounted route
5
+ get '/__test__test', to: 'fake#data', as: :__test__test
6
+ end
@@ -0,0 +1,16 @@
1
+ require "fake_api/engine"
2
+ require "faker"
3
+ require "pry"
4
+ require "ostruct"
5
+
6
+ require_relative './fake_api/data.rb'
7
+ require_relative './fake_api/base.rb'
8
+ require_relative './fake_api/route.rb'
9
+ require_relative './fake_api/factory.rb'
10
+ require_relative './fake_api/handler.rb'
11
+ require_relative './fake_api/debug.rb'
12
+
13
+ require_relative './generators/fake_api_generator.rb' rescue nil
14
+
15
+ module FakeApi
16
+ end
@@ -0,0 +1,11 @@
1
+ module FakeApi
2
+ class Base
3
+ mattr_accessor :data
4
+ @@data = FakeApiData.instance
5
+ class << self
6
+ delegate_missing_to :data
7
+ end
8
+ end
9
+ Routing = Base
10
+ Factoring = Base
11
+ end
@@ -0,0 +1,71 @@
1
+ module FakeApi
2
+
3
+ class FakeApiData
4
+ attr_reader :responses, :routes
5
+
6
+ def FakeApiData.instance
7
+ @@instance ||= FakeApiData.new
8
+ end
9
+
10
+ def initialize
11
+ @responses = {}
12
+ @routes = {}
13
+ end
14
+
15
+ def factory(name, &block)
16
+ response = Factory.new(name: name, value: block)
17
+ @responses[name] = response
18
+ end
19
+
20
+ def route(request_method, route:, &block)
21
+ e = Route.new(route: route)
22
+ @routes[request_method.upcase] ||= {}
23
+ @routes[request_method.upcase][route] = e
24
+ end
25
+
26
+ def get(path)
27
+ route("GET", route: path)
28
+ end
29
+
30
+ def post(path)
31
+ route("POST", route: path)
32
+ end
33
+
34
+ def put(path)
35
+ route("PUT", route: path)
36
+ end
37
+
38
+ def patch(path)
39
+ route("PATCH", route: path)
40
+ end
41
+
42
+ def delete(path)
43
+ route("DELETE", route: path)
44
+ end
45
+
46
+ def object(something)
47
+ response_or_value(something)
48
+ end
49
+ alias :create :object
50
+ alias :build :object
51
+
52
+ def create_list(something, amount)
53
+ result = []
54
+ amount.times { result << response_or_value(something) }
55
+ result
56
+ end
57
+
58
+ def response_or_value(e)
59
+ if e.is_a?(Symbol)
60
+ if response = FakeApiData.instance.responses[e]
61
+ response.value.call
62
+ else
63
+ nil
64
+ end
65
+ else
66
+ e
67
+ end
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,42 @@
1
+ module FakeApi
2
+
3
+ class Debug
4
+
5
+ def Debug.status
6
+ result = {}
7
+ result[:factories] = []
8
+ result[:responses] = {}
9
+
10
+ FakeApiData.instance.responses.each do |name, response|
11
+ result[:factories] << name
12
+ end
13
+
14
+ FakeApiData.instance.routes.each do |request_method, info|
15
+ result[:responses][request_method] ||= []
16
+ info.each do |(path, route)|
17
+ result[:responses][request_method] << {
18
+ route: route.route,
19
+ status: route.status
20
+ }
21
+ end
22
+ end
23
+ result
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ # module FakeApi
31
+ # extend ActiveSupport::Autoload
32
+
33
+ # eager_autoload do
34
+ # autoload :Handler
35
+ # autoload :Route
36
+ # autoload :Response
37
+ # autoload :Factory
38
+ # autoload :Routing
39
+ # autoload :Handler
40
+ # autoload :FakeApiData
41
+ # end
42
+ # end
@@ -0,0 +1,22 @@
1
+ module FakeApi
2
+ class Engine < ::Rails::Engine
3
+ def Engine.mounted_in
4
+ @mounted_in ||= FakeApi::Engine.routes.url_helpers.__test__test_path.gsub("/__test__test", '')
5
+ end
6
+
7
+ config.to_prepare do
8
+ Engine.load_fake_api_dependencies
9
+ end
10
+
11
+ config.after_initialize do |app|
12
+ app.config.paths.add 'app/fake_api', eager_load: true
13
+ app.config.autoload_paths += Dir["#{Rails.root}/app/fake_api/**/*.rb"]
14
+ end
15
+
16
+ def Engine.load_fake_api_dependencies
17
+ Dir["#{Rails.root}/app/fake_api/**/*.rb"].each do |file|
18
+ require_dependency file
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module FakeApi
2
+ class Factory
3
+ attr_reader :name, :value
4
+ def initialize(name:, value: nil, &block)
5
+ @name = name
6
+ @value = value || block
7
+ end
8
+
9
+ def returns(new_value = nil, &block)
10
+ @value = new_value || block
11
+ self
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,56 @@
1
+ module FakeApi
2
+ class Handler
3
+
4
+ def Handler.handle(method, path:, params: {}, headers: {})
5
+ if route = Handler.resolve(method, path)
6
+ result(
7
+ data: route.response.call,
8
+ status: route.status,
9
+ headers: route.headers,
10
+ cookies: route.cookies,
11
+ session: route.session
12
+ )
13
+ else
14
+ # READ MORE: https://github.com/igorkasyanchuk/fake_api
15
+ result(
16
+ data: %Q{
17
+ Route "#{FakeApi::Engine.mounted_in}/#{path}" was not found. Please edit your fake_api rounting file(s) in app/fake_api/*.rb.\n\nAvailable:
18
+ \n#{available.presence || 'NONE'}
19
+ }.strip,
20
+ status: 500,
21
+ )
22
+ end
23
+ end
24
+
25
+ def Handler.result(data:, status: 200, headers: {}, cookies: {}, session: {})
26
+ OpenStruct.new(
27
+ data: data,
28
+ status: status,
29
+ headers: headers,
30
+ cookies: cookies,
31
+ session: session
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ def Handler.resolve(method, path)
38
+ FakeApiData.instance.routes.fetch(method, {}).each do |k, v|
39
+ return v if "/#{path}" == k
40
+ return v if k.is_a?(Regexp) && "/#{path}" =~ k
41
+ end
42
+ nil
43
+ end
44
+
45
+ def Handler.available
46
+ result = []
47
+ FakeApiData.instance.routes.collect do |method, route|
48
+ route.each do |k, v|
49
+ result << "#{method} #{FakeApi::Engine.mounted_in}#{k}"
50
+ end
51
+ end
52
+ result.sort.join("\n")
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,40 @@
1
+ module FakeApi
2
+ class Route
3
+ attr_reader :route, :response, :status, :headers, :cookies, :session
4
+
5
+ def initialize(route:, response: nil, status: 200, headers: {}, cookies: {}, session: {})
6
+ @route = route
7
+ @response = response
8
+ @status = status
9
+ @headers = headers
10
+ @cookies = cookies
11
+ @session = session
12
+ self
13
+ end
14
+
15
+ def with_status(new_status)
16
+ @status = new_status
17
+ self
18
+ end
19
+
20
+ def and_return(new_response = nil, &block)
21
+ @response = new_response || block
22
+ self
23
+ end
24
+
25
+ def with_headers(new_headers)
26
+ @headers = new_headers
27
+ self
28
+ end
29
+
30
+ def with_cookies(new_cookies)
31
+ @cookies = new_cookies
32
+ self
33
+ end
34
+
35
+ def with_session(new_session)
36
+ @session = new_session
37
+ self
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module FakeApi
2
+ VERSION = '0.9.0'
3
+ end
@@ -0,0 +1,20 @@
1
+ class FakeApiGenerator < Rails::Generators::Base
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def create_fake_api_file
5
+ if file_name.blank?
6
+ puts "Sample: rails g fake_api Product"
7
+ exit
8
+ end
9
+ template 'routing.rb', File.join('app/fake_api', "#{file_name}_routing.rb")
10
+ template 'factory.rb', File.join('app/fake_api', "#{file_name}_factory.rb")
11
+ end
12
+
13
+ def class_name
14
+ args[0]&.strip
15
+ end
16
+
17
+ def file_name
18
+ class_name&.underscore
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ class <%= class_name %>Factory < FakeApi::Factoring
2
+
3
+ factory(:user) do
4
+ {
5
+ id: rand(100),
6
+ first_name: Faker::Name.first_name,
7
+ last_name: Faker::Name.first_name,
8
+ avatar_url: Faker::Avatar.image(size: '128x128')
9
+ }
10
+ end
11
+
12
+ factory(:project) do
13
+ {
14
+ id: rand(1_000),
15
+ title: Faker::Company.name,
16
+ description: Faker::Company.catch_phrase,
17
+ type: Faker::Company.type,
18
+ author: object(:user)
19
+ }
20
+ end
21
+
22
+ factory(:complex) do
23
+ {
24
+ projects: create_list(:project, 2),
25
+ user: object(:user)
26
+ }
27
+ end
28
+
29
+ end
@@ -0,0 +1,13 @@
1
+ class <%= class_name %>Routing < FakeApi::Routing
2
+ get('/projects').and_return { create_list(:project, 5) }.with_status(202).with_headers({TOKEN: "SECRET"})
3
+
4
+ get(%r{/projects/\d+$})
5
+ .and_return { object(:project) }
6
+ .with_cookies({x: "A"})
7
+ .with_session({y: "B"})
8
+ .with_headers({token: "C"})
9
+
10
+ post('/projects').and_return { object(:project).merge({created: 'ok'}) }
11
+
12
+ delete(%r{/projects/\d+$}).and_return { { result: :deleted } }.with_status(200)
13
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :fake_api do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fake_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Igor Kasyanchuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-02-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faker
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: The fastest way to prototype API in your Rails app using Faker and mix
84
+ of rails routes, factory bot and faker syntax
85
+ email:
86
+ - igorkasyanchuk@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - MIT-LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - app/assets/config/fake_api_manifest.js
95
+ - app/controllers/fake_controller.rb
96
+ - config/routes.rb
97
+ - lib/fake_api.rb
98
+ - lib/fake_api/base.rb
99
+ - lib/fake_api/data.rb
100
+ - lib/fake_api/debug.rb
101
+ - lib/fake_api/engine.rb
102
+ - lib/fake_api/factory.rb
103
+ - lib/fake_api/handler.rb
104
+ - lib/fake_api/route.rb
105
+ - lib/fake_api/version.rb
106
+ - lib/generators/fake_api_generator.rb
107
+ - lib/generators/templates/factory.rb
108
+ - lib/generators/templates/routing.rb
109
+ - lib/tasks/fake_api_tasks.rake
110
+ homepage: https://github.com/igorkasyanchuk/fake_api
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.0.6
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: The fastest way to prototype API in your Rails app.
133
+ test_files: []