api-scheme 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0dc806b74dad245da064e9ca3f750999f1bda267
4
+ data.tar.gz: 6f0e255f4985f615909b206865ab34d782de4c09
5
+ SHA512:
6
+ metadata.gz: cbd4b1a968ab50cff49de048bbf81ba6f79f72962b5fa6a03259bc661b4e283ec7b03dfd61fc5088f1f4edc7c253b8b7392e3cf3463aec0cd6e1aa4dc81d908c
7
+ data.tar.gz: 15fba0ab5d763c73826d944dbf92f06c598f210bd378bc72b348047c038d1cc8efe0ae3a58673f4582e2dd8d3b505c2a807dc6dcf6212eae5a9cb9cb739823b7
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+
14
+ *~
15
+ *.sw*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.3
5
+ before_install: gem install bundler -v 1.15.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rails-api-scheme.gemspec
4
+ gemspec
@@ -0,0 +1,94 @@
1
+ # Api::Scheme
2
+
3
+ Provides simple error handling and param processing scheme
4
+ to make API and other actions for Rail Action Controller
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'api-scheme'
12
+ ```
13
+
14
+ or with explicit require:
15
+
16
+ ```ruby
17
+ gem 'api-scheme', require: 'api/scheme'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install rails-api-scheme
27
+
28
+ ## Usage
29
+
30
+ Do:
31
+
32
+ ```ruby
33
+ require 'api/scheme'
34
+ ```
35
+
36
+ before usage. Then add the similar the following scheme
37
+ into the rails action controller:
38
+
39
+ ```ruby
40
+ class UsersController < ApplicationController
41
+ include Api::Scheme
42
+
43
+ error_map %W(ActionController::ParameterMissing) => 400,
44
+ %W(ActiveRecord::RecordNotFound) => 404,
45
+ %W(ActiveRecord::RecordInvalid) => 422..0,
46
+ %W(ActiveRecord::RecordNotUnique) => 422..1
47
+
48
+ param_map({
49
+ user: [
50
+ :first_name,
51
+ :last_name,
52
+ :email
53
+ ]
54
+ })
55
+
56
+ use_model 'User'
57
+
58
+ use_actions :index, :show, :create, :update, :destroy
59
+ end
60
+ ```
61
+
62
+ You can also use custom success, and error handlers as follows:
63
+
64
+ ```ruby
65
+ render_error_with :render_error
66
+ render_success_with :render_success
67
+
68
+ def render_error text, minor, major
69
+ render json: { text: text, code: minor }, status: major
70
+ end
71
+
72
+ def render_success data, status
73
+ render json: data, status
74
+ end
75
+ ```
76
+
77
+ Or even define the action method yourself instead of using the defaults, thus just skip `use_actions` call,
78
+ and declare required methods excplicitly in the controller.
79
+
80
+ ```ruby
81
+ def create
82
+ @user = User.create!(permitted_params)
83
+ end
84
+ ```
85
+
86
+ ## Development
87
+
88
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
89
+
90
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
91
+
92
+ ## Contributing
93
+
94
+ Bug reports and pull requests are welcome on GitHub at https://github.com/majioa/rails-api-scheme.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "api/scheme"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "api-scheme"
8
+ spec.version = Api::Scheme::VERSION
9
+ spec.authors = ["Malo Skrylevo"]
10
+ spec.email = ["majioa@yandex.ru"]
11
+
12
+ spec.summary = %q{API Scheme for Rails Action Controller}
13
+ spec.description = %q{Provides simple error handling and param processing scheme
14
+ to make API and other actions for Rail Action Controller. This version
15
+ obsoletes \`rails-api-scheme` gem}
16
+ spec.homepage = "https://github.com/majioa/rails-api-scheme"
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.15"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rails/api/scheme"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,235 @@
1
+ require "api/scheme/version"
2
+ require "api/scheme/action"
3
+
4
+ module Api::Scheme
5
+ def self.included klass
6
+ # NOTE put default render handlers to the module to use from as if is the ancestor
7
+ self.instance_variable_set(:@success_proc, :default_success_proc)
8
+ self.instance_variable_set(:@error_proc, :default_error_proc)
9
+
10
+ klass.class_eval %Q"
11
+ class << self
12
+ def error_map map
13
+ @error_map ||= map
14
+ end
15
+
16
+ def model_error_map map
17
+ @model_error_map ||= map
18
+ end
19
+
20
+ def param_map map
21
+ @param_map ||= map
22
+ end
23
+
24
+ def render_success_with method = nil, &prc
25
+ @success_proc ||= method || prc
26
+ end
27
+
28
+ def render_error_with method = nil, &prc
29
+ @error_proc ||= method || prc
30
+ end
31
+
32
+ def use_model name
33
+ @model_name = name
34
+ end
35
+
36
+ def use_actions *list
37
+ list.each do |action|
38
+ Api::Scheme::Action.define_for(self, action)
39
+ end
40
+
41
+ Api::Scheme::Action::SCHEME.each do |method_name, cases|
42
+ before = [ cases[:before] ].flatten.compact & list
43
+
44
+ if before.present?
45
+ Api::Scheme::Action.define_for(self, method_name)
46
+ self.before_action(method_name, only: before)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ "
52
+
53
+ klass.rescue_from StandardError, with: :render_default_error
54
+ end
55
+
56
+ def _getter var
57
+ self.class.ancestors.reduce(nil) { |value, klass| value || klass.instance_variable_get(var) }
58
+ end
59
+
60
+ def error_map
61
+ error_map = _getter(:@error_map)
62
+
63
+ error_map ||= map
64
+ return error_map if _getter(:@error_map_parsed)
65
+
66
+ error_map = parse_error_map
67
+ self.class.instance_variable_set(:@error_map_parsed, true)
68
+ self.class.instance_variable_set(:@error_map, error_map)
69
+ end
70
+
71
+ def parse_error_map
72
+ error_map = _getter(:@error_map)
73
+ error_map.map do |(errors, code)|
74
+ list = errors.map do |e|
75
+ begin
76
+ e.constantize
77
+ rescue NameError
78
+ begin
79
+ "#{self.class}::#{e}".constantize
80
+ rescue NameError
81
+ begin
82
+ send(:class_eval, e.camelize)
83
+ rescue NameError
84
+ raise InvalidErrorTypeError, e
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ [ list, code ]
91
+ end.to_h
92
+ end
93
+
94
+ def code_parse code, e
95
+ json = { message: e.message, type: e.class }
96
+
97
+ case code
98
+ when Range
99
+ logger.error "#{e.class}: #{e.message}"
100
+
101
+ [ code.first, json.merge(error_code: code.last) ]
102
+ when NilClass
103
+ logger.error "#{e.class}: #{e.message}\n\t#{e.backtrace[0...50].join("\n\t")}"
104
+
105
+ [ 500, json ]
106
+ else
107
+ logger.error "#{e.class}: #{e.message}"
108
+
109
+ [ code, json ]
110
+ end
111
+ end
112
+
113
+ def get_code_of_model_error_map e
114
+ error_text = e.record.errors.messages.reduce(nil) {|s, (_, v)| s || v.join }
115
+
116
+ _getter(:@model_error_map).to_a.reverse.reduce(nil) do |code, (re, new_code)|
117
+ re =~ error_text && new_code || code
118
+ end
119
+ end
120
+
121
+ def get_code_of_error_map e
122
+ error_map.find do |errors, codes|
123
+ errors.any? { |error| e.kind_of?(error) }
124
+ end.try(:last)
125
+ end
126
+
127
+ def get_pure_code code
128
+ if code.is_a?(Range)
129
+ code.begin
130
+ else
131
+ code
132
+ end
133
+ end
134
+
135
+ def get_sub_code code
136
+ if code.is_a?(Range)
137
+ code.end
138
+ end
139
+ end
140
+
141
+ def get_code_text code
142
+ path = code.is_a?(Range) && "#{code.begin}.#{code.end}" || code.to_s
143
+
144
+ I18n.t("action_controller.#{controller_path}.errors.#{path}")
145
+ end
146
+
147
+ def render_default_success data, options = {}
148
+ success_proc = _getter(:@success_proc)
149
+
150
+ prc = success_proc.kind_of?(Proc) && success_proc || self.method(success_proc.to_s.to_sym)
151
+
152
+ prc[data, options]
153
+ end
154
+
155
+ def render_default_error e
156
+ code =
157
+ if e.to_s.split('::').last == 'Validations'
158
+ get_code_of_model_error_map(e)
159
+ else
160
+ get_code_of_error_map(e)
161
+ end
162
+
163
+ error_proc = _getter(:@error_proc)
164
+
165
+ prc = error_proc.kind_of?(Proc) && error_proc || self.method(error_proc.to_s.to_sym)
166
+ args = [get_code_text(code), get_sub_code(code), get_pure_code(code), code, e ]
167
+
168
+ prc[*args[0...prc.arity.abs]]
169
+ end
170
+
171
+ def default_success_proc data
172
+ render data: data, status: 200
173
+ end
174
+
175
+ def default_error_proc _, _, _, code, e
176
+ status, json = code_parse(code, e)
177
+
178
+ render data: json, status: status
179
+ end
180
+
181
+ def model
182
+ @model ||= _getter(:@model_name).constantize
183
+ end
184
+
185
+ def model_key
186
+ model.name.tableize.singularize
187
+ end
188
+
189
+ def models_key
190
+ model.name.tableize
191
+ end
192
+
193
+ def model_instance
194
+ self.instance_variable_get(:"@#{model_key}")
195
+ end
196
+
197
+ def model_instances
198
+ self.instance_variable_get(:"@#{models_key}")
199
+ end
200
+
201
+ def model_instance= value
202
+ self.instance_variable_set(:"@#{model_key}", value)
203
+ end
204
+
205
+ def model_instances= value
206
+ self.instance_variable_set(:"@#{models_key}", value)
207
+ end
208
+
209
+ def permitted_params_require key, map
210
+ params.require(key).permit(*map)
211
+ end
212
+
213
+ def permitted_params_permit keys
214
+ default = {}
215
+
216
+ keys.reduce(default) do |h, key|
217
+ value = /(?<name>.*)\?$/ =~ key ? params[name] : params.require(key)
218
+ value.nil? && h || h.merge( (name || key).to_sym => value )
219
+ end
220
+ end
221
+
222
+ def permitted_params
223
+ param_map = _getter(:@param_map)
224
+
225
+ if param_map.is_a?(Hash)
226
+ permitted_params_require(param_map.keys.first, param_map.values.first)
227
+ else
228
+ permitted_params_permit([ param_map ].flatten)
229
+ end
230
+ end
231
+
232
+ def validate_access_token
233
+ raise InvalidUserError if not current_user
234
+ end
235
+ end
@@ -0,0 +1,60 @@
1
+ require "api/scheme/version"
2
+
3
+ module Api::Scheme::Action
4
+ SCHEME = {
5
+ fetch_model_instances: {
6
+ before: :index
7
+ },
8
+ fetch_model_instance: {
9
+ before: %i(show update destroy)
10
+ },
11
+ init_model_instance: {
12
+ before: :create
13
+ }
14
+ }
15
+
16
+ def index
17
+ render_default_success models_key => model_instances
18
+ end
19
+
20
+ def show
21
+ render_default_success model_key => model_instance
22
+ end
23
+
24
+ def create
25
+ model_instance.save!
26
+
27
+ render_default_success({ model_key => model_instance }, status: :created)
28
+ end
29
+
30
+ def update
31
+ model_instance.update!(permitted_params)
32
+
33
+ render_default_success({ model_key => model_instance }, status: :ok)
34
+ end
35
+
36
+ def destroy
37
+ model_instance.destroy!
38
+
39
+ render_default_success({ model_key => model_instance }, status: :ok)
40
+ end
41
+
42
+ def fetch_model_instances
43
+ self.model_instances = model.all
44
+ end
45
+
46
+ def fetch_model_instance
47
+ self.model_instance = model.find(params[:id])
48
+ end
49
+
50
+ def init_model_instance
51
+ self.model_instance = model.new(permitted_params)
52
+ end
53
+
54
+ class << self
55
+ def define_for klass, name
56
+ method = self.instance_method(name)
57
+ klass.send(:define_method, name, method)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module Api
2
+ module Scheme
3
+ VERSION = "0.2"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: api-scheme
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Malo Skrylevo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: |-
56
+ Provides simple error handling and param processing scheme
57
+ to make API and other actions for Rail Action Controller. This version
58
+ obsoletes \`rails-api-scheme` gem
59
+ email:
60
+ - majioa@yandex.ru
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - ".travis.yml"
68
+ - Gemfile
69
+ - README.md
70
+ - Rakefile
71
+ - api-scheme.gemspec
72
+ - bin/console
73
+ - bin/setup
74
+ - lib/api/scheme.rb
75
+ - lib/api/scheme/action.rb
76
+ - lib/api/scheme/version.rb
77
+ homepage: https://github.com/majioa/rails-api-scheme
78
+ licenses: []
79
+ metadata:
80
+ allowed_push_host: https://rubygems.org
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.6.12
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: API Scheme for Rails Action Controller
101
+ test_files: []