formwandler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1819221ed8cfcaee304d259ef15aa55aaf708048
4
+ data.tar.gz: 797085a3cd7806da1f9440b9a6d145648bdf840c
5
+ SHA512:
6
+ metadata.gz: 0f51d5d47fc62c67637c824993fde8045e763f652ca0394eadd8b93292c1073d7bca93661e39cb2ce0818bbc8c4c226e45012a46687c103ad337b1b3b136ac27
7
+ data.tar.gz: d79738347d59093d801c57e56a69db4784a6cac20e0af155f2e91be8f796bc1ff1c58bfdddc8924b112d4093f9239e09d0acb705c604887c05bbd1fe01a5ab2a
@@ -0,0 +1 @@
1
+ require_relative 'formwandler/form'
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formwandler
4
+ class Field
5
+ attr_reader :form, :field_definition
6
+
7
+ delegate :name, :model, :source, to: :field_definition
8
+
9
+ def initialize(form:, field_definition:)
10
+ @form = form
11
+ @field_definition = field_definition
12
+ end
13
+
14
+ def hidden?
15
+ if field_definition.hidden.respond_to?(:call)
16
+ form.instance_exec(&field_definition.hidden)
17
+ else
18
+ field_definition.hidden
19
+ end
20
+ end
21
+
22
+ def visible?
23
+ !hidden?
24
+ end
25
+
26
+ def disabled?
27
+ if field_definition.disabled.respond_to?(:call)
28
+ form.instance_exec(&field_definition.disabled)
29
+ else
30
+ field_definition.disabled
31
+ end
32
+ end
33
+
34
+ def options
35
+ all_options - hidden_options
36
+ end
37
+
38
+ def default?
39
+ !field_definition.default.nil?
40
+ end
41
+
42
+ def default
43
+ if field_definition.default.respond_to?(:call)
44
+ form.instance_exec(&field_definition.default)
45
+ else
46
+ field_definition.default
47
+ end
48
+ end
49
+
50
+ def delocalize
51
+ if field_definition.delocalize.respond_to?(:call)
52
+ form.instance_exec(&field_definition.delocalize)
53
+ else
54
+ field_definition.delocalize
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def all_options
61
+ _evaluate_field_definition_value(:options) || options_from_model || []
62
+ end
63
+
64
+ def hidden_options
65
+ field_definition.hidden_options&.reject do |_option, value|
66
+ _evaluate_value(value)
67
+ end&.keys || []
68
+ end
69
+
70
+ def options_from_model
71
+ return unless model_instance.present? && model_instance.is_a?(ActiveRecord::Base)
72
+ return unless enum_field?
73
+ model_instance.class.public_send(name.to_s.pluralize).keys
74
+ end
75
+
76
+ def enum_field?
77
+ model_instance.class.attribute_types[name.to_s].is_a?(ActiveRecord::Enum::EnumType)
78
+ end
79
+
80
+ def model_instance
81
+ form.models[field_definition.model]
82
+ end
83
+
84
+ def _evaluate_field_definition_value(attr_name)
85
+ value = field_definition.send(attr_name)
86
+ _evaluate_value(value)
87
+ end
88
+
89
+ def _evaluate_value(value)
90
+ if value.respond_to?(:call)
91
+ form.instance_exec(&value)
92
+ else
93
+ value
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formwandler
4
+ class FieldDefinition
5
+ attr_reader :name, :hidden, :disabled, :default, :delocalize, :model, :source, :options
6
+
7
+ def initialize(name, hidden: false, disabled: false, default: nil, delocalize: nil, model: nil, source: nil, options: nil)
8
+ @name = name
9
+ @hidden = hidden
10
+ @disabled = disabled
11
+ @default = default
12
+ @delocalize = delocalize
13
+ @model = model
14
+ @source = source || name
15
+ @options = options
16
+ end
17
+
18
+ def hide_option(name, value = true)
19
+ hidden_options[name.to_sym] = value
20
+ end
21
+
22
+ def hidden_options
23
+ @hidden_options ||= {}
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'field_definition'
4
+ require_relative 'field'
5
+
6
+ module Formwandler
7
+ class Form
8
+ include ActiveModel::Model
9
+ include ActiveModel::Validations::Callbacks
10
+
11
+ class << self
12
+ def model_name
13
+ ActiveModel::Name.new(self, nil, name.chomp('Form').underscore)
14
+ end
15
+
16
+ def field_definitions
17
+ @field_definitions ||= {}
18
+ end
19
+
20
+ def field(name, opts = {})
21
+ field_definitions[name] = FieldDefinition.new(name, opts)
22
+
23
+ yield(field_definitions[name]) if block_given?
24
+
25
+ if field_definitions[name].model.present?
26
+ attribute_accessor(name, field_definitions[name])
27
+ else
28
+ attr_accessor name
29
+ end
30
+ end
31
+
32
+ def attribute_accessor(name, field_definition)
33
+ define_method "#{name}=" do |value|
34
+ models[field_definition.model].send("#{field_definition.source}=", value)
35
+ end
36
+
37
+ define_method name do
38
+ models[field_definition.model].send(field_definition.source)
39
+ end
40
+ end
41
+
42
+ def save_order(*model_names)
43
+ @model_save_order = model_names
44
+ end
45
+
46
+ attr_reader :model_save_order
47
+ end
48
+
49
+ attr_reader :fields, :models, :current_user
50
+
51
+ def initialize(models: {}, current_user: nil)
52
+ @models = models.symbolize_keys
53
+ @current_user = current_user
54
+
55
+ initialize_fields
56
+ initialize_models
57
+ assign_defaults
58
+ end
59
+
60
+ def persisted?
61
+ models.values.first&.persisted? || false
62
+ end
63
+
64
+ def to_param
65
+ models.values.first.to_param
66
+ end
67
+
68
+ def field(name)
69
+ fields.fetch(name)
70
+ end
71
+
72
+ def fields_for_model(model)
73
+ fields.values.select { |field| field.model == model }
74
+ end
75
+
76
+ def models_valid?
77
+ all_valid = true
78
+ models.each do |name, model|
79
+ all_valid = false if model.invalid?
80
+ fields_for_model(name).each do |field|
81
+ model.errors[field.source].each do |error_message|
82
+ errors.add(field.name, error_message)
83
+ end
84
+ end
85
+ end
86
+ all_valid
87
+ end
88
+
89
+ def submit(params)
90
+ assign_attributes permitted_params(params)
91
+
92
+ if valid? && models_valid?
93
+ save_models!
94
+ load_results
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+ protected
101
+
102
+ def initialize_models
103
+ end
104
+
105
+ def assign_defaults
106
+ fields.values.each do |field|
107
+ next unless field.default?
108
+ send("#{field.name}=", field.default) if send(field.name).nil?
109
+ end
110
+ end
111
+
112
+ def save_models!
113
+ models.sort_by do |(name, _model)|
114
+ self.class.model_save_order.index(name) || self.class.model_save_order.size
115
+ end.map(&:last).compact.each(&:save!)
116
+ end
117
+
118
+ def load_results
119
+ true
120
+ end
121
+
122
+ def permitted_params(params)
123
+ visible_fields = fields.values.reject(&:hidden?).map(&:name)
124
+ delocalizations = fields.transform_values(&:delocalize).compact
125
+ params = ActionController::Parameters.new(params) if params.is_a?(Hash)
126
+ params.require(model_name.param_key).permit(*visible_fields).delocalize(delocalizations)
127
+ end
128
+
129
+ private
130
+
131
+ def initialize_fields
132
+ @fields = self.class.field_definitions.transform_values do |field_definition|
133
+ Field.new(form: self, field_definition: field_definition)
134
+ end
135
+ end
136
+ end
137
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: formwandler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Bilharz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ description: Create form objects for multiple models, dynamically mark fields as hidden
28
+ or disabled and even hide select options dynamically
29
+ email: sbilharz@heilmannsoftware.de
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/formwandler.rb
35
+ - lib/formwandler/field.rb
36
+ - lib/formwandler/field_definition.rb
37
+ - lib/formwandler/form.rb
38
+ homepage:
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '2.3'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.6.13
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Form objects for rails
62
+ test_files: []