compony 0.0.3 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0925cbc177ea6e36fd56db15cb41227efcb66b7ea5896a2dd76cc13fc7974a5
4
- data.tar.gz: 517baac0f2d25bb94857c5796d6ae41a94c291628edda7e453736488c2616036
3
+ metadata.gz: 8fa06e0cecacc05de1b6567e29594a44fcbfed063d0268e08de239432c51a7f2
4
+ data.tar.gz: 24ec1426a6259b2cc3b1ddbd0127d8878a46701a0d7b2003bf5668eddf1eb8a0
5
5
  SHA512:
6
- metadata.gz: 2a6ef1cb3327c852d2f544c376f22f34d8bac2eb56ff7c29e13fb887d0b4fcd91aa3de4dcfa3f16393ec386abbbefae593379c87e848518f00978d566250064a
7
- data.tar.gz: d3a985885dc53f7a4556c338038342f17b65991cea8ac0f993ff2ab0b638984d59b9a09b78ec7e9bec08eea4795debe1892a212875262629ff371295d771021c
6
+ metadata.gz: 66fc9fe7beb64285bc2f21a945d1596c24206072b0fd64172a3eea2130754363f11d980b9159b0ebc1702be72526eb2ce8e613d17ece625ac377eeb0698ee876
7
+ data.tar.gz: fce5758f4f812d6ba053cf085c807ffecbce5163ed676d529d00aaa36257478a3887f60d3d6bc1ec0294151144edb6d63dc415f82c8fa6b7727fc4f84be34a97
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 0.0.5
2
+
3
+ - Fix row bug for Email field type
4
+ - Auto-focus first non-hidden element in forms
5
+ - Add field type :url
6
+ - Automatically set the correct class when generating known components
7
+ - Add generator `components` that is able to mass-produce the most used components
8
+ - Make fields point to the correct `model_class` in case of STI
9
+ - Support hidden Anchormodel fields
10
+
11
+ # 0.0.4
12
+
13
+ - Unscope the namespace of resourceful components
14
+ - Add field type :email
15
+
16
+ ## Steps to take
17
+
18
+ - When inheriting from components, replace `Components::Resourceful::...` by `Components::...`
19
+
1
20
  # 0.0.3
2
21
 
3
22
  - Tolerate nil anchormodels
data/compony.gemspec CHANGED
@@ -2,31 +2,31 @@
2
2
  # This file is auto-generated via: 'rake gemspec'.
3
3
 
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: compony 0.0.3 ruby lib
5
+ # stub: compony 0.0.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "compony".freeze
9
- s.version = "0.0.3".freeze
9
+ s.version = "0.0.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Sandro Kalbermatter".freeze, "contributors".freeze]
14
- s.date = "2024-03-25"
15
- s.files = [".gitignore".freeze, ".ruby-version".freeze, ".yardopts".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "app/controllers/compony_controller.rb".freeze, "compony.gemspec".freeze, "config/locales/de.yml".freeze, "config/locales/en.yml".freeze, "config/routes.rb".freeze, "doc/resourceful_lifecycle.graphml".freeze, "doc/resourceful_lifecycle.pdf".freeze, "lib/compony.rb".freeze, "lib/compony/component.rb".freeze, "lib/compony/component_mixins/default/labelling.rb".freeze, "lib/compony/component_mixins/default/standalone.rb".freeze, "lib/compony/component_mixins/default/standalone/resourceful_verb_dsl.rb".freeze, "lib/compony/component_mixins/default/standalone/standalone_dsl.rb".freeze, "lib/compony/component_mixins/default/standalone/verb_dsl.rb".freeze, "lib/compony/component_mixins/resourceful.rb".freeze, "lib/compony/components/button.rb".freeze, "lib/compony/components/form.rb".freeze, "lib/compony/components/resourceful/destroy.rb".freeze, "lib/compony/components/resourceful/edit.rb".freeze, "lib/compony/components/resourceful/new.rb".freeze, "lib/compony/components/with_form.rb".freeze, "lib/compony/controller_mixin.rb".freeze, "lib/compony/engine.rb".freeze, "lib/compony/method_accessible_hash.rb".freeze, "lib/compony/model_fields/anchormodel.rb".freeze, "lib/compony/model_fields/association.rb".freeze, "lib/compony/model_fields/attachment.rb".freeze, "lib/compony/model_fields/base.rb".freeze, "lib/compony/model_fields/boolean.rb".freeze, "lib/compony/model_fields/currency.rb".freeze, "lib/compony/model_fields/date.rb".freeze, "lib/compony/model_fields/datetime.rb".freeze, "lib/compony/model_fields/decimal.rb".freeze, "lib/compony/model_fields/float.rb".freeze, "lib/compony/model_fields/integer.rb".freeze, "lib/compony/model_fields/phone.rb".freeze, "lib/compony/model_fields/rich_text.rb".freeze, "lib/compony/model_fields/string.rb".freeze, "lib/compony/model_fields/text.rb".freeze, "lib/compony/model_fields/time.rb".freeze, "lib/compony/model_mixin.rb".freeze, "lib/compony/request_context.rb".freeze, "lib/compony/version.rb".freeze, "lib/compony/view_helpers.rb".freeze, "lib/generators/component/USAGE".freeze, "lib/generators/component/component_generator.rb".freeze, "lib/generators/component/templates/component.rb.erb".freeze]
14
+ s.date = "2023-06-20"
15
+ s.files = [".gitignore".freeze, ".ruby-version".freeze, ".yardopts".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "app/controllers/compony_controller.rb".freeze, "compony.gemspec".freeze, "config/locales/de.yml".freeze, "config/locales/en.yml".freeze, "config/routes.rb".freeze, "doc/resourceful_lifecycle.graphml".freeze, "doc/resourceful_lifecycle.pdf".freeze, "lib/compony.rb".freeze, "lib/compony/component.rb".freeze, "lib/compony/component_mixins/default/labelling.rb".freeze, "lib/compony/component_mixins/default/standalone.rb".freeze, "lib/compony/component_mixins/default/standalone/resourceful_verb_dsl.rb".freeze, "lib/compony/component_mixins/default/standalone/standalone_dsl.rb".freeze, "lib/compony/component_mixins/default/standalone/verb_dsl.rb".freeze, "lib/compony/component_mixins/resourceful.rb".freeze, "lib/compony/components/button.rb".freeze, "lib/compony/components/destroy.rb".freeze, "lib/compony/components/edit.rb".freeze, "lib/compony/components/form.rb".freeze, "lib/compony/components/new.rb".freeze, "lib/compony/components/with_form.rb".freeze, "lib/compony/controller_mixin.rb".freeze, "lib/compony/engine.rb".freeze, "lib/compony/method_accessible_hash.rb".freeze, "lib/compony/model_fields/anchormodel.rb".freeze, "lib/compony/model_fields/association.rb".freeze, "lib/compony/model_fields/attachment.rb".freeze, "lib/compony/model_fields/base.rb".freeze, "lib/compony/model_fields/boolean.rb".freeze, "lib/compony/model_fields/currency.rb".freeze, "lib/compony/model_fields/date.rb".freeze, "lib/compony/model_fields/datetime.rb".freeze, "lib/compony/model_fields/decimal.rb".freeze, "lib/compony/model_fields/email.rb".freeze, "lib/compony/model_fields/float.rb".freeze, "lib/compony/model_fields/integer.rb".freeze, "lib/compony/model_fields/phone.rb".freeze, "lib/compony/model_fields/rich_text.rb".freeze, "lib/compony/model_fields/string.rb".freeze, "lib/compony/model_fields/text.rb".freeze, "lib/compony/model_fields/time.rb".freeze, "lib/compony/model_fields/url.rb".freeze, "lib/compony/model_mixin.rb".freeze, "lib/compony/request_context.rb".freeze, "lib/compony/version.rb".freeze, "lib/compony/view_helpers.rb".freeze, "lib/generators/component/USAGE".freeze, "lib/generators/component/component_generator.rb".freeze, "lib/generators/component/templates/component.rb.erb".freeze, "lib/generators/component/templates/destroy.rb.erb".freeze, "lib/generators/component/templates/edit.rb.erb".freeze, "lib/generators/component/templates/form.rb.erb".freeze, "lib/generators/component/templates/new.rb.erb".freeze, "lib/generators/components/USAGE".freeze, "lib/generators/components/components_generator.rb".freeze]
16
16
  s.required_ruby_version = Gem::Requirement.new(">= 3.0.0".freeze)
17
- s.rubygems_version = "3.5.6".freeze
17
+ s.rubygems_version = "3.4.13".freeze
18
18
  s.summary = "Needs summary".freeze
19
19
 
20
20
  s.specification_version = 4
21
21
 
22
- s.add_development_dependency(%q<yard>.freeze, [">= 0.9.28".freeze])
23
- s.add_development_dependency(%q<rubocop>.freeze, [">= 1.48".freeze])
24
- s.add_development_dependency(%q<rubocop-rails>.freeze, [">= 2.18.0".freeze])
25
- s.add_runtime_dependency(%q<request_store>.freeze, [">= 1.5".freeze])
26
- s.add_runtime_dependency(%q<dyny>.freeze, [">= 0.0.3".freeze])
27
- s.add_runtime_dependency(%q<schemacop>.freeze, [">= 3.0.17".freeze])
28
- s.add_runtime_dependency(%q<simple_form>.freeze, [">= 5.1.0".freeze])
29
- s.add_runtime_dependency(%q<dslblend>.freeze, [">= 0.0.3".freeze])
30
- s.add_runtime_dependency(%q<anchormodel>.freeze, ["~> 0.1.2".freeze])
31
- s.add_runtime_dependency(%q<cancancan>.freeze, ["~> 3.4.0".freeze])
22
+ s.add_development_dependency(%q<yard>.freeze, [">= 0.9.28"])
23
+ s.add_development_dependency(%q<rubocop>.freeze, [">= 1.48"])
24
+ s.add_development_dependency(%q<rubocop-rails>.freeze, [">= 2.18.0"])
25
+ s.add_runtime_dependency(%q<request_store>.freeze, [">= 1.5"])
26
+ s.add_runtime_dependency(%q<dyny>.freeze, [">= 0.0.3"])
27
+ s.add_runtime_dependency(%q<schemacop>.freeze, [">= 3.0.17"])
28
+ s.add_runtime_dependency(%q<simple_form>.freeze, [">= 5.1.0"])
29
+ s.add_runtime_dependency(%q<dslblend>.freeze, [">= 0.0.3"])
30
+ s.add_runtime_dependency(%q<anchormodel>.freeze, ["~> 0.1.2"])
31
+ s.add_runtime_dependency(%q<cancancan>.freeze, ["~> 3.4.0"])
32
32
  end
@@ -0,0 +1,75 @@
1
+ module Compony
2
+ module Components
3
+ # @api description
4
+ # This component is used for the Rails destroy paradigm. Asks for confirm when queried using GET.
5
+ class Destroy < Compony::Component
6
+ include Compony::ComponentMixins::Resourceful
7
+
8
+ setup do
9
+ standalone path: "#{family_name}/:id/destroy" do
10
+ verb :get do
11
+ authorize { can?(:destroy, @data) }
12
+ end
13
+
14
+ verb :delete do
15
+ authorize { can?(:destroy, @data) }
16
+ store_data # This enables the global store_data block defined below for this path and verb.
17
+ respond do
18
+ evaluate_with_backfire(&@on_destroyed_block)
19
+ end
20
+ end
21
+ end
22
+
23
+ label(:long) { |data| I18n.t('compony.components.destroy.label.long', data_label: data.label) }
24
+ label(:short) { |_| I18n.t('compony.components.destroy.label.short') }
25
+ icon { :trash }
26
+ color { :danger }
27
+
28
+ content do
29
+ div I18n.t('compony.components.destroy.confirm_question', data_label: @data.label)
30
+ div do
31
+ concat compony_button(comp_cst,
32
+ @data,
33
+ label: I18n.t('compony.components.destroy.confirm_button'),
34
+ method: :delete)
35
+ end
36
+ end
37
+
38
+ store_data do
39
+ # Validate params against the form's schema
40
+ local_data = @data # Capture data for usage in the Schemacop call
41
+ schema = Schemacop::Schema3.new :hash, additional_properties: true do
42
+ if local_data.class.primary_key_type_key == :string
43
+ str! :id
44
+ else
45
+ int! :id, cast_str: true
46
+ end
47
+ end
48
+ schema.validate!(controller.request.params)
49
+
50
+ # Perform destroy
51
+ @data.destroy!
52
+ end
53
+
54
+ on_destroyed do
55
+ flash.notice = I18n.t('compony.components.destroy.data_was_destroyed', data_label: @data.label)
56
+ redirect_to evaluate_with_backfire(&@on_destroyed_redirect_path_block), status: :see_other # 303: force GET
57
+ end
58
+
59
+ on_destroyed_redirect_path do
60
+ Compony.path(:index, family_cst)
61
+ end
62
+ end
63
+
64
+ # DSL method
65
+ def on_destroyed(&block)
66
+ @on_destroyed_block = block
67
+ end
68
+
69
+ # DSL method
70
+ def on_destroyed_redirect_path(&block)
71
+ @on_destroyed_redirect_path_block = block
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,94 @@
1
+ module Compony
2
+ module Components
3
+ # @api description
4
+ # This component is used for the Rails edit and update paradigm. Performs update when the form is submitted.
5
+ class Edit < Compony::Components::WithForm
6
+ include Compony::ComponentMixins::Resourceful
7
+ setup do
8
+ submit_verb :patch
9
+ standalone path: "#{family_name}/:id/edit" do
10
+ verb :get do
11
+ authorize { can?(:edit, @data) }
12
+ assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
13
+ end
14
+ verb submit_verb do
15
+ authorize { can?(:update, @data) }
16
+ assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
17
+ store_data # This enables the global store_data block defined below for this path and verb.
18
+ respond do
19
+ if @update_succeeded
20
+ evaluate_with_backfire(&@on_updated_block)
21
+ else
22
+ evaluate_with_backfire(&@on_update_failed_block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ label(:long) { |data| I18n.t('compony.components.edit.label.long', data_label: data.label) }
29
+ label(:short) { |_| I18n.t('compony.components.edit.label.short') }
30
+ icon { :pencil }
31
+
32
+ content do
33
+ concat form_comp.render(controller, data: @data)
34
+ end
35
+
36
+ assign_attributes do
37
+ # Validate params against the form's schema
38
+ local_form_comp = form_comp # Capture form_comp for usage in the Schemacop call
39
+ local_data = @data # Capture data for usage in the Schemacop call
40
+ schema = Schemacop::Schema3.new :hash, additional_properties: true do
41
+ if local_data.class.primary_key_type_key == :string
42
+ str! :id
43
+ else
44
+ int! :id, cast_str: true
45
+ end
46
+ hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
47
+ end
48
+ schema.validate!(controller.request.params)
49
+
50
+ # TODO: Why are we not saving the validated params?
51
+ attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
52
+ @data.assign_attributes(attrs_to_assign) if attrs_to_assign
53
+ end
54
+
55
+ store_data do
56
+ @update_succeeded = @data.save
57
+ end
58
+
59
+ on_updated do
60
+ flash.notice = I18n.t('compony.components.edit.data_was_updated', data_label: data.label)
61
+ redirect_to evaluate_with_backfire(&@on_updated_redirect_path_block)
62
+ end
63
+
64
+ on_updated_redirect_path do
65
+ if Compony.comp_class_for(:show, @data)
66
+ Compony.path(:show, @data)
67
+ else
68
+ Compony.path(:index, @data)
69
+ end
70
+ end
71
+
72
+ on_update_failed do
73
+ Rails.logger.warn(@data&.errors&.full_messages)
74
+ render_standalone(controller, status: :unprocessable_entity)
75
+ end
76
+ end
77
+
78
+ # DSL method
79
+ def on_updated(&block)
80
+ @on_updated_block = block
81
+ end
82
+
83
+ # DSL method
84
+ def on_updated_redirect_path(&block)
85
+ @on_updated_redirect_path_block = block
86
+ end
87
+
88
+ # DSL method
89
+ def on_update_failed(&block)
90
+ @on_update_failed_block = block
91
+ end
92
+ end
93
+ end
94
+ end
@@ -71,6 +71,7 @@ module Compony
71
71
  # @todo Refactor? Could this be greatly simplified by having `form_field to |f|` ?
72
72
  def with_simpleform(simpleform)
73
73
  @simpleform = simpleform
74
+ @focus_given = false
74
75
  yield
75
76
  @simpleform = nil
76
77
  end
@@ -85,8 +86,12 @@ module Compony
85
86
  fail("Field #{name.to_sym.inspect} is not defined on #{@simpleform.object.inspect} but was requested in #{inspect}.") unless model_field
86
87
 
87
88
  if hidden
88
- return @simpleform.input model_field.schema_key, as: :hidden, **input_opts
89
+ return model_field.simpleform_input_hidden(@simpleform, self, **input_opts)
89
90
  else
91
+ unless @focus_given
92
+ input_opts[:autofocus] = true unless input_opts.key? :autofocus
93
+ @focus_given = true
94
+ end
90
95
  return model_field.simpleform_input(@simpleform, self, **input_opts)
91
96
  end
92
97
  end
@@ -0,0 +1,93 @@
1
+ module Compony
2
+ module Components
3
+ # @api description
4
+ # This component is used for the Rails new and create paradigm. Performs update when the form is submitted.
5
+ class New < Compony::Components::WithForm
6
+ include Compony::ComponentMixins::Resourceful
7
+
8
+ setup do
9
+ submit_verb :post
10
+ load_data { @data = data_class.new }
11
+ standalone path: "#{family_name}/new" do
12
+ verb :get do
13
+ authorize { can?(:create, data_class) }
14
+ assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
15
+ end
16
+ verb submit_verb do
17
+ authorize { can?(:create, data_class) }
18
+ assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
19
+ store_data # This enables the global store_data block defined below for this path and verb.
20
+ respond do
21
+ if @create_succeeded
22
+ evaluate_with_backfire(&@on_created_block)
23
+ else
24
+ evaluate_with_backfire(&@on_create_failed_block)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ label(:long) { I18n.t('compony.components.new.label.long', data_class: data_class.model_name.human) }
31
+ label(:short) { I18n.t('compony.components.new.label.short') }
32
+ icon { :plus }
33
+
34
+ add_content do
35
+ h2 component.label
36
+ end
37
+ add_content do
38
+ concat form_comp.render(controller, data: @data)
39
+ end
40
+
41
+ assign_attributes do
42
+ local_form_comp = form_comp # Capture form_comp for usage in the Schemacop call
43
+ local_data = @data # Capture data for usage in the Schemacop call
44
+ schema = Schemacop::Schema3.new :hash, additional_properties: true do
45
+ hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
46
+ end
47
+ schema.validate!(controller.request.params)
48
+
49
+ # TODO: Why are we not saving the validated params?
50
+ attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
51
+ @data.assign_attributes(attrs_to_assign) if attrs_to_assign
52
+ end
53
+
54
+ store_data do
55
+ @create_succeeded = @data.save
56
+ end
57
+
58
+ on_created do
59
+ flash.notice = I18n.t('compony.components.new.data_was_created', data_label: data.label)
60
+ redirect_to evaluate_with_backfire(&@on_created_redirect_path_block)
61
+ end
62
+
63
+ on_created_redirect_path do
64
+ if Compony.comp_class_for(:show, @data)
65
+ Compony.path(:show, @data)
66
+ else
67
+ Compony.path(:index, @data)
68
+ end
69
+ end
70
+
71
+ on_create_failed do
72
+ Rails.logger.warn(@data&.errors&.full_messages)
73
+ render_standalone(controller, status: :unprocessable_entity)
74
+ end
75
+ end
76
+
77
+ # DSL method
78
+ def on_created(&block)
79
+ @on_created_block = block
80
+ end
81
+
82
+ # DSL method
83
+ def on_created_redirect_path(&block)
84
+ @on_created_redirect_path_block = block
85
+ end
86
+
87
+ # DSL method
88
+ def on_create_failed(&block)
89
+ @on_create_failed_block = block
90
+ end
91
+ end
92
+ end
93
+ end
@@ -23,6 +23,13 @@ module Compony
23
23
  }.merge(input_opts)
24
24
  return form.input @name, **opts
25
25
  end
26
+
27
+ def simpleform_input_hidden(form, _component, **input_opts)
28
+ selected_cst = form.object.send(@name)
29
+ input_opts[:input_html] ||= {}
30
+ input_opts[:input_html][:value] = selected_cst.is_a?(::Anchormodel) ? selected_cst.key : selected_cst
31
+ return form.input @name, as: :hidden, **input_opts
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -46,6 +46,12 @@ module Compony
46
46
  return form.input @name, **input_opts
47
47
  end
48
48
 
49
+ # Used in form helper
50
+ # Given a simpleform instance, returns a suitable hidden input for thetype
51
+ def simpleform_input_hidden(form, _component, **input_opts)
52
+ return form.input @name, as: :hidden, **input_opts
53
+ end
54
+
49
55
  protected
50
56
 
51
57
  # If given a scalar, calls the block on the scalar. If given a list, calls the block on every member and joins the result with ",".
@@ -0,0 +1,13 @@
1
+ module Compony
2
+ module ModelFields
3
+ class Email < Base
4
+ def value_for(data, controller: nil, **_)
5
+ return transform_and_join(data.send(@name), controller:) do |el|
6
+ fail('Must pass controller to generate the link to the email.') unless controller
7
+ return nil unless el
8
+ return controller.helpers.mail_to(el)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Compony
2
+ module ModelFields
3
+ class Url < Base
4
+ def value_for(data, controller: nil, **_)
5
+ return transform_and_join(data.send(@name), controller:) do |el|
6
+ fail('Must pass controller to generate the link to the link.') unless controller
7
+ return nil unless el
8
+ return controller.helpers.link_to(el, el, target: '_blank', rel: 'noopener')
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -11,6 +11,13 @@ module Compony
11
11
  end
12
12
 
13
13
  class_methods do
14
+ # This hook updates all fields from a subclass, making sure that fields point to correct model classes even in STI
15
+ # e.g. in Parent: field :foo, ... omitted in child -> child.fields[:foo] should point to Child and not Parent.
16
+ def inherited(subclass)
17
+ super
18
+ subclass.fields = subclass.fields.transform_values { |f| f.class.new(f.name, subclass, **f.extra_attrs) }
19
+ end
20
+
14
21
  # DSL method, defines a new field which will be translated and can be added to field groups
15
22
  # For virtual attributes, you must pass a type explicitely, otherwise it's auto-infered.
16
23
  def field(name, type, **extra_attrs)
@@ -2,7 +2,7 @@ module Compony
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- PATCH = 3
5
+ PATCH = 5
6
6
 
7
7
  EDGE = false
8
8
 
data/lib/compony.rb CHANGED
@@ -247,6 +247,7 @@ require 'compony/model_fields/currency'
247
247
  require 'compony/model_fields/date'
248
248
  require 'compony/model_fields/datetime'
249
249
  require 'compony/model_fields/decimal'
250
+ require 'compony/model_fields/email'
250
251
  require 'compony/model_fields/float'
251
252
  require 'compony/model_fields/integer'
252
253
  require 'compony/model_fields/phone'
@@ -254,6 +255,7 @@ require 'compony/model_fields/rich_text'
254
255
  require 'compony/model_fields/string'
255
256
  require 'compony/model_fields/text'
256
257
  require 'compony/model_fields/time'
258
+ require 'compony/model_fields/url'
257
259
  require 'compony/component_mixins/default/standalone'
258
260
  require 'compony/component_mixins/default/standalone/standalone_dsl'
259
261
  require 'compony/component_mixins/default/standalone/verb_dsl'
@@ -264,9 +266,9 @@ require 'compony/component'
264
266
  require 'compony/components/button'
265
267
  require 'compony/components/form'
266
268
  require 'compony/components/with_form'
267
- require 'compony/components/resourceful/new'
268
- require 'compony/components/resourceful/edit'
269
- require 'compony/components/resourceful/destroy'
269
+ require 'compony/components/new'
270
+ require 'compony/components/edit'
271
+ require 'compony/components/destroy'
270
272
  require 'compony/method_accessible_hash'
271
273
  require 'compony/model_mixin'
272
274
  require 'compony/request_context'
@@ -3,12 +3,23 @@ class ComponentGenerator < Rails::Generators::NamedBase
3
3
 
4
4
  def add_component
5
5
  segments = name.underscore.split('/')
6
- fail('NAME must be of the form Family::ComponentName or family/component_name') if segments.size != 2
6
+ fail("NAME must be of the form Family::ComponentName or family/component_name but got #{name.inspect}") if segments.size != 2
7
7
  @family, @comp = segments
8
8
  @family = @family.pluralize # Force plural
9
9
  @family_cst = @family.camelize.pluralize # Force plural
10
10
  @comp_cst = @comp.camelize # Tolerate singular and plural
11
11
 
12
- template 'component.rb.erb', "app/components/#{@family}/#{@comp}.rb"
12
+ case @comp_cst
13
+ when 'Destroy'
14
+ template 'destroy.rb.erb', "app/components/#{@family}/#{@comp}.rb"
15
+ when 'Edit'
16
+ template 'edit.rb.erb', "app/components/#{@family}/#{@comp}.rb"
17
+ when 'Form'
18
+ template 'form.rb.erb', "app/components/#{@family}/#{@comp}.rb"
19
+ when 'New'
20
+ template 'new.rb.erb', "app/components/#{@family}/#{@comp}.rb"
21
+ else
22
+ template 'component.rb.erb', "app/components/#{@family}/#{@comp}.rb"
23
+ end
13
24
  end
14
25
  end
@@ -1,4 +1,4 @@
1
1
  class Components::<%= @family_cst %>::<%= @comp_cst %> < Compony::Component
2
2
  setup do
3
3
  end
4
- end
4
+ end
@@ -0,0 +1,2 @@
1
+ class Components::<%= @family_cst %>::<%= @comp_cst %> < Compony::Components::Destroy
2
+ end
@@ -0,0 +1,2 @@
1
+ class Components::<%= @family_cst %>::<%= @comp_cst %> < Compony::Components::Edit
2
+ end
@@ -0,0 +1,8 @@
1
+ class Components::<%= @family_cst %>::<%= @comp_cst %> < Compony::Components::Form
2
+ setup do
3
+ form_fields do
4
+ end
5
+
6
+ schema_fields
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ class Components::<%= @family_cst %>::<%= @comp_cst %> < Compony::Components::New
2
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generate the most used components for a family
3
+
4
+ Example:
5
+ bin/rails generate components Users
6
+
7
+ This will create and fill with the usual components:
8
+ app/components/users/
@@ -0,0 +1,12 @@
1
+ class ComponentsGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path('templates', __dir__)
3
+
4
+ def add_component
5
+ @family = name.underscore.pluralize # Force plural
6
+ @family_cst = @family.camelize.pluralize # Force plural
7
+
8
+ %w[Destroy Edit Form New].each do |comp_cst|
9
+ generate "component #{name}::#{comp_cst}"
10
+ end
11
+ end
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: compony
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Kalbermatter
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-03-25 00:00:00.000000000 Z
12
+ date: 2023-06-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: yard
@@ -182,10 +182,10 @@ files:
182
182
  - lib/compony/component_mixins/default/standalone/verb_dsl.rb
183
183
  - lib/compony/component_mixins/resourceful.rb
184
184
  - lib/compony/components/button.rb
185
+ - lib/compony/components/destroy.rb
186
+ - lib/compony/components/edit.rb
185
187
  - lib/compony/components/form.rb
186
- - lib/compony/components/resourceful/destroy.rb
187
- - lib/compony/components/resourceful/edit.rb
188
- - lib/compony/components/resourceful/new.rb
188
+ - lib/compony/components/new.rb
189
189
  - lib/compony/components/with_form.rb
190
190
  - lib/compony/controller_mixin.rb
191
191
  - lib/compony/engine.rb
@@ -199,6 +199,7 @@ files:
199
199
  - lib/compony/model_fields/date.rb
200
200
  - lib/compony/model_fields/datetime.rb
201
201
  - lib/compony/model_fields/decimal.rb
202
+ - lib/compony/model_fields/email.rb
202
203
  - lib/compony/model_fields/float.rb
203
204
  - lib/compony/model_fields/integer.rb
204
205
  - lib/compony/model_fields/phone.rb
@@ -206,6 +207,7 @@ files:
206
207
  - lib/compony/model_fields/string.rb
207
208
  - lib/compony/model_fields/text.rb
208
209
  - lib/compony/model_fields/time.rb
210
+ - lib/compony/model_fields/url.rb
209
211
  - lib/compony/model_mixin.rb
210
212
  - lib/compony/request_context.rb
211
213
  - lib/compony/version.rb
@@ -213,6 +215,12 @@ files:
213
215
  - lib/generators/component/USAGE
214
216
  - lib/generators/component/component_generator.rb
215
217
  - lib/generators/component/templates/component.rb.erb
218
+ - lib/generators/component/templates/destroy.rb.erb
219
+ - lib/generators/component/templates/edit.rb.erb
220
+ - lib/generators/component/templates/form.rb.erb
221
+ - lib/generators/component/templates/new.rb.erb
222
+ - lib/generators/components/USAGE
223
+ - lib/generators/components/components_generator.rb
216
224
  homepage:
217
225
  licenses: []
218
226
  metadata: {}
@@ -1,77 +0,0 @@
1
- module Compony
2
- module Components
3
- module Resourceful
4
- # @api description
5
- # This component is used for the Rails destroy paradigm. Asks for confirm when queried using GET.
6
- class Destroy < Compony::Component
7
- include Compony::ComponentMixins::Resourceful
8
-
9
- setup do
10
- standalone path: "#{family_name}/:id/destroy" do
11
- verb :get do
12
- authorize { can?(:destroy, @data) }
13
- end
14
-
15
- verb :delete do
16
- authorize { can?(:destroy, @data) }
17
- store_data # This enables the global store_data block defined below for this path and verb.
18
- respond do
19
- evaluate_with_backfire(&@on_destroyed_block)
20
- end
21
- end
22
- end
23
-
24
- label(:long) { |data| I18n.t('compony.components.destroy.label.long', data_label: data.label) }
25
- label(:short) { |_| I18n.t('compony.components.destroy.label.short') }
26
- icon { :trash }
27
- color { :danger }
28
-
29
- content do
30
- div I18n.t('compony.components.destroy.confirm_question', data_label: @data.label)
31
- div do
32
- concat compony_button(comp_cst,
33
- @data,
34
- label: I18n.t('compony.components.destroy.confirm_button'),
35
- method: :delete)
36
- end
37
- end
38
-
39
- store_data do
40
- # Validate params against the form's schema
41
- local_data = @data # Capture data for usage in the Schemacop call
42
- schema = Schemacop::Schema3.new :hash, additional_properties: true do
43
- if local_data.class.primary_key_type_key == :string
44
- str! :id
45
- else
46
- int! :id, cast_str: true
47
- end
48
- end
49
- schema.validate!(controller.request.params)
50
-
51
- # Perform destroy
52
- @data.destroy!
53
- end
54
-
55
- on_destroyed do
56
- flash.notice = I18n.t('compony.components.destroy.data_was_destroyed', data_label: @data.label)
57
- redirect_to evaluate_with_backfire(&@on_destroyed_redirect_path_block), status: :see_other # 303: force GET
58
- end
59
-
60
- on_destroyed_redirect_path do
61
- Compony.path(:index, family_cst)
62
- end
63
- end
64
-
65
- # DSL method
66
- def on_destroyed(&block)
67
- @on_destroyed_block = block
68
- end
69
-
70
- # DSL method
71
- def on_destroyed_redirect_path(&block)
72
- @on_destroyed_redirect_path_block = block
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,96 +0,0 @@
1
- module Compony
2
- module Components
3
- module Resourceful
4
- # @api description
5
- # This component is used for the Rails edit and update paradigm. Performs update when the form is submitted.
6
- class Edit < Compony::Components::WithForm
7
- include Compony::ComponentMixins::Resourceful
8
- setup do
9
- submit_verb :patch
10
- standalone path: "#{family_name}/:id/edit" do
11
- verb :get do
12
- authorize { can?(:edit, @data) }
13
- assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
14
- end
15
- verb submit_verb do
16
- authorize { can?(:update, @data) }
17
- assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
18
- store_data # This enables the global store_data block defined below for this path and verb.
19
- respond do
20
- if @update_succeeded
21
- evaluate_with_backfire(&@on_updated_block)
22
- else
23
- evaluate_with_backfire(&@on_update_failed_block)
24
- end
25
- end
26
- end
27
- end
28
-
29
- label(:long) { |data| I18n.t('compony.components.edit.label.long', data_label: data.label) }
30
- label(:short) { |_| I18n.t('compony.components.edit.label.short') }
31
- icon { :pencil }
32
-
33
- content do
34
- concat form_comp.render(controller, data: @data)
35
- end
36
-
37
- assign_attributes do
38
- # Validate params against the form's schema
39
- local_form_comp = form_comp # Capture form_comp for usage in the Schemacop call
40
- local_data = @data # Capture data for usage in the Schemacop call
41
- schema = Schemacop::Schema3.new :hash, additional_properties: true do
42
- if local_data.class.primary_key_type_key == :string
43
- str! :id
44
- else
45
- int! :id, cast_str: true
46
- end
47
- hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
48
- end
49
- schema.validate!(controller.request.params)
50
-
51
- # TODO: Why are we not saving the validated params?
52
- attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
53
- @data.assign_attributes(attrs_to_assign) if attrs_to_assign
54
- end
55
-
56
- store_data do
57
- @update_succeeded = @data.save
58
- end
59
-
60
- on_updated do
61
- flash.notice = I18n.t('compony.components.edit.data_was_updated', data_label: data.label)
62
- redirect_to evaluate_with_backfire(&@on_updated_redirect_path_block)
63
- end
64
-
65
- on_updated_redirect_path do
66
- if Compony.comp_class_for(:show, @data)
67
- Compony.path(:show, @data)
68
- else
69
- Compony.path(:index, @data)
70
- end
71
- end
72
-
73
- on_update_failed do
74
- Rails.logger.warn(@data&.errors&.full_messages)
75
- render_standalone(controller, status: :unprocessable_entity)
76
- end
77
- end
78
-
79
- # DSL method
80
- def on_updated(&block)
81
- @on_updated_block = block
82
- end
83
-
84
- # DSL method
85
- def on_updated_redirect_path(&block)
86
- @on_updated_redirect_path_block = block
87
- end
88
-
89
- # DSL method
90
- def on_update_failed(&block)
91
- @on_update_failed_block = block
92
- end
93
- end
94
- end
95
- end
96
- end
@@ -1,95 +0,0 @@
1
- module Compony
2
- module Components
3
- module Resourceful
4
- # @api description
5
- # This component is used for the Rails new and create paradigm. Performs update when the form is submitted.
6
- class New < Compony::Components::WithForm
7
- include Compony::ComponentMixins::Resourceful
8
-
9
- setup do
10
- submit_verb :post
11
- load_data { @data = data_class.new }
12
- standalone path: "#{family_name}/new" do
13
- verb :get do
14
- authorize { can?(:create, data_class) }
15
- assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
16
- end
17
- verb submit_verb do
18
- authorize { can?(:create, data_class) }
19
- assign_attributes # This enables the global assign_attributes block defined below for this path and verb.
20
- store_data # This enables the global store_data block defined below for this path and verb.
21
- respond do
22
- if @create_succeeded
23
- evaluate_with_backfire(&@on_created_block)
24
- else
25
- evaluate_with_backfire(&@on_create_failed_block)
26
- end
27
- end
28
- end
29
- end
30
-
31
- label(:long) { I18n.t('compony.components.new.label.long', data_class: data_class.model_name.human) }
32
- label(:short) { I18n.t('compony.components.new.label.short') }
33
- icon { :plus }
34
-
35
- add_content do
36
- h2 component.label
37
- end
38
- add_content do
39
- concat form_comp.render(controller, data: @data)
40
- end
41
-
42
- assign_attributes do
43
- local_form_comp = form_comp # Capture form_comp for usage in the Schemacop call
44
- local_data = @data # Capture data for usage in the Schemacop call
45
- schema = Schemacop::Schema3.new :hash, additional_properties: true do
46
- hsh? local_form_comp.schema_wrapper_key_for(local_data), &local_form_comp.schema_block_for(local_data)
47
- end
48
- schema.validate!(controller.request.params)
49
-
50
- # TODO: Why are we not saving the validated params?
51
- attrs_to_assign = controller.request.params[form_comp.schema_wrapper_key_for(@data)]
52
- @data.assign_attributes(attrs_to_assign) if attrs_to_assign
53
- end
54
-
55
- store_data do
56
- @create_succeeded = @data.save
57
- end
58
-
59
- on_created do
60
- flash.notice = I18n.t('compony.components.new.data_was_created', data_label: data.label)
61
- redirect_to evaluate_with_backfire(&@on_created_redirect_path_block)
62
- end
63
-
64
- on_created_redirect_path do
65
- if Compony.comp_class_for(:show, @data)
66
- Compony.path(:show, @data)
67
- else
68
- Compony.path(:index, @data)
69
- end
70
- end
71
-
72
- on_create_failed do
73
- Rails.logger.warn(@data&.errors&.full_messages)
74
- render_standalone(controller, status: :unprocessable_entity)
75
- end
76
- end
77
-
78
- # DSL method
79
- def on_created(&block)
80
- @on_created_block = block
81
- end
82
-
83
- # DSL method
84
- def on_created_redirect_path(&block)
85
- @on_created_redirect_path_block = block
86
- end
87
-
88
- # DSL method
89
- def on_create_failed(&block)
90
- @on_create_failed_block = block
91
- end
92
- end
93
- end
94
- end
95
- end