reaction 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/lib/reaction/action.rb +55 -0
- data/lib/reaction/doc.rb +17 -0
- data/lib/reaction/each_validator.rb +43 -0
- data/lib/reaction/errors.rb +45 -0
- data/lib/reaction/has_docs.rb +18 -0
- data/lib/reaction/has_errors.rb +15 -0
- data/lib/reaction/has_metas.rb +23 -0
- data/lib/reaction/has_params.rb +49 -0
- data/lib/reaction/has_types.rb +28 -0
- data/lib/reaction/has_validators.rb +33 -0
- data/lib/reaction/type.rb +40 -0
- data/lib/reaction/types/raw_type.rb +16 -0
- data/lib/reaction.rb +14 -0
- data/reaction.gemspec +18 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70ffb42ccf83ba294bfb07d8887055c9a2343d2e
|
4
|
+
data.tar.gz: 44a1abae9c843a15bbe634c28ab867216b1042f4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc74f3443a4b55a2828c03ce89b971f7022320f8cbb994b5e8c099ee4ebdb48d24abd2ab8e78e9a76049d2bd257a4ec45504ba963c590238a7ddbcb6e8b9010d
|
7
|
+
data.tar.gz: be02f4c83dbf04a96762b3d009942542ff477e0c21962d8a5d5b475598939293d8db2db83a48fc72894ae26a6b2f108c12b1f3b5121dc2695111ea3c81b8922f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Jonathan Calhoun
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Reaction
|
2
|
+
class Action
|
3
|
+
include HasErrors
|
4
|
+
include HasParams
|
5
|
+
|
6
|
+
# meta isn't used by default, but it is supported for use cases
|
7
|
+
# where you need to attach metadata to parameters, such as
|
8
|
+
# who or what set the attribute. For example, Paid currently
|
9
|
+
# has a flow like:
|
10
|
+
#
|
11
|
+
# action = Action.new(request.params, via: :request)
|
12
|
+
# action.set_params(account: api_key.account, via: :api_key)
|
13
|
+
#
|
14
|
+
# Which allows us to use a validator to define who can set
|
15
|
+
# various attributes. Specifically, we limit certain attributes
|
16
|
+
# from being set by the request so end users can't set params
|
17
|
+
# that we don't expect them to set.
|
18
|
+
#
|
19
|
+
def initialize(params = {}, meta = {})
|
20
|
+
set_params(params, meta)
|
21
|
+
end
|
22
|
+
|
23
|
+
def invoke
|
24
|
+
validate!
|
25
|
+
ret = perform
|
26
|
+
ensure
|
27
|
+
cleanup
|
28
|
+
end
|
29
|
+
|
30
|
+
def perform
|
31
|
+
# Implement this for your action
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate!
|
35
|
+
self.class.types.each do |name, type|
|
36
|
+
type.validate_each(self, name, raw_param(name))
|
37
|
+
end
|
38
|
+
self.class.validators.each do |name, validator|
|
39
|
+
validator.validate_each(self, name, raw_param(name))
|
40
|
+
end
|
41
|
+
if errors.any?
|
42
|
+
raise ArgumentError.new("Validations failed: #{errors.full_messages.join(',')}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def cleanup
|
47
|
+
self.class.types.each do |name, type|
|
48
|
+
type.cleanup
|
49
|
+
end
|
50
|
+
self.class.validators.each do |name, validator|
|
51
|
+
validator.cleanup
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/reaction/doc.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This class serves no real purpose in this gem, but is here so that
|
2
|
+
# developers can use it to create more dynamic documentation by
|
3
|
+
# documenting attributes used for actions directly in the actions,
|
4
|
+
# and then access these when generating their actual documenation.
|
5
|
+
#
|
6
|
+
module Reaction
|
7
|
+
class Doc
|
8
|
+
attr_reader :name, :message, :options
|
9
|
+
|
10
|
+
def initialize(name, message, options = {})
|
11
|
+
@name = name.to_sym
|
12
|
+
@message = message
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Reaction
|
2
|
+
class EachValidator
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
# If you need to validate a parameter outside of the basic
|
10
|
+
# type then validators are your best bet. They are generally
|
11
|
+
# very similar to Rails' EachValidator. You provide a
|
12
|
+
# validate_each method that takes in a record, attribute, and
|
13
|
+
# value and then if the attribute isn't valid adds an error
|
14
|
+
# to record.errors. You can also access the data provided
|
15
|
+
# for the validator via the +options+ method.
|
16
|
+
#
|
17
|
+
# Example validate_each method for a RequiredValidator which
|
18
|
+
# would be used like:
|
19
|
+
#
|
20
|
+
# param :cat, required: true
|
21
|
+
# param :dog, required: false
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# def validate_each(record, attribute, value)
|
25
|
+
# if options && value.nil?
|
26
|
+
# record.errors.add(attribute, 'is required.')
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
def validate_each(record, attribute, value)
|
31
|
+
raise NotImplementedError.new('Subclasses must implement a validate_each(record, attribute, value) method')
|
32
|
+
end
|
33
|
+
|
34
|
+
# Cleanup is provided in case you need to create files that
|
35
|
+
# require cleanup after the action has been performed. For
|
36
|
+
# example, Paid creates Tempfiles with some types and uses
|
37
|
+
# the cleanup phase to ensure these files get closed up
|
38
|
+
# properly.
|
39
|
+
#
|
40
|
+
def cleanup
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Reaction
|
2
|
+
class Errors
|
3
|
+
|
4
|
+
attr_accessor :messages
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@messages = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def clear
|
11
|
+
messages.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
def include?(key)
|
15
|
+
!messages[key].nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(key)
|
19
|
+
messages[key]
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(key, message)
|
23
|
+
messages[key] ||= []
|
24
|
+
messages[key] << message
|
25
|
+
end
|
26
|
+
|
27
|
+
def any?
|
28
|
+
messages.any?
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
messages
|
33
|
+
end
|
34
|
+
|
35
|
+
def full_messages
|
36
|
+
ret = []
|
37
|
+
messages.each do |key, values|
|
38
|
+
values.each do |value|
|
39
|
+
ret << "#{key}: #{value}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
ret
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Reaction
|
2
|
+
module HasDocs
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def doc(name, message, options = {})
|
9
|
+
docs[name.to_sym] = Doc.new(name, message, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def docs
|
13
|
+
@docs ||= {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Reaction
|
2
|
+
module HasMetas
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def meta(name)
|
11
|
+
metas[name.to_sym]
|
12
|
+
end
|
13
|
+
|
14
|
+
def metas
|
15
|
+
@metas ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_meta(name, meta)
|
19
|
+
metas[name.to_sym] = meta
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Reaction
|
2
|
+
module HasParams
|
3
|
+
def self.included(base)
|
4
|
+
base.include HasDocs
|
5
|
+
base.include HasMetas
|
6
|
+
base.include HasTypes
|
7
|
+
base.include HasValidators
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def param(name, options = {})
|
13
|
+
set_type(name, options.delete(:type))
|
14
|
+
set_validators(name, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def param(name)
|
19
|
+
typed_params[name.to_sym] ||= begin
|
20
|
+
type = self.class.types[name.to_sym]
|
21
|
+
type ? type.convert(raw_param(name)) : raw_param(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def params
|
26
|
+
@params ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def typed_params
|
30
|
+
@typed_params ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_param(name, value, meta = {})
|
34
|
+
set_meta(name, meta)
|
35
|
+
params[name.to_sym] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_params(params = {}, meta = {})
|
39
|
+
params.each do |name, value|
|
40
|
+
set_param(name, value, meta)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def raw_param(name)
|
45
|
+
params[name.to_sym]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Reaction
|
2
|
+
module HasTypes
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def types
|
9
|
+
@types ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_type(name, type)
|
13
|
+
type ||= Type
|
14
|
+
klass = class_for_type(type)
|
15
|
+
types[name.to_sym] = klass.new(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def class_for_type(type)
|
19
|
+
return type if type.is_a?(Class)
|
20
|
+
name = type.to_s.split('_').map(&:capitalize).join
|
21
|
+
const_get("#{name}Type")
|
22
|
+
rescue NameError
|
23
|
+
raise ArgumentError.new("Unknown param type: #{type}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Reaction
|
2
|
+
module HasValidators
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def validators
|
9
|
+
@validators ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_validator(name, validator, options = {})
|
13
|
+
klass = class_for_validator(validator)
|
14
|
+
validators[name.to_sym] = klass.new(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_validators(name, validators = {})
|
18
|
+
validators.each do |validator, options|
|
19
|
+
set_validator(name, validator, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def class_for_validator(validator)
|
24
|
+
return validator if validator.is_a?(Class)
|
25
|
+
name = validator.to_s.split('_').map(&:capitalize).join
|
26
|
+
const_get("#{name}Validator")
|
27
|
+
rescue NameError
|
28
|
+
raise ArgumentError.new("Unknown param validator: #{validator}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Reaction
|
2
|
+
class Type
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name.to_sym
|
7
|
+
end
|
8
|
+
|
9
|
+
# If you need to validate based on type you can. These
|
10
|
+
# work identically to the validators, except type validations
|
11
|
+
# are always called before other validators, and the +options+
|
12
|
+
# hash isn't available. If you have a particularly good
|
13
|
+
# use case for passing in options to a Type reach out; It
|
14
|
+
# should be pretty easy to add in, we just haven't had a
|
15
|
+
# need for it yet.
|
16
|
+
#
|
17
|
+
def validate_each(record, attribute, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Convert is used to transform a value into whatever
|
21
|
+
# format you expect it to be. For example, you might
|
22
|
+
# have a convert method that casts a string into an
|
23
|
+
# integer, or one that takes in various date formats
|
24
|
+
# and converts them to a DateTime prior to the param
|
25
|
+
# being used in the action.
|
26
|
+
#
|
27
|
+
def convert(value)
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
# Cleanup is provided in case you need to create files that
|
32
|
+
# require cleanup after the action has been performed. For
|
33
|
+
# example, Paid creates Tempfiles with some types and uses
|
34
|
+
# the cleanup phase to ensure these files get closed up
|
35
|
+
# properly.
|
36
|
+
#
|
37
|
+
def cleanup
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This is the default type for any param. It doesn't do any
|
2
|
+
# validations and simply returns the value as provided.
|
3
|
+
#
|
4
|
+
module Reaction
|
5
|
+
class RawType < Type
|
6
|
+
def validate_each(record, attribute, value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def convert(value)
|
10
|
+
value
|
11
|
+
end
|
12
|
+
|
13
|
+
def cleanup
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/reaction.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Reaction
|
2
|
+
autoload :Action, 'reaction/action'
|
3
|
+
autoload :Doc, 'reaction/doc'
|
4
|
+
autoload :EachValidator, 'reaction/each_validator'
|
5
|
+
autoload :Errors, 'reaction/errors'
|
6
|
+
autoload :HasDocs, 'reaction/has_docs'
|
7
|
+
autoload :HasErrors, 'reaction/has_errors'
|
8
|
+
autoload :HasMetas, 'reaction/has_metas'
|
9
|
+
autoload :HasParams, 'reaction/has_params'
|
10
|
+
autoload :HasTypes, 'reaction/has_types'
|
11
|
+
autoload :HasValidators, 'reaction/has_validators'
|
12
|
+
autoload :RawType, 'reaction/types/raw_type'
|
13
|
+
autoload :Type, 'reaction/type'
|
14
|
+
end
|
data/reaction.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'reaction'
|
7
|
+
gem.version = '0.0.0'
|
8
|
+
gem.authors = ["Jonathan Calhoun"]
|
9
|
+
gem.email = ["joncalhoun@gmail.com"]
|
10
|
+
gem.description = 'Reaction is a library to help build reusable actions for Rails controllers.'
|
11
|
+
gem.summary = 'Reaction is a library to help build reusable actions for Rails controllers.'
|
12
|
+
gem.homepage = 'https://github.com/joncalhoun/reaction'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reaction
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Calhoun
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Reaction is a library to help build reusable actions for Rails controllers.
|
14
|
+
email:
|
15
|
+
- joncalhoun@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- Gemfile
|
22
|
+
- LICENSE.txt
|
23
|
+
- lib/reaction.rb
|
24
|
+
- lib/reaction/action.rb
|
25
|
+
- lib/reaction/doc.rb
|
26
|
+
- lib/reaction/each_validator.rb
|
27
|
+
- lib/reaction/errors.rb
|
28
|
+
- lib/reaction/has_docs.rb
|
29
|
+
- lib/reaction/has_errors.rb
|
30
|
+
- lib/reaction/has_metas.rb
|
31
|
+
- lib/reaction/has_params.rb
|
32
|
+
- lib/reaction/has_types.rb
|
33
|
+
- lib/reaction/has_validators.rb
|
34
|
+
- lib/reaction/type.rb
|
35
|
+
- lib/reaction/types/raw_type.rb
|
36
|
+
- reaction.gemspec
|
37
|
+
homepage: https://github.com/joncalhoun/reaction
|
38
|
+
licenses: []
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.4.5
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Reaction is a library to help build reusable actions for Rails controllers.
|
60
|
+
test_files: []
|