grape_on_rails 1.0.1

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,24 @@
1
+ module GrapeOnRails
2
+ module Authenticator
3
+ include Support
4
+
5
+ attr_accessor :current_user
6
+
7
+ def authenticate!
8
+ @current_user = GoR.models.user_token.constantize
9
+ .find_token!(token_on_header)&.public_send(GoR.models.user.downcase)
10
+ raise APIError::Unauthenticated unless current_user
11
+ end
12
+
13
+ def refresh_token!
14
+ token = GoR.models.user_token.constantize.find_by refresh_token: token_on_header
15
+ token ? token.renew! : raise(APIError::Unauthenticated)
16
+ token
17
+ end
18
+
19
+ def token_on_header
20
+ auth_header = request.headers[GoR.access_token_header]
21
+ auth_header&.scan(/^#{GoR.access_token_value_prefix} (.+)$/i)&.flatten&.[]0
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ module GrapeOnRails
2
+ module ErrorFormatter
3
+ def render_error error_name, message, http_status
4
+ render json: {
5
+ GoR.error_code_key => GoR.errors.public_send(error_name).code,
6
+ GoR.error_message_key => message
7
+ }, status: http_status
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ module GrapeOnRails
2
+ module Integrations
3
+ class Railtie < Rails::Railtie
4
+ def auto_reload_in_development
5
+ return unless ::Rails.env.development? && ::Rails::VERSION::MAJOR >= 4
6
+ reload_on_api_only_mode
7
+ reload_on_normal_mode
8
+ end
9
+
10
+ def reload_on_api_only_mode
11
+ ActionController::Base.class_eval(&reload_on_each_request)
12
+ end
13
+
14
+ def reload_on_normal_mode
15
+ ActionController::API.class_eval(&reload_on_each_request)
16
+ end
17
+
18
+ def reload_on_each_request
19
+ proc do
20
+ prepend_before_action{::GrapeOnRails.reload}
21
+ end
22
+ end
23
+
24
+ config.before_configuration{GrapeOnRails.load}
25
+
26
+ config.after_initialize do
27
+ ActiveSupport.on_load(:active_record) do
28
+ extend GrapeOnRails::Models
29
+ end
30
+ ActiveSupport.on_load(:action_controller) do
31
+ include GrapeOnRails::API
32
+ include GrapeOnRails::Authenticator
33
+ end
34
+ auto_reload_in_development
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ require "grape_on_rails/attributes"
2
+
3
+ module GrapeOnRails
4
+ module Macros
5
+ include GrapeOnRails::Attributes
6
+
7
+ def requires *attrs
8
+ options = attrs.extract_options!
9
+ raise APIError::ValidationError if attrs.any?{|a| params[a].nil?}
10
+ options.each{|k, v| verify k, attrs, v}
11
+ declared_attrs attrs
12
+ end
13
+
14
+ def optional *attrs
15
+ options = attrs.extract_options!
16
+ return if attrs.any?{|a| params[a].nil?}
17
+ options.each{|k, v| verify k, attrs, v}
18
+ declared_attrs attrs
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ require "grape_on_rails/models/user_actor"
2
+ require "grape_on_rails/models/user_token_actor"
3
+
4
+ module GrapeOnRails
5
+ module Models
6
+ DEFAULT_USER_ACTOR = "User"
7
+ DEFAULT_USER_TOKEN_ACTOR = "UserToken"
8
+
9
+ def acts_as model
10
+ include GrapeOnRails::Models.const_get "#{model}_actor".classify
11
+ missing_columns = check_missing_columns
12
+ raise "You need to add columns: #{missing_columns} to #{name} model" unless missing_columns.empty?
13
+ end
14
+
15
+ class << self
16
+ def set_default_model_actors config
17
+ config.models = StructuralHash.new.from_hash default_model_actors unless config.models
18
+ end
19
+
20
+ def default_model_actors
21
+ {
22
+ user: DEFAULT_USER_ACTOR,
23
+ user_token: DEFAULT_USER_TOKEN_ACTOR
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ module GrapeOnRails
2
+ module Models
3
+ module UserActor
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_secure_password
8
+ end
9
+
10
+ class_methods do
11
+ def authenticate! email, password
12
+ find_by(email: email).tap do |user|
13
+ raise APIError::WrongEmailPassword unless user&.authenticate password
14
+ end
15
+ end
16
+
17
+ def check_missing_columns
18
+ %w(email password_digest) - column_names
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module GrapeOnRails
2
+ module Models
3
+ module UserTokenActor
4
+ extend ActiveSupport::Concern
5
+ include Support
6
+ include SecuredGenerator
7
+
8
+ included do
9
+ has_secure_token
10
+ has_secure_token :refresh_token
11
+
12
+ def renew! remember = false
13
+ update! expires_at: GoR.token_configs.public_send(remember ? :expires_in : :short_expires_in).second.from_now,
14
+ token: unique_random(:token), refresh_token: unique_random(:refresh_token)
15
+ self
16
+ end
17
+
18
+ def expired?
19
+ expires_at <= Time.zone.now
20
+ end
21
+
22
+ def expires!
23
+ update_attributes! expired_at: Time.zone.now
24
+ end
25
+ end
26
+
27
+ class_methods do
28
+ def generate! user
29
+ token = find_or_initialize_by GoR.models.user.downcase => user
30
+ token.renew!
31
+ end
32
+
33
+ def find_token! token
34
+ find_by(token: token).tap do |user_token|
35
+ raise APIError::TokenExpired if user_token&.expired?
36
+ end
37
+ end
38
+
39
+ def check_missing_columns
40
+ %w(token refresh_token expires_at) - column_names
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module GrapeOnRails
2
+ module SecuredGenerator
3
+ def unique_random attr
4
+ str_len = GoR.token_configs
5
+ .public_send(attr)
6
+ .public_send(:secure_length).to_i / 2
7
+ loop do
8
+ str = SecureRandom.hex str_len
9
+ break str unless self.class.exists?(attr => str)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ require "yaml"
2
+ require "erb"
3
+
4
+ module GrapeOnRails
5
+ module Sources
6
+ class YAMLSource
7
+ attr_accessor :path
8
+
9
+ def initialize path
10
+ @path = path
11
+ end
12
+
13
+ def load
14
+ @path && File.exist?(@path.to_s) ? YAML.safe_load(ERB.new(IO.read(@path.to_s)).result) : {}
15
+ rescue Psych::SyntaxError => e
16
+ raise "YAML syntax error occurred while parsing #{@path}. " \
17
+ "Error: #{e.message}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ require "ostruct"
2
+
3
+ class GrapeOnRails::StructuralHash < OpenStruct
4
+ include Enumerable
5
+
6
+ def from_hash hash
7
+ marshal_load __convert(hash)
8
+ end
9
+
10
+ def to_hash
11
+ {}.tap do |result|
12
+ marshal_dump.each{|k, v| result[k] = hash_value v}
13
+ end
14
+ end
15
+
16
+ def each *args, &block
17
+ marshal_dump.each(*args, &block)
18
+ end
19
+
20
+ private
21
+ # Recursively converts Hashes to Options (including Hashes inside Arrays)
22
+ # rubocop:disable PerceivedComplexity
23
+ def __convert hash #:nodoc:
24
+ instance = self.class.new
25
+ hash.each do |k, v|
26
+ k = k.to_s if !k.respond_to?(:to_sym) && k.respond_to?(:to_s)
27
+ instance.new_ostruct_member k
28
+ if v.is_a?(Hash)
29
+ v = v["type"] == "hash" ? v["contents"] : __convert(v)
30
+ elsif v.is_a?(Array)
31
+ v = v.map{|e| e.instance_of?(Hash) ? __convert(e) : e}
32
+ end
33
+ instance.send "#{k}=".to_sym, v
34
+ end
35
+ instance
36
+ end
37
+ # rubocop:enable PerceivedComplexity
38
+
39
+ def hash_value value
40
+ if value.instance_of? GrapeOnRails::StructuralHash
41
+ value.to_hash
42
+ elsif v.instance_of? Array
43
+ descend_array(value)
44
+ else
45
+ value
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ require "i18n"
2
+
3
+ module GrapeOnRails
4
+ module Support
5
+ def gor_translate error
6
+ GoR.errors.public_send(error).public_send(I18n.locale)
7
+ end
8
+ alias_method :gor_t, :gor_translate
9
+
10
+ def class_name instance
11
+ instance.class.name.demodulize.underscore
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module GrapeOnRails
2
+ module Types
3
+ module Boolean
4
+ def to_b
5
+ case self
6
+ when "true"
7
+ true
8
+ when "false"
9
+ false
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class TrueClass
17
+ include GrapeOnRails::Types::Boolean
18
+ end
19
+ class FalseClass
20
+ include GrapeOnRails::Types::Boolean
21
+ end
@@ -0,0 +1,37 @@
1
+ require "grape_on_rails/macros"
2
+
3
+ module GrapeOnRails
4
+ module Validator
5
+ extend ActiveSupport::Concern
6
+
7
+ include GrapeOnRails::Macros
8
+
9
+ ALLOW_ACTIONS = /(create|update|destroy|index|show)/
10
+
11
+ class_methods do
12
+ def validate_actions *actions
13
+ actions.each do |action|
14
+ meta_method = "before_action :validate_#{action}, only: :#{action}"
15
+ class_eval meta_method
16
+ end
17
+ end
18
+
19
+ def method_missing method, *args, &block
20
+ action = extract_action method
21
+ super unless action&.match? ALLOW_ACTIONS
22
+ define_method "hook_#{action}", block
23
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
24
+ before_action :hook_#{action}, only: :#{action}
25
+ RUBY
26
+ end
27
+
28
+ def respond_to_missing? method_name, include_private = false
29
+ extract_action(method_name)&.match?(ALLOW_ACTIONS) || super(method_name, include_private)
30
+ end
31
+
32
+ def extract_action method_name
33
+ method_name[/.+?(?=_params)/]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module GrapeOnRails
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe GrapeOnRails do
4
+ it "has a version number" do
5
+ expect(GrapeOnRails::VERSION).not_to be nil
6
+ end
7
+
8
+ it "does something useful" do
9
+ expect(false).to eq(true)
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require "bundler/setup"
2
+ require "grape_on_rails"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape_on_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - topcbl
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '4.1'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '6'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '4.1'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '6'
53
+ - !ruby/object:Gem::Dependency
54
+ name: activesupport
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '4.1'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '6'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '4.1'
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '6'
73
+ - !ruby/object:Gem::Dependency
74
+ name: railties
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '4.1'
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: '6'
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '4.1'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '6'
93
+ - !ruby/object:Gem::Dependency
94
+ name: bundler
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.15'
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '1.15'
107
+ - !ruby/object:Gem::Dependency
108
+ name: rake
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '10.0'
114
+ type: :development
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '10.0'
121
+ - !ruby/object:Gem::Dependency
122
+ name: rspec
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '3.0'
128
+ type: :development
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ description: Bring Grape DSL to Rails-api. Make it easier for writing api.
136
+ email:
137
+ - topcbl@gmail.com
138
+ executables: []
139
+ extensions: []
140
+ extra_rdoc_files: []
141
+ files:
142
+ - ".gitignore"
143
+ - ".rspec"
144
+ - ".rubocop.yml"
145
+ - ".rubocop_disabled.yml"
146
+ - ".rubocop_enabled.yml"
147
+ - ".travis.yml"
148
+ - CODE_OF_CONDUCT.md
149
+ - Gemfile
150
+ - LICENSE.txt
151
+ - README.md
152
+ - Rakefile
153
+ - bin/console
154
+ - bin/setup
155
+ - grape_on_rails.gemspec
156
+ - lib/grape_on_rails.rb
157
+ - lib/grape_on_rails/api.rb
158
+ - lib/grape_on_rails/api_error.rb
159
+ - lib/grape_on_rails/attributes.rb
160
+ - lib/grape_on_rails/authenticator.rb
161
+ - lib/grape_on_rails/error_formatter.rb
162
+ - lib/grape_on_rails/integrations/railtie.rb
163
+ - lib/grape_on_rails/macros.rb
164
+ - lib/grape_on_rails/models.rb
165
+ - lib/grape_on_rails/models/user_actor.rb
166
+ - lib/grape_on_rails/models/user_token_actor.rb
167
+ - lib/grape_on_rails/secured_generator.rb
168
+ - lib/grape_on_rails/sources/yaml_source.rb
169
+ - lib/grape_on_rails/structural_hash.rb
170
+ - lib/grape_on_rails/support.rb
171
+ - lib/grape_on_rails/types/boolean.rb
172
+ - lib/grape_on_rails/validator.rb
173
+ - lib/grape_on_rails/version.rb
174
+ - spec/grape_on_rails_spec.rb
175
+ - spec/spec_helper.rb
176
+ homepage: https://github.com/chau-bao-long/grape-on-rails
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.6.11
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Bring Grape DSL to Rails-api. Make it easier for writing api.
200
+ test_files: []