api-scheme 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []