ciesta 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.hound.yml +2 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +1 -1
- data/README.md +98 -39
- data/Rakefile +4 -2
- data/bin/console +4 -3
- data/ciesta.gemspec +21 -19
- data/lib/ciesta.rb +13 -8
- data/lib/ciesta/delegator.rb +22 -0
- data/lib/ciesta/errors.rb +8 -1
- data/lib/ciesta/field.rb +70 -19
- data/lib/ciesta/field_list.rb +84 -0
- data/lib/ciesta/form.rb +101 -68
- data/lib/ciesta/syncer.rb +34 -0
- data/lib/ciesta/types.rb +7 -4
- data/lib/ciesta/validator.rb +32 -14
- data/lib/ciesta/version.rb +3 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa6bcec524ba6b6e11a71bd2fa2852c805f4a2359375a6aeba89fb627a9b8235
|
4
|
+
data.tar.gz: ee3c19951c78d75b8adc45296277b3610e8fd7ca8c93ea4a99c60bfa2d6d0613
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8780acfc8511f0e275a67643ea470e4150f7c16991b841c17353b39788a892ab3d4716bc56cbfd759cefe81555ba351141e7ce13b5427d0b70a67259824f0adf
|
7
|
+
data.tar.gz: 7bc5a4bbeb0549e329f11a6f810b2369debf9955d0fa276cd92ac40cf264f5685147362676d344aacd613e4bfa833cc7f5b2de518125f02367ff7e0bf1099861
|
data/.hound.yml
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
# Ciesta
|
2
2
|
|
3
|
-
[](https://coveralls.io/github/nulldef/ciesta?branch=master)
|
4
3
|
[](https://travis-ci.org/nulldef/ciesta)
|
4
|
+
[](https://coveralls.io/github/nulldef/ciesta?branch=master)
|
5
|
+
[](https://badge.fury.io/rb/ciesta)
|
5
6
|
|
6
7
|
Create simple form objects
|
7
8
|
|
8
9
|
Supported Ruby 2.2.0+
|
9
10
|
|
11
|
+
You should keep it in mind that here uses [dry-validation](https://github.com/dry-rb/dry-validation) and [dry-types](https://github.com/dry-rb/dry-types) for validation and typification respectively.
|
12
|
+
|
10
13
|
- [Installation](#installation)
|
11
14
|
- [Usage](#usage)
|
12
|
-
- [Basic
|
13
|
-
- [
|
14
|
-
- [
|
15
|
-
- [
|
15
|
+
- [Basic case](#basic-case)
|
16
|
+
- [Syncing](#syncing)
|
17
|
+
- [Validation](#validation)
|
18
|
+
- [Advanced declaring fields](#advanced-declaring-fields)
|
19
|
+
- [Types](#types)
|
20
|
+
- [Default value](#default-value)
|
21
|
+
- [Mass updating values](#mass-updating-values)
|
16
22
|
- [Contributing](#contributing)
|
17
23
|
- [License](#license)
|
18
24
|
|
@@ -21,7 +27,7 @@ Supported Ruby 2.2.0+
|
|
21
27
|
Add this line to your application's Gemfile:
|
22
28
|
|
23
29
|
```ruby
|
24
|
-
gem
|
30
|
+
gem "ciesta"
|
25
31
|
```
|
26
32
|
|
27
33
|
And then execute:
|
@@ -30,17 +36,53 @@ And then execute:
|
|
30
36
|
|
31
37
|
Or install it yourself as:
|
32
38
|
|
33
|
-
$ gem install
|
39
|
+
$ gem install ciesta
|
34
40
|
|
35
|
-
## Usage
|
36
41
|
|
37
|
-
|
42
|
+
## Usage
|
38
43
|
|
39
|
-
|
44
|
+
### Basic case
|
45
|
+
Just imageine that we have a user object with `name` and `age` attributes:
|
40
46
|
|
41
47
|
```ruby
|
42
48
|
User = Struct.new(:name, :age)
|
43
49
|
|
50
|
+
user = User.new(nil, nil)
|
51
|
+
```
|
52
|
+
And we need to save new values and update and we will use simple form object:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class Form < Ciesta::Form
|
56
|
+
field :name
|
57
|
+
field :age
|
58
|
+
end
|
59
|
+
|
60
|
+
form = Form.new(user)
|
61
|
+
```
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
form.name = "John"
|
65
|
+
form.age = 33
|
66
|
+
form.sync!
|
67
|
+
|
68
|
+
user.name # => "John"
|
69
|
+
user.age # => 33
|
70
|
+
```
|
71
|
+
|
72
|
+
### Syncing
|
73
|
+
You can pass a block to sync method to do some stuff with object after syncing.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
form.sync! do |user|
|
77
|
+
user.make_happy!
|
78
|
+
end
|
79
|
+
```
|
80
|
+
Both methods `sync` and `sync!` are provides this DSL.
|
81
|
+
|
82
|
+
### Validation
|
83
|
+
There we want to validate incoming values. We should use `validate` method:
|
84
|
+
|
85
|
+
```ruby
|
44
86
|
class Form < Ciesta::Form
|
45
87
|
field :name
|
46
88
|
field :age
|
@@ -50,58 +92,75 @@ class Form < Ciesta::Form
|
|
50
92
|
required(:age).filled(gt?: 18)
|
51
93
|
end
|
52
94
|
end
|
95
|
+
|
96
|
+
form = Form.new(user)
|
53
97
|
```
|
54
98
|
|
55
|
-
|
99
|
+
Trying to sync with invalid form will raise `Ciesta::FormNotValid` error.
|
56
100
|
|
57
101
|
```ruby
|
58
|
-
|
59
|
-
form
|
60
|
-
form.
|
61
|
-
form.
|
102
|
+
form.age = 15
|
103
|
+
form.valid? # => false
|
104
|
+
form.sync! # => raises Ciesta::FormNotValid
|
105
|
+
form.errors # => { age: ["must be greater than 18"] }
|
106
|
+
...
|
107
|
+
form.age = 42
|
108
|
+
form.sync! # => true
|
109
|
+
|
110
|
+
user.age # => 42
|
62
111
|
```
|
63
112
|
|
64
|
-
###
|
113
|
+
### Advanced declaring fields
|
65
114
|
|
66
|
-
|
115
|
+
#### Types
|
116
|
+
You can define a type of the field using `Ciesta::Types` namespace.
|
67
117
|
|
68
118
|
```ruby
|
69
|
-
|
70
|
-
field :bar
|
71
|
-
end
|
72
|
-
|
73
|
-
form.assign!(foo: 1, bar: 2) # => raises Ciesta::FieldNotDefined
|
119
|
+
field :age, type: Ciesta::Types::Coercible::Int
|
74
120
|
...
|
75
|
-
form.
|
76
|
-
form.
|
121
|
+
form.age = "42"
|
122
|
+
form.age # => 42
|
77
123
|
```
|
124
|
+
Default type is `Ciesta::Types::Any`.
|
78
125
|
|
79
|
-
|
126
|
+
#### Default value
|
127
|
+
If your attribute wasn't set yet but value already is in use, you can set `default` option to avoid some kind of exceptions.
|
80
128
|
|
81
|
-
|
129
|
+
```ruby
|
130
|
+
field :age, default: 42
|
131
|
+
...
|
132
|
+
form.age # => 42
|
133
|
+
```
|
82
134
|
|
83
|
-
|
135
|
+
Default value can also be a `Proc`, wich will executed in object context.
|
84
136
|
|
85
137
|
```ruby
|
86
|
-
|
87
|
-
|
88
|
-
|
138
|
+
class User
|
139
|
+
def default_age
|
140
|
+
42
|
141
|
+
end
|
142
|
+
end
|
89
143
|
```
|
90
144
|
|
91
|
-
|
145
|
+
```ruby
|
146
|
+
field :age, default: -> { default_age }
|
147
|
+
...
|
148
|
+
form.age # => 42
|
149
|
+
```
|
92
150
|
|
93
|
-
|
151
|
+
## Mass updating values
|
152
|
+
There are exists two methods for mass update form fields: `assign` and `assign!`.
|
94
153
|
|
95
154
|
```ruby
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
#
|
101
|
-
form.assign(foo: 1) # => true
|
102
|
-
form.foo # => "1"
|
155
|
+
form.assign!(name: "Neo", age: 30)
|
156
|
+
form.sync!
|
157
|
+
...
|
158
|
+
user.name # => "Neo"
|
159
|
+
user.age # => 30
|
103
160
|
```
|
104
161
|
|
162
|
+
`assign!` method will raise `Ciesta::FieldNotDefined` error if one of passed attribute is not exists in the form.
|
163
|
+
|
105
164
|
## Contributing
|
106
165
|
|
107
166
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/nulldef/ciesta](https://github.com/nulldef/ciesta).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/ciesta.gemspec
CHANGED
@@ -1,41 +1,43 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
6
|
+
require "ciesta/version"
|
5
7
|
|
6
8
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
9
|
+
spec.name = "ciesta"
|
8
10
|
spec.version = Ciesta::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
+
spec.authors = ["Alexey"]
|
12
|
+
spec.email = ["alex.coder1@gmail.com"]
|
11
13
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
14
|
+
spec.summary = "Create form objects easy"
|
15
|
+
spec.description = "Gem for creating and using form object"
|
16
|
+
spec.homepage = "https://github.com/nulldef/ciesta"
|
17
|
+
spec.license = "MIT"
|
16
18
|
|
17
19
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
20
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
21
|
if spec.respond_to?(:metadata)
|
20
22
|
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
21
23
|
else
|
22
|
-
raise
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
25
|
end
|
24
26
|
|
25
27
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
28
|
f.match(%r{^(test|spec|features)/})
|
27
29
|
end
|
28
|
-
spec.bindir =
|
30
|
+
spec.bindir = "exe"
|
29
31
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
-
spec.require_paths = [
|
32
|
+
spec.require_paths = ["lib"]
|
31
33
|
spec.required_ruby_version = ">= 2.0"
|
32
34
|
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
35
|
+
spec.add_dependency "dry-types", "~> 0.12.1"
|
36
|
+
spec.add_dependency "dry-validation", "~> 0.11.1"
|
35
37
|
|
36
|
-
spec.add_development_dependency
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
39
|
+
spec.add_development_dependency "coveralls", "~> 0.8"
|
40
|
+
spec.add_development_dependency "pry", "~> 0.11"
|
41
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
42
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
41
43
|
end
|
data/lib/ciesta.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-types"
|
4
|
+
require "dry-validation"
|
5
|
+
require "ciesta/delegator"
|
6
|
+
require "ciesta/version"
|
7
|
+
require "ciesta/field_list"
|
8
|
+
require "ciesta/syncer"
|
9
|
+
require "ciesta/types"
|
10
|
+
require "ciesta/validator"
|
11
|
+
require "ciesta/errors"
|
12
|
+
require "ciesta/field"
|
13
|
+
require "ciesta/form"
|
9
14
|
|
10
15
|
module Ciesta
|
11
16
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Simple module for delegate methods
|
4
|
+
module Ciesta::Delegator
|
5
|
+
# Delegates methods to object
|
6
|
+
#
|
7
|
+
# @param [Array<String, Symbol>] methods Methods to delegate
|
8
|
+
# @param [Symbol] to Method's name delegate to
|
9
|
+
def delegate(*methods, to:)
|
10
|
+
methods.each do |name|
|
11
|
+
method_def = [
|
12
|
+
"def #{name}(*args, &block)",
|
13
|
+
" if !#{to}.nil?",
|
14
|
+
" #{to}.#{name}(*args, &block)",
|
15
|
+
" end",
|
16
|
+
"end",
|
17
|
+
].join(";")
|
18
|
+
|
19
|
+
module_eval(method_def)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/ciesta/errors.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ciesta
|
4
|
+
# Error class for error about violating constraints
|
2
5
|
ViolatesConstraints = Class.new(ArgumentError)
|
3
|
-
|
6
|
+
|
7
|
+
# Error for invalid object
|
8
|
+
FormNotValid = Class.new(StandardError)
|
9
|
+
|
10
|
+
# Error for missing field definition
|
4
11
|
FieldNotDefined = Class.new(NoMethodError)
|
5
12
|
end
|
data/lib/ciesta/field.rb
CHANGED
@@ -1,27 +1,78 @@
|
|
1
|
-
|
2
|
-
class Field
|
3
|
-
DEFAULT_TYPE = Ciesta::Types::Any
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
3
|
+
# Class for storing form field
|
4
|
+
#
|
5
|
+
# @attr_reader [Symbol] name Field name
|
6
|
+
# @attr_reader [Ciesta::Types::Declaration] type Field type
|
7
|
+
class Ciesta::Field
|
8
|
+
# Default type when another one is not passed
|
9
|
+
DEFAULT_TYPE = Ciesta::Types::Any
|
6
10
|
|
7
|
-
|
8
|
-
self.name = name.to_sym
|
9
|
-
self.type = options.delete(:type) || DEFAULT_TYPE
|
10
|
-
self.value = options.delete(:default)
|
11
|
-
end
|
11
|
+
attr_reader :name, :type
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
# Constructor
|
14
|
+
#
|
15
|
+
# @param [Symbol] name Name of the field
|
16
|
+
# @param [Hash] options Field's options
|
17
|
+
# @option [Ciesta::Types::Definition] :type Type of value stored in this field
|
18
|
+
# @option [Proc, Lambda, any] :default Default value for this field
|
19
|
+
def initialize(name, options)
|
20
|
+
@name = name.to_sym
|
21
|
+
@type = options.delete(:type) || DEFAULT_TYPE
|
22
|
+
@default = options.delete(:default)
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
# Sets a new value for field
|
26
|
+
#
|
27
|
+
# @param [any] val Value
|
28
|
+
#
|
29
|
+
# @raise Ciesta::ViolatesConstraints
|
30
|
+
def value=(val)
|
31
|
+
@value = type[val]
|
32
|
+
@was_set = true
|
33
|
+
rescue Dry::Types::ConstraintError
|
34
|
+
raise Ciesta::ViolatesConstraints, "#{val} is not a #{type.name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns current value
|
38
|
+
#
|
39
|
+
# @return [any]
|
40
|
+
def value
|
41
|
+
return @value if @was_set
|
22
42
|
|
23
|
-
|
43
|
+
@value || default
|
44
|
+
end
|
24
45
|
|
25
|
-
|
46
|
+
# Binds current field to object
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @param [Object] obj Object to mapping to
|
50
|
+
def bind(obj)
|
51
|
+
@binding = obj
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Returns typed default value for field
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
# @return [any]
|
60
|
+
# @raise Ciesta::ViolatesConstraints
|
61
|
+
def default
|
62
|
+
type[raw_default]
|
63
|
+
rescue Dry::Types::ConstraintError
|
64
|
+
raise Ciesta::ViolatesConstraints, "#{def_value} is not a #{type.name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns raw default value
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
# @return [any]
|
71
|
+
def raw_default
|
72
|
+
if @default.respond_to?(:call) && @binding
|
73
|
+
@binding.instance_exec(&@default)
|
74
|
+
else
|
75
|
+
@default
|
76
|
+
end
|
26
77
|
end
|
27
78
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# List of object fields
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @attr_reader [Hash<Symbol, Ciesta::Field>] list Field list
|
7
|
+
class Ciesta::FieldList
|
8
|
+
# Constructor
|
9
|
+
def initialize
|
10
|
+
@list = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds new field to list
|
14
|
+
#
|
15
|
+
# @param [Ciesta::Field] field New field
|
16
|
+
def <<(field)
|
17
|
+
list[field.name] = field
|
18
|
+
end
|
19
|
+
|
20
|
+
# Getting field by name
|
21
|
+
#
|
22
|
+
# @param [Symbol] name Field name
|
23
|
+
#
|
24
|
+
# @return [Ciesta::Field]
|
25
|
+
def [](name)
|
26
|
+
list[name.to_sym].value
|
27
|
+
end
|
28
|
+
|
29
|
+
# Setting field value
|
30
|
+
#
|
31
|
+
# @param [Symbol] name Field name
|
32
|
+
# @param [any] value Field value
|
33
|
+
def []=(name, value)
|
34
|
+
list[name.to_sym].value = value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Mass assign values to fields
|
38
|
+
#
|
39
|
+
# @param [Hash<Symbol, any>] attributes Attributes
|
40
|
+
#
|
41
|
+
# @raise Ciesta::FieldNotDefined
|
42
|
+
# @return [Boolean]
|
43
|
+
def assign!(attributes)
|
44
|
+
attributes.each { |name, value| self[name] = value }
|
45
|
+
true
|
46
|
+
rescue NoMethodError => e
|
47
|
+
raise Ciesta::FieldNotDefined, "Field #{e.name} is not specified"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Mass assign values to fields
|
51
|
+
#
|
52
|
+
# @param [Hash<Symbol, any>] attributes Attributes
|
53
|
+
#
|
54
|
+
# @return [Boolean]
|
55
|
+
def assign(attributes)
|
56
|
+
attributes = attributes.keep_if { |key| keys.include?(key) }
|
57
|
+
assign!(attributes) rescue false
|
58
|
+
end
|
59
|
+
|
60
|
+
# Getting all field names
|
61
|
+
#
|
62
|
+
# @return [Array<Symbol>]
|
63
|
+
def keys
|
64
|
+
list.keys
|
65
|
+
end
|
66
|
+
|
67
|
+
# Getting all field values
|
68
|
+
#
|
69
|
+
# @return [Hash<Symbol, any>]
|
70
|
+
def attributes
|
71
|
+
list.values.each_with_object({}) { |field, mem| mem[field.name] = field.value }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Iterate over all fields
|
75
|
+
#
|
76
|
+
# @param [Block] block Block to iterate
|
77
|
+
def each
|
78
|
+
list.each { |name, field| yield(name, field.value) }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
attr_reader :list
|
84
|
+
end
|
data/lib/ciesta/form.rb
CHANGED
@@ -1,91 +1,124 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Main form class
|
4
|
+
#
|
5
|
+
# @attr_reader [Object] object Object of form
|
6
|
+
class Ciesta::Form
|
7
|
+
extend Ciesta::Delegator
|
8
|
+
|
9
|
+
# @!method assign
|
10
|
+
# @!method assign!
|
11
|
+
# @!method attributes
|
12
|
+
# @see Ciesta::FieldList
|
13
|
+
delegate :assign, :assign!, :attributes, to: :fields
|
14
|
+
|
15
|
+
# @!method sync!
|
16
|
+
# @!method sync
|
17
|
+
# @see Ciesta::Syncer
|
18
|
+
delegate :sync, to: :syncer
|
19
|
+
|
20
|
+
# @!method errors
|
21
|
+
# @see Ciesta::Validator
|
22
|
+
delegate :errors, to: :validator
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Declare new form field
|
26
|
+
#
|
27
|
+
# @param [Symbol] name Field name
|
28
|
+
# @param [Hash] options Options
|
29
|
+
# @option (see Ciesta::Field)
|
30
|
+
def field(name, options = {})
|
4
31
|
name = name.to_sym
|
5
|
-
fields
|
32
|
+
fields << Ciesta::Field.new(name, options)
|
6
33
|
|
7
|
-
define_method(name) {
|
8
|
-
define_method("#{name}=") { |value|
|
34
|
+
define_method(name) { fields[name] }
|
35
|
+
define_method("#{name}=") { |value| fields[name] = value }
|
9
36
|
end
|
10
37
|
|
11
|
-
|
38
|
+
# Declare rules for valudation
|
39
|
+
#
|
40
|
+
# @param [Block] block Block with validation rules
|
41
|
+
# @see http://dry-rb.org/gems/dry-validation
|
42
|
+
def validate(&block)
|
12
43
|
validator.use(&block)
|
13
44
|
end
|
14
45
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
mem[key] = object.public_send(key)
|
22
|
-
end
|
23
|
-
|
24
|
-
assign(obj_values)
|
25
|
-
end
|
26
|
-
|
27
|
-
def valid?(params = nil)
|
28
|
-
assign(params) if params
|
29
|
-
validator.valid?(attributes)
|
30
|
-
end
|
31
|
-
|
32
|
-
def assign!(attributes)
|
33
|
-
attributes.each { |key, value| send("#{key}=", value) }
|
34
|
-
rescue NoMethodError => e
|
35
|
-
raise Ciesta::FieldNotDefined, e.message
|
36
|
-
end
|
37
|
-
|
38
|
-
def assign(params)
|
39
|
-
keys = fields.keys
|
40
|
-
params.keep_if { |key, _value| keys.include?(key) }
|
41
|
-
begin
|
42
|
-
assign!(params)
|
43
|
-
rescue StandardError
|
44
|
-
nil
|
45
|
-
end
|
46
|
+
# Returns field list
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @return [Ciesta::FieldList]
|
50
|
+
def fields
|
51
|
+
@fields ||= Ciesta::FieldList.new
|
46
52
|
end
|
47
53
|
|
48
|
-
|
49
|
-
|
54
|
+
# Returns form validator
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
# @return [Ciesta::Validator]
|
58
|
+
def validator
|
59
|
+
@validator ||= Ciesta::Validator.new
|
50
60
|
end
|
61
|
+
end
|
51
62
|
|
52
|
-
|
53
|
-
raise Ciesta::NotValid, 'Form is not valid' unless errors.empty?
|
63
|
+
attr_accessor :object
|
54
64
|
|
55
|
-
|
65
|
+
# Constructor
|
66
|
+
#
|
67
|
+
# @param [Object] object Object wich will be updated though this form
|
68
|
+
def initialize(object)
|
69
|
+
@object = object
|
56
70
|
|
57
|
-
|
58
|
-
|
71
|
+
obj_values = fields.keys.each_with_object({}) do |key, mem|
|
72
|
+
mem[key] = object.public_send(key)
|
59
73
|
end
|
60
74
|
|
61
|
-
|
62
|
-
|
63
|
-
rescue StandardError
|
64
|
-
nil
|
65
|
-
end
|
75
|
+
assign(obj_values)
|
76
|
+
end
|
66
77
|
|
67
|
-
|
78
|
+
# Checks if form is valid
|
79
|
+
#
|
80
|
+
# @param [Hash] params Attrubutes to assign before validation
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
def valid?(params = nil)
|
84
|
+
assign(params) if params
|
85
|
+
validator.valid?(attributes)
|
86
|
+
end
|
68
87
|
|
69
|
-
|
70
|
-
|
71
|
-
|
88
|
+
# Sync form attributes to object
|
89
|
+
#
|
90
|
+
# @param [Block] block Block wich will be yielded after synfing
|
91
|
+
#
|
92
|
+
# @raise Ciesta::FormNotValid
|
93
|
+
# @return [Boolean]
|
94
|
+
def sync!(&block)
|
95
|
+
raise Ciesta::FormNotValid, "Form is not valid" unless valid?
|
96
|
+
syncer.sync(&block)
|
97
|
+
end
|
72
98
|
|
73
|
-
|
74
|
-
@validator ||= Ciesta::Validator.new
|
75
|
-
end
|
99
|
+
private
|
76
100
|
|
77
|
-
|
78
|
-
|
79
|
-
|
101
|
+
# Sync class for form
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
# @return [Ciesta::Syncer]
|
105
|
+
def syncer
|
106
|
+
@syncer ||= Ciesta::Syncer.new(object, fields)
|
107
|
+
end
|
80
108
|
|
81
|
-
|
82
|
-
|
83
|
-
|
109
|
+
# Returns form validator
|
110
|
+
#
|
111
|
+
# @api private
|
112
|
+
# @see Ciesta::Form.validator
|
113
|
+
def validator
|
114
|
+
self.class.validator
|
115
|
+
end
|
84
116
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
117
|
+
# Returns field list
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
# @see Ciesta::Form.fields
|
121
|
+
def fields
|
122
|
+
self.class.fields
|
90
123
|
end
|
91
124
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Class for syncing fields and object
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @attr_reader [Object] object Form objecy
|
7
|
+
# @attr_reader [Ciesta::FieldList] fields Field list
|
8
|
+
class Ciesta::Syncer
|
9
|
+
# Constructor
|
10
|
+
#
|
11
|
+
# @param [Object] object Form objecr
|
12
|
+
# @param [Ciesta::FieldList] fields Field list
|
13
|
+
def initialize(object, fields)
|
14
|
+
@object = object
|
15
|
+
@fields = fields
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sync attributes with objec
|
19
|
+
#
|
20
|
+
# @param [Block] block Block which will be yielded after syncing
|
21
|
+
#
|
22
|
+
# @return [Booelan]
|
23
|
+
def sync
|
24
|
+
fields.each { |name, value| object.send("#{name}=", value) }
|
25
|
+
|
26
|
+
yield(object) if block_given?
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :object, :fields
|
34
|
+
end
|
data/lib/ciesta/types.rb
CHANGED
data/lib/ciesta/validator.rb
CHANGED
@@ -1,20 +1,38 @@
|
|
1
|
-
|
2
|
-
class Validator
|
3
|
-
attr_accessor :schema, :errors
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
# Validatior class for form
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @attr_reader [Dry::Validation::Schema] schema Schema for validation
|
7
|
+
# @attr_reader [Hash] errors Array with errors
|
8
|
+
class Ciesta::Validator
|
9
|
+
attr_reader :errors
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
# Constructor
|
12
|
+
def initialize
|
13
|
+
@errors = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set schema for validation
|
17
|
+
#
|
18
|
+
# @param [Block] block Block wich returns the schema
|
19
|
+
def use(&block)
|
20
|
+
@schema = Dry::Validation.Form(&block)
|
21
|
+
end
|
12
22
|
|
13
|
-
|
14
|
-
|
23
|
+
# Checks if schema is valid
|
24
|
+
#
|
25
|
+
# @param [Hash<Symbol, any>] attributes Attributes to check
|
26
|
+
#
|
27
|
+
# @return [Boolean]
|
28
|
+
def valid?(attributes)
|
29
|
+
return true if schema.nil?
|
15
30
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
31
|
+
@errors = schema.call(attributes).errors
|
32
|
+
errors.empty?
|
19
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :schema
|
20
38
|
end
|
data/lib/ciesta/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ciesta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-types
|
@@ -116,7 +116,9 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- ".gitignore"
|
119
|
+
- ".hound.yml"
|
119
120
|
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
120
122
|
- ".travis.yml"
|
121
123
|
- CODE_OF_CONDUCT.md
|
122
124
|
- Gemfile
|
@@ -128,9 +130,12 @@ files:
|
|
128
130
|
- bin/setup
|
129
131
|
- ciesta.gemspec
|
130
132
|
- lib/ciesta.rb
|
133
|
+
- lib/ciesta/delegator.rb
|
131
134
|
- lib/ciesta/errors.rb
|
132
135
|
- lib/ciesta/field.rb
|
136
|
+
- lib/ciesta/field_list.rb
|
133
137
|
- lib/ciesta/form.rb
|
138
|
+
- lib/ciesta/syncer.rb
|
134
139
|
- lib/ciesta/types.rb
|
135
140
|
- lib/ciesta/validator.rb
|
136
141
|
- lib/ciesta/version.rb
|