formalism-model_forms 0.6.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 +7 -0
- data/CHANGELOG.md +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/lib/formalism/model_forms.rb +56 -0
- data/lib/formalism/model_forms/_base.rb +166 -0
- data/lib/formalism/model_forms/conditions/_base.rb +19 -0
- data/lib/formalism/model_forms/create.rb +37 -0
- data/lib/formalism/model_forms/delete.rb +31 -0
- data/lib/formalism/model_forms/find.rb +82 -0
- data/lib/formalism/model_forms/find_or_create.rb +43 -0
- data/lib/formalism/model_forms/list.rb +94 -0
- data/lib/formalism/model_forms/move.rb +36 -0
- data/lib/formalism/model_forms/select.rb +34 -0
- data/lib/formalism/model_forms/update.rb +56 -0
- data/lib/formalism/model_forms/version.rb +7 -0
- metadata +288 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 65944c5d862966df0531c80ae45d603e01bd8a576983a5ad180e75c29f9116e6
|
4
|
+
data.tar.gz: eedd373f63d2e6b4dc2da93c896fa9ebdf65a0e172a8c2794f5497abe622195f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5580e66079f5f4fafa3819a0fe9bb0a8825819bd2daacf5da932689e5123748be187613fd7bc9d3e01b285f7ffa952ad7602980705efa3987e75dcca9954db75
|
7
|
+
data.tar.gz: e2b511cb8a47a70018fa55af7a2a0807d51429740ebf243edd203826751316a6ae500b3e3f709e6df370b1ffe3928bd3987341725af428e5911a15e31acea22e
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## master (unreleased)
|
4
|
+
|
5
|
+
## 0.6.0 (2020-09-23)
|
6
|
+
|
7
|
+
## 0.5.0 (2020-09-22)
|
8
|
+
|
9
|
+
* Require `forwardable` before its usage.
|
10
|
+
|
11
|
+
## 0.4.0 (2020-07-10)
|
12
|
+
|
13
|
+
* Remove `puts` about `define_for_project` steps
|
14
|
+
|
15
|
+
## 0.3.0 (2020-07-10)
|
16
|
+
|
17
|
+
* Remove debug actions
|
18
|
+
|
19
|
+
## 0.2.0 (2020-07-10)
|
20
|
+
|
21
|
+
* Update `flame-pagination` to version `0.3.0`, which allows Flame version 5.
|
22
|
+
|
23
|
+
## 0.1.0 (2020-07-10)
|
24
|
+
|
25
|
+
* Initial release.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Alexander Popov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Formalism Model Forms
|
2
|
+
|
3
|
+
[](https://cirrus-ci.com/github/AlexWayfer/formalism-model_forms)
|
4
|
+
[](https://codecov.io/gh/AlexWayfer/formalism-model_forms)
|
5
|
+
[](https://codeclimate.com/github/AlexWayfer/formalism-model_forms)
|
6
|
+
[](https://depfu.com/repos/github/AlexWayfer/formalism-model_forms)
|
7
|
+
[](https://inch-ci.org/github/AlexWayfer/formalism-model_forms)
|
8
|
+
[](LICENSE.txt)
|
9
|
+
[](https://rubygems.org/gems/formalism-model_forms)
|
10
|
+
|
11
|
+
Ruby gem with standard [Formalism](https://github.com/AlexWayfer/formalism) forms
|
12
|
+
for ([Sequel](https://sequel.jeremyevans.net/)) models.
|
13
|
+
Used by [Flame Generate Toys](https://github.com/AlexWayfer/flame_generate_toys).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'formalism-model_forms'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```shell
|
26
|
+
bundle install
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```shell
|
32
|
+
gem install formalism-model_forms
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# forms/_base.rb
|
39
|
+
|
40
|
+
require 'formalism/model_forms'
|
41
|
+
|
42
|
+
## Define base form and model forms via gem
|
43
|
+
module MyProject
|
44
|
+
module Forms
|
45
|
+
## Base class for forms
|
46
|
+
class Base < Formalism::Form
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Formalism::ModelForms.define_for_project self
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
`.define_for_project` requires project namespace (`self` in the example above)
|
55
|
+
and allows such options:
|
56
|
+
|
57
|
+
* `:forms_namespace`, default is `project_namespace::Forms`
|
58
|
+
* `:models_namespace`, default is `project_namespace::Models`
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# forms/user/create.rb
|
62
|
+
|
63
|
+
module MyProject
|
64
|
+
module Forms
|
65
|
+
module User
|
66
|
+
class Create < Formalism::ModelForms::Create
|
67
|
+
field :name
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def validate
|
72
|
+
errors.add 'Name is not provided' if name.to_s.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
# `execute` is inherited from `Formalism::ModelForms`
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
## Development
|
83
|
+
|
84
|
+
After checking out the repo, run `bundle install` to install dependencies.
|
85
|
+
|
86
|
+
Then, run `toys rspec` to run the tests.
|
87
|
+
|
88
|
+
To install this gem onto your local machine, run `toys gem install`.
|
89
|
+
|
90
|
+
To release a new version, run `toys gem release %version%`.
|
91
|
+
See how it works [here](https://github.com/AlexWayfer/gem_toys#release).
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/AlexWayfer/formalism).
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
The gem is available as open source under the terms of the
|
100
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'formalism'
|
4
|
+
require 'module_methods'
|
5
|
+
|
6
|
+
Dir["#{__dir__}/model_forms/**/*.rb"].sort.each { |file| require file }
|
7
|
+
|
8
|
+
module Formalism
|
9
|
+
## Module for model forms
|
10
|
+
module ModelForms
|
11
|
+
class << self
|
12
|
+
def define_for_project(
|
13
|
+
project_namespace,
|
14
|
+
forms_namespace: project_namespace::Forms,
|
15
|
+
models_namespace: (project_namespace::Models if project_namespace.const_defined?(:Models))
|
16
|
+
)
|
17
|
+
forms_namespace.const_set :Model, Module.new
|
18
|
+
|
19
|
+
define_model_base_form forms_namespace, models_namespace
|
20
|
+
|
21
|
+
define_other_model_forms forms_namespace
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def define_model_base_form(forms_namespace, models_namespace)
|
27
|
+
forms_namespace::Model.const_set :Base, (Class.new(forms_namespace::Base) do
|
28
|
+
include ModelForms::Base
|
29
|
+
|
30
|
+
const_set :FORMS_NAMESPACE, forms_namespace
|
31
|
+
const_set :MODELS_NAMESPACE, models_namespace
|
32
|
+
end)
|
33
|
+
end
|
34
|
+
|
35
|
+
FORM_CLASSES =
|
36
|
+
%i[Create Delete Find FindOrCreate List Move Update]
|
37
|
+
.map { |name| [name, :Base] }
|
38
|
+
.push(%i[Select Find])
|
39
|
+
.to_h
|
40
|
+
.freeze
|
41
|
+
|
42
|
+
def define_other_model_forms(forms_namespace)
|
43
|
+
FORM_CLASSES.each do |form_name, parent_name|
|
44
|
+
forms_namespace::Model.class_eval(
|
45
|
+
<<~CODE,
|
46
|
+
class #{form_name} < #{forms_namespace}::Model::#{parent_name}
|
47
|
+
include ModelForms::#{form_name}
|
48
|
+
end
|
49
|
+
CODE
|
50
|
+
__FILE__, __LINE__ - 4
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'gorilla_patch/inflections'
|
5
|
+
require 'gorilla_patch/namespace'
|
6
|
+
require 'memery'
|
7
|
+
require 'sequel'
|
8
|
+
|
9
|
+
module Formalism
|
10
|
+
module ModelForms
|
11
|
+
## Base form for models
|
12
|
+
module Base
|
13
|
+
extend ::ModuleMethods::Extension
|
14
|
+
include Formalism::Form::Fields
|
15
|
+
|
16
|
+
# TODO: Check
|
17
|
+
extend Forwardable
|
18
|
+
def_delegators(
|
19
|
+
'self.class',
|
20
|
+
:namespace, :model_name, :model, :instance_variable_name
|
21
|
+
)
|
22
|
+
|
23
|
+
## Module for Base form class methods
|
24
|
+
module ClassMethods
|
25
|
+
include Memery
|
26
|
+
|
27
|
+
def inherited(form)
|
28
|
+
super
|
29
|
+
|
30
|
+
return if form.instance_name == :model
|
31
|
+
return if form.method_defined?(form.instance_name.to_s)
|
32
|
+
|
33
|
+
form.alias_method form.instance_name, :instance
|
34
|
+
form.alias_method "#{form.instance_name}=", :instance=
|
35
|
+
end
|
36
|
+
|
37
|
+
using GorillaPatch::Namespace
|
38
|
+
using GorillaPatch::Inflections
|
39
|
+
|
40
|
+
memoize def namespace
|
41
|
+
self::FORMS_NAMESPACE.const_get(deconstantize)
|
42
|
+
end
|
43
|
+
|
44
|
+
memoize def model_name
|
45
|
+
deconstantize.split('::')[2..-1].reverse
|
46
|
+
.find { |part| self::MODELS_NAMESPACE.const_defined?(part, false) || part == 'Model' }
|
47
|
+
end
|
48
|
+
|
49
|
+
memoize def model
|
50
|
+
self::MODELS_NAMESPACE.const_get(model_name, false)
|
51
|
+
end
|
52
|
+
|
53
|
+
memoize def instance_name
|
54
|
+
model_name.underscore.to_sym
|
55
|
+
end
|
56
|
+
|
57
|
+
memoize def instance_variable_name
|
58
|
+
:"@#{instance_name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(*)
|
63
|
+
@cached =
|
64
|
+
Sequel::Plugins.const_defined?(:StaticCache) &&
|
65
|
+
model.plugins.include?(Sequel::Plugins::StaticCache)
|
66
|
+
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
def before_retry
|
71
|
+
if instance_variable_defined?(instance_variable_name)
|
72
|
+
remove_instance_variable instance_variable_name
|
73
|
+
end
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def instance
|
79
|
+
return @instance if defined?(@instance)
|
80
|
+
|
81
|
+
@instance = find_instance
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def execute
|
87
|
+
# TODO: Check
|
88
|
+
Actions::Application::Restart.run if @cached && ENV['RACK_ENV'] != 'test'
|
89
|
+
|
90
|
+
instance
|
91
|
+
end
|
92
|
+
|
93
|
+
attr_writer :instance
|
94
|
+
|
95
|
+
def find_instance
|
96
|
+
model[id].public_send(@cached ? :dup : :itself)
|
97
|
+
end
|
98
|
+
|
99
|
+
def field_condition(name, value)
|
100
|
+
options = self.class.fields_and_nested_forms[name]
|
101
|
+
|
102
|
+
return instance_exec(name, value, &options[:condition]) if options.key?(:condition)
|
103
|
+
|
104
|
+
field_condition_class(options[:type]).new(name, value).result
|
105
|
+
end
|
106
|
+
|
107
|
+
using GorillaPatch::Inflections
|
108
|
+
|
109
|
+
def field_condition_class(type)
|
110
|
+
const_name = type.to_s.camelize
|
111
|
+
const_name = 'Base' if const_name.empty? || !Conditions.const_defined?(const_name, false)
|
112
|
+
|
113
|
+
Conditions.const_get(const_name, false)
|
114
|
+
end
|
115
|
+
|
116
|
+
def conditions_for_dataset(except: [])
|
117
|
+
conditions = fields_and_nested_forms.map do |name, value|
|
118
|
+
next Sequel[true] if Array(except).include?(name)
|
119
|
+
|
120
|
+
field_condition(name, value)
|
121
|
+
end
|
122
|
+
|
123
|
+
## `[].reduce(:&)` returns `nil`
|
124
|
+
conditions.any? ? conditions.reduce(:&) : conditions
|
125
|
+
end
|
126
|
+
|
127
|
+
def compare_value(instance_value, value)
|
128
|
+
## It can be Number, nil, etc., but logic for Regexp is below
|
129
|
+
instance_value = instance_value.to_s if value.is_a?(String) && !instance_value.is_a?(Regexp)
|
130
|
+
|
131
|
+
## Match instead of equal if any side is Regexp
|
132
|
+
if instance_value.is_a?(Regexp) || value.is_a?(Regexp)
|
133
|
+
instance_value.match? value
|
134
|
+
## And just equal if not
|
135
|
+
else
|
136
|
+
instance_value == value
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def select_cached
|
141
|
+
model.all.select do |instance|
|
142
|
+
fields_and_nested_forms.all? do |key, values|
|
143
|
+
key =
|
144
|
+
self.class.fields_and_nested_forms[key].fetch(:compare_key, key)
|
145
|
+
|
146
|
+
Array(values).any? do |value|
|
147
|
+
compare_value instance.public_send(key), value
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Pluralizes instance_method name
|
154
|
+
module Plural
|
155
|
+
include Base
|
156
|
+
include Memery
|
157
|
+
|
158
|
+
using GorillaPatch::Inflections.from_sequel
|
159
|
+
|
160
|
+
memoize def instance_name
|
161
|
+
model_name.underscore.pluralize.to_sym
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
module Conditions
|
6
|
+
## Base class for `:condition` form field option
|
7
|
+
class Base
|
8
|
+
def initialize(name, value)
|
9
|
+
@name = name
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def result
|
14
|
+
Sequel[@name => @value]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model creation
|
6
|
+
module Create
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
|
10
|
+
@instance_from_initialization = @instance
|
11
|
+
|
12
|
+
initialize_instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def before_retry
|
16
|
+
super
|
17
|
+
|
18
|
+
initialize_instance
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def execute
|
24
|
+
instance.set(fields_and_nested_forms).save
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_instance
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize_instance
|
33
|
+
self.instance = model.new fields(for_merge: true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model deletion
|
6
|
+
module Delete
|
7
|
+
include Formalism::ModelForms::Base
|
8
|
+
|
9
|
+
# module ClassMethods
|
10
|
+
# def inherited(form)
|
11
|
+
# super
|
12
|
+
#
|
13
|
+
# return unless form.model.auditable?
|
14
|
+
#
|
15
|
+
# form.include RequiredChangesAuthor
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
|
19
|
+
field :id, Integer
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def execute
|
24
|
+
instance.changes_author = changes_author if model.auditable?
|
25
|
+
|
26
|
+
instance.destroy
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model finding
|
6
|
+
module Find
|
7
|
+
include Formalism::ModelForms::Base
|
8
|
+
include Memery
|
9
|
+
|
10
|
+
field :id, Integer, default: nil
|
11
|
+
|
12
|
+
def initialize(params_or_instance = {})
|
13
|
+
if params_or_instance.is_a?(Hash)
|
14
|
+
super(params_or_instance)
|
15
|
+
else
|
16
|
+
@initialized_with_instance = true
|
17
|
+
self.instance = params_or_instance
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def before_retry
|
23
|
+
return if @initialized_with_instance
|
24
|
+
|
25
|
+
super
|
26
|
+
|
27
|
+
return unless instance_variable_defined? :@instance
|
28
|
+
|
29
|
+
remove_instance_variable :@instance
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def execute
|
35
|
+
self.instance = @cached ? find_cached : find_non_cached
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_instance
|
39
|
+
return unless valid?
|
40
|
+
|
41
|
+
## `run` can return `nil` if form is not `runnable`
|
42
|
+
run&.result
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_cached
|
46
|
+
dataset.find do |instance|
|
47
|
+
fields_and_nested_forms.all? do |key, value|
|
48
|
+
compare_value instance[key], value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_non_cached
|
54
|
+
dataset.first
|
55
|
+
rescue Sequel::DatabaseError => e
|
56
|
+
## I'm not sure that there is safe to use
|
57
|
+
## `Sequel::DatabaseError`
|
58
|
+
raise e unless e.cause.is_a? PG::DataException
|
59
|
+
raise e if e.cause.is_a? PG::InvalidTextRepresentation
|
60
|
+
end
|
61
|
+
|
62
|
+
memoize def dataset
|
63
|
+
## `Dataset#all` for `EAGER` with `.first` in Find forms
|
64
|
+
(@cached ? model : filtered_non_cached_dataset).all
|
65
|
+
end
|
66
|
+
|
67
|
+
memoize def filtered_non_cached_dataset
|
68
|
+
unfiltered_non_cached_dataset.where(conditions_for_dataset)
|
69
|
+
end
|
70
|
+
|
71
|
+
memoize def unfiltered_non_cached_dataset
|
72
|
+
(list_form_class&.new(params) || model).dataset
|
73
|
+
end
|
74
|
+
|
75
|
+
def list_form_class
|
76
|
+
return unless namespace.const_defined?(:List) && namespace::List.is_a?(Class)
|
77
|
+
|
78
|
+
namespace::List
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model finding
|
6
|
+
module FindOrCreate
|
7
|
+
extend ::ModuleMethods::Extension
|
8
|
+
|
9
|
+
## Module for class methods
|
10
|
+
module ClassMethods
|
11
|
+
def inherited(child_form)
|
12
|
+
super
|
13
|
+
|
14
|
+
child_form.nested :find, child_form.namespace::Find,
|
15
|
+
initialize: ->(form) { form.new(@params_or_instance) },
|
16
|
+
errors_key: nil
|
17
|
+
|
18
|
+
child_form.nested :create, child_form.namespace::Create,
|
19
|
+
initialize: ->(form) { form.new(@params_or_instance) },
|
20
|
+
errors_key: nil,
|
21
|
+
merge_errors: -> { find_form.instance.nil? }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(params_or_instance = {})
|
26
|
+
@params_or_instance = params_or_instance
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def instance
|
32
|
+
find_form.instance || create_form.instance
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def execute
|
38
|
+
self.instance = create_form.run.result unless instance.exists?
|
39
|
+
instance
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'flame/pagination/for_forms'
|
4
|
+
|
5
|
+
module Formalism
|
6
|
+
module ModelForms
|
7
|
+
## Base form for model listing
|
8
|
+
module List
|
9
|
+
include Formalism::ModelForms::Base
|
10
|
+
include Memery
|
11
|
+
extend ModelForms::Base::Plural
|
12
|
+
|
13
|
+
## Module for class methods
|
14
|
+
module ClassMethods
|
15
|
+
include Memery
|
16
|
+
using GorillaPatch::Namespace
|
17
|
+
|
18
|
+
memoize def namespace
|
19
|
+
return super if super.demodulize != 'List'
|
20
|
+
|
21
|
+
self::FORMS_NAMESPACE.const_get(super.deconstantize, false)
|
22
|
+
end
|
23
|
+
|
24
|
+
def search_fields(*fields)
|
25
|
+
@search_fields ||= Set.new
|
26
|
+
return @search_fields if fields.empty?
|
27
|
+
|
28
|
+
@search_fields.merge(fields)
|
29
|
+
end
|
30
|
+
|
31
|
+
def inherited(list_form)
|
32
|
+
super
|
33
|
+
|
34
|
+
list_form.search_fields(*search_fields)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
EAGER = %i[].freeze
|
39
|
+
|
40
|
+
include Flame::Pagination::ForForms
|
41
|
+
|
42
|
+
field :search_query, merge: false
|
43
|
+
|
44
|
+
memoize def dataset
|
45
|
+
return select_cached if @cached
|
46
|
+
|
47
|
+
dataset =
|
48
|
+
ordered_unfiltered_dataset
|
49
|
+
.where(conditions_for_dataset)
|
50
|
+
.eager(*self.class::EAGER)
|
51
|
+
|
52
|
+
return dataset unless dataset.respond_to? :search
|
53
|
+
|
54
|
+
dataset.search(search_query, self.class.search_fields)
|
55
|
+
end
|
56
|
+
|
57
|
+
memoize def dataset_edges
|
58
|
+
[dataset.first, dataset.last]
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def execute
|
64
|
+
self.instance =
|
65
|
+
if dataset
|
66
|
+
if @cached
|
67
|
+
then cached_dataset_for_execute
|
68
|
+
else non_cached_dataset_for_execute.all
|
69
|
+
end
|
70
|
+
else
|
71
|
+
[]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def unfiltered_dataset
|
76
|
+
model.dataset
|
77
|
+
end
|
78
|
+
|
79
|
+
def ordered_unfiltered_dataset
|
80
|
+
result = unfiltered_dataset.from_self(alias: model.table_name)
|
81
|
+
result = result.reverse_order(:id) unless unfiltered_dataset.opts[:order]
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def cached_dataset_for_execute
|
86
|
+
limit_by_page ? dataset[limit_by_page] : dataset
|
87
|
+
end
|
88
|
+
|
89
|
+
def non_cached_dataset_for_execute
|
90
|
+
dataset.where(id: dataset.select(:id).limit(limit_by_page))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model moving
|
6
|
+
module Move
|
7
|
+
include Formalism::ModelForms::Base
|
8
|
+
|
9
|
+
field :id, Integer
|
10
|
+
|
11
|
+
attr_reader :direction
|
12
|
+
|
13
|
+
def initialize(direction, id, how_many = 1)
|
14
|
+
@direction = direction.to_sym
|
15
|
+
@how_many = how_many.to_i
|
16
|
+
|
17
|
+
super(id: id)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
POSSIBLE_DIRECTIONS = %i[up down].freeze
|
23
|
+
|
24
|
+
def validate
|
25
|
+
return if POSSIBLE_DIRECTIONS.include?(@direction)
|
26
|
+
|
27
|
+
errors.add :direction, :unknown, args: { direction: @direction }
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute
|
31
|
+
instance.public_send(:"move_#{@direction}", @how_many)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for many instances of model finding
|
6
|
+
module Select
|
7
|
+
include Formalism::ModelForms::Base
|
8
|
+
extend ModelForms::Base::Plural
|
9
|
+
|
10
|
+
field :id, Array, of: Integer, default: []
|
11
|
+
|
12
|
+
def initialize(params_or_instance = {})
|
13
|
+
## Instance should be an `Array` if there is not `params`
|
14
|
+
params_or_instance = params_or_instance.to_a unless params_or_instance.is_a?(Hash)
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def execute
|
22
|
+
self.instance = @cached ? select_cached : dataset
|
23
|
+
end
|
24
|
+
|
25
|
+
def instance_respond_to?(name)
|
26
|
+
@instance&.first&.respond_to?(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def instance_public_send(name)
|
30
|
+
@instance.map { |instance| instance.public_send(name) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formalism
|
4
|
+
module ModelForms
|
5
|
+
## Base form for model updating
|
6
|
+
module Update
|
7
|
+
include Formalism::ModelForms::Base
|
8
|
+
|
9
|
+
## Redefine `filed` and `nested` class methods with a specific `:default` from `instance`
|
10
|
+
module ClassMethods
|
11
|
+
%i[field nested].each do |method_name|
|
12
|
+
define_method(method_name) do |name, type_or_form = nil, **options|
|
13
|
+
unless method_name == :nested && options[:merge] == false
|
14
|
+
options[:default] = options.fetch(
|
15
|
+
:default, -> { instance&.public_send(name) }
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
super(name, type_or_form, **options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
field :id, Integer, merge: false
|
25
|
+
|
26
|
+
def initialize(params, id_or_instance)
|
27
|
+
if id_or_instance.is_a?(model)
|
28
|
+
self.instance = id_or_instance
|
29
|
+
else
|
30
|
+
self.id = id_or_instance
|
31
|
+
end
|
32
|
+
|
33
|
+
super params
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate
|
39
|
+
return if instance
|
40
|
+
|
41
|
+
add_error :itself, :not_exist
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute
|
45
|
+
instance.update(fields_and_nested_forms)
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def field_changed?(field)
|
50
|
+
return true unless instance.respond_to?(field)
|
51
|
+
|
52
|
+
public_send(field) != instance.public_send(field)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: formalism-model_forms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Popov
|
8
|
+
- Ivan Tyurin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2020-09-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: alt_memery
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: flame-pagination
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.3.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.3.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: formalism
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 0.2.0
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.2.0
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: gorilla_patch
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '4.0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '4.0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: module_methods
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.1.0
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.1.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: sequel
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '5.0'
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '5.0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: pry-byebug
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '3.9'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '3.9'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: bundler
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '2.0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '2.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: gem_toys
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - "~>"
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 0.4.0
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 0.4.0
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: toys
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - "~>"
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 0.11.0
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - "~>"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: 0.11.0
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: codecov
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - "~>"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 0.2.0
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: 0.2.0
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: rspec
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - "~>"
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '3.9'
|
175
|
+
type: :development
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "~>"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '3.9'
|
182
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: simplecov
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 0.19.0
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - "~>"
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 0.19.0
|
196
|
+
- !ruby/object:Gem::Dependency
|
197
|
+
name: rubocop
|
198
|
+
requirement: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - "~>"
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: 0.91.0
|
203
|
+
type: :development
|
204
|
+
prerelease: false
|
205
|
+
version_requirements: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - "~>"
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: 0.91.0
|
210
|
+
- !ruby/object:Gem::Dependency
|
211
|
+
name: rubocop-performance
|
212
|
+
requirement: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - "~>"
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '1.0'
|
217
|
+
type: :development
|
218
|
+
prerelease: false
|
219
|
+
version_requirements: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - "~>"
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '1.0'
|
224
|
+
- !ruby/object:Gem::Dependency
|
225
|
+
name: rubocop-rspec
|
226
|
+
requirement: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - "~>"
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '1.0'
|
231
|
+
type: :development
|
232
|
+
prerelease: false
|
233
|
+
version_requirements: !ruby/object:Gem::Requirement
|
234
|
+
requirements:
|
235
|
+
- - "~>"
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '1.0'
|
238
|
+
description: 'Standard Formalism forms for Sequel Models, such like create, find,
|
239
|
+
delete, etc.
|
240
|
+
|
241
|
+
'
|
242
|
+
email: alex.wayfer@gmail.com
|
243
|
+
executables: []
|
244
|
+
extensions: []
|
245
|
+
extra_rdoc_files: []
|
246
|
+
files:
|
247
|
+
- CHANGELOG.md
|
248
|
+
- LICENSE.txt
|
249
|
+
- README.md
|
250
|
+
- lib/formalism/model_forms.rb
|
251
|
+
- lib/formalism/model_forms/_base.rb
|
252
|
+
- lib/formalism/model_forms/conditions/_base.rb
|
253
|
+
- lib/formalism/model_forms/create.rb
|
254
|
+
- lib/formalism/model_forms/delete.rb
|
255
|
+
- lib/formalism/model_forms/find.rb
|
256
|
+
- lib/formalism/model_forms/find_or_create.rb
|
257
|
+
- lib/formalism/model_forms/list.rb
|
258
|
+
- lib/formalism/model_forms/move.rb
|
259
|
+
- lib/formalism/model_forms/select.rb
|
260
|
+
- lib/formalism/model_forms/update.rb
|
261
|
+
- lib/formalism/model_forms/version.rb
|
262
|
+
homepage: https://github.com/AlexWayfer/formalism-model_forms
|
263
|
+
licenses:
|
264
|
+
- MIT
|
265
|
+
metadata:
|
266
|
+
source_code_uri: https://github.com/AlexWayfer/formalism-model_forms
|
267
|
+
homepage_uri: https://github.com/AlexWayfer/formalism-model_forms
|
268
|
+
changelog_uri: https://github.com/AlexWayfer/formalism-model_forms/blob/master/CHANGELOG.md
|
269
|
+
post_install_message:
|
270
|
+
rdoc_options: []
|
271
|
+
require_paths:
|
272
|
+
- lib
|
273
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
274
|
+
requirements:
|
275
|
+
- - ">="
|
276
|
+
- !ruby/object:Gem::Version
|
277
|
+
version: 2.5.0
|
278
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
279
|
+
requirements:
|
280
|
+
- - ">="
|
281
|
+
- !ruby/object:Gem::Version
|
282
|
+
version: '0'
|
283
|
+
requirements: []
|
284
|
+
rubygems_version: 3.1.2
|
285
|
+
signing_key:
|
286
|
+
specification_version: 4
|
287
|
+
summary: Standard Formalism forms for Sequel Models
|
288
|
+
test_files: []
|