super 0.0.7 → 0.0.8

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +56 -0
  3. data/README.md +6 -0
  4. data/STABILITY.md +50 -0
  5. data/app/assets/javascripts/super/application.js +12 -7
  6. data/app/controllers/super/application_controller.rb +1 -1
  7. data/app/views/layouts/super/application.html.erb +14 -6
  8. data/app/views/super/application/_super_panel.html.erb +1 -1
  9. data/app/views/super/application/_super_schema_display_index.html.erb +4 -4
  10. data/app/views/super/application/_super_schema_display_show.html.erb +3 -3
  11. data/app/views/super/application/_super_schema_form.html.erb +1 -1
  12. data/frontend/super-frontend/dist/application.js +12 -7
  13. data/frontend/super-frontend/src/javascripts/super/application.ts +5 -8
  14. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  15. data/lib/generators/super/webpacker/webpacker_generator.rb +3 -2
  16. data/lib/super.rb +11 -0
  17. data/lib/super/assets.rb +108 -38
  18. data/lib/super/configuration.rb +18 -73
  19. data/lib/super/controls.rb +2 -25
  20. data/lib/super/controls/optional.rb +41 -16
  21. data/lib/super/controls/required.rb +1 -29
  22. data/lib/super/controls/steps.rb +9 -23
  23. data/lib/super/display.rb +72 -0
  24. data/lib/super/display/guesser.rb +34 -0
  25. data/lib/super/display/schema_types.rb +19 -62
  26. data/lib/super/engine.rb +1 -1
  27. data/lib/super/filter.rb +5 -130
  28. data/lib/super/filter/form_object.rb +97 -0
  29. data/lib/super/filter/guesser.rb +30 -0
  30. data/lib/super/filter/plugin.rb +47 -0
  31. data/lib/super/filter/schema_types.rb +1 -7
  32. data/lib/super/form.rb +27 -40
  33. data/lib/super/form/builder.rb +48 -0
  34. data/lib/super/form/guesser.rb +27 -0
  35. data/lib/super/form/schema_types.rb +19 -29
  36. data/lib/super/form/strong_params.rb +29 -0
  37. data/lib/super/pagination.rb +10 -16
  38. data/lib/super/schema.rb +0 -25
  39. data/lib/super/schema/common.rb +25 -0
  40. data/lib/super/schema/guesser.rb +77 -0
  41. data/lib/super/version.rb +1 -1
  42. metadata +14 -2
@@ -1,48 +1,35 @@
1
1
  module Super
2
+ # This schema type is used on your +#edit+ and +#new+ forms
3
+ #
4
+ # ```ruby
5
+ # class MembersController::Controls
6
+ # # ...
7
+ #
8
+ # def new_schema
9
+ # Super::Form.new do |fields, type|
10
+ # fields[:name] = type.string
11
+ # fields[:rank] = type.select
12
+ # fields[:position] = type.string
13
+ # fields[:ship_id] = type.select(
14
+ # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
15
+ # )
16
+ # end
17
+ # end
18
+ #
19
+ # # ...
20
+ # end
21
+ # ```
2
22
  class Form
3
- module FieldErrorProc
4
- def error_wrapping(html_tag)
5
- if Thread.current[:super_form_builder]
6
- return html_tag
7
- end
23
+ include Schema::Common
8
24
 
9
- super
10
- end
25
+ def initialize
26
+ @fields = Schema::Fields.new
27
+ @schema_types = SchemaTypes.new(fields: @fields)
28
+ yield(@fields, @schema_types)
11
29
  end
12
30
 
13
- class Builder < ActionView::Helpers::FormBuilder
14
- # These methods were originally defined in the following files
15
- #
16
- # * actionview/lib/action_view/helpers/form_helper.rb
17
- # * actionview/lib/action_view/helpers/form_options_helper.rb
18
- # * actionview/lib/action_view/helpers/date_helper.rb
19
- %w[
20
- label text_field password_field hidden_field file_field text_area
21
- check_box radio_button color_field search_field telephone_field
22
- date_field time_field datetime_field month_field week_field url_field
23
- email_field number_field range_field
24
-
25
- select collection_select grouped_collection_select time_zone_select
26
- collection_radio_buttons collection_check_boxes
27
-
28
- time_select datetime_select date_select
29
- ].each do |field_type_method|
30
- class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
31
- def #{field_type_method}(*)
32
- Thread.current[:super_form_builder] = true
33
- super
34
- ensure
35
- Thread.current[:super_form_builder] = nil
36
- end
37
- RUBY
38
- end
39
-
40
- alias datetime_local_field datetime_field
41
- alias phone_field telephone_field
31
+ def to_partial_path
32
+ "super_schema_form"
42
33
  end
43
34
  end
44
35
  end
45
-
46
- ActionView::Helpers::Tags::Base.class_eval do
47
- prepend Super::Form::FieldErrorProc
48
- end
@@ -0,0 +1,48 @@
1
+ module Super
2
+ class Form
3
+ module FieldErrorProc
4
+ def error_wrapping(html_tag)
5
+ if Thread.current[:super_form_builder]
6
+ return html_tag
7
+ end
8
+
9
+ super
10
+ end
11
+ end
12
+
13
+ class Builder < ActionView::Helpers::FormBuilder
14
+ # These methods were originally defined in the following files
15
+ #
16
+ # * actionview/lib/action_view/helpers/form_helper.rb
17
+ # * actionview/lib/action_view/helpers/form_options_helper.rb
18
+ # * actionview/lib/action_view/helpers/date_helper.rb
19
+ %w[
20
+ label text_field password_field hidden_field file_field text_area
21
+ check_box radio_button color_field search_field telephone_field
22
+ date_field time_field datetime_field month_field week_field url_field
23
+ email_field number_field range_field
24
+
25
+ select collection_select grouped_collection_select time_zone_select
26
+ collection_radio_buttons collection_check_boxes
27
+
28
+ time_select datetime_select date_select
29
+ ].each do |field_type_method|
30
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
31
+ def #{field_type_method}(*)
32
+ Thread.current[:super_form_builder] = true
33
+ super
34
+ ensure
35
+ Thread.current[:super_form_builder] = nil
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ alias datetime_local_field datetime_field
41
+ alias phone_field telephone_field
42
+ end
43
+ end
44
+ end
45
+
46
+ ActionView::Helpers::Tags::Base.class_eval do
47
+ prepend Super::Form::FieldErrorProc
48
+ end
@@ -0,0 +1,27 @@
1
+ module Super
2
+ class Form
3
+ class Guesser
4
+ def initialize(model:, fields:, type:)
5
+ @model = model
6
+ @fields = fields
7
+ @type = type
8
+ end
9
+
10
+ def call
11
+ Schema::Guesser
12
+ .new(model: @model, fields: @fields, type: @type)
13
+ .assign_type { |attribute_name| attribute_type_for(attribute_name) }
14
+ .reject { |attribute_name| attribute_name == "id" }
15
+ .reject { |attribute_name| attribute_name == "created_at" }
16
+ .reject { |attribute_name| attribute_name == "updated_at" }
17
+ .call
18
+ end
19
+
20
+ private
21
+
22
+ def attribute_type_for(attribute_name)
23
+ @type.string
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,26 +1,5 @@
1
1
  module Super
2
2
  class Form
3
- # This schema type is used on your +#edit+ and +#new+ forms
4
- #
5
- # ```ruby
6
- # class MembersController::Controls
7
- # # ...
8
- #
9
- # def new_schema
10
- # Super::Schema.new(Super::Form::SchemaTypes.new) do |fields, type|
11
- # fields[:name] = type.generic("form_field_text")
12
- # fields[:rank] = type.generic("form_field_select", collection: Member.ranks.keys)
13
- # fields[:position] = type.generic("form_field_text")
14
- # fields[:ship_id] = type.generic(
15
- # "form_field_select",
16
- # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
17
- # )
18
- # end
19
- # end
20
- #
21
- # # ...
22
- # end
23
- # ```
24
3
  class SchemaTypes
25
4
  class Generic
26
5
  def initialize(partial_path:, extras:, nested:)
@@ -31,6 +10,16 @@ module Super
31
10
 
32
11
  attr_reader :nested_fields
33
12
 
13
+ def each_attribute
14
+ if block_given?
15
+ @nested_fields.each do |key, value|
16
+ yield(key, value)
17
+ end
18
+ end
19
+
20
+ enum_for(:each_attribute)
21
+ end
22
+
34
23
  # This takes advantage of a feature of Rails. If the value of
35
24
  # `#to_partial_path` is `my_form_field`, Rails renders
36
25
  # `app/views/super/application/_my_form_field.html.erb`, and this
@@ -69,17 +58,22 @@ module Super
69
58
  end
70
59
  end
71
60
 
72
- def before_yield(fields:)
61
+ def initialize(fields:)
73
62
  @fields = fields
74
63
  end
75
64
 
76
- def after_yield
77
- end
78
-
79
65
  def generic(partial_path, **extras)
80
66
  Generic.new(partial_path: partial_path, extras: extras, nested: {})
81
67
  end
82
68
 
69
+ def select(**extras)
70
+ Generic.new(partial_path: "form_field_select", extras: extras, nested: {})
71
+ end
72
+
73
+ def string(**extras)
74
+ Generic.new(partial_path: "form_field_text", extras: extras, nested: {})
75
+ end
76
+
83
77
  def has_many(reader, **extras)
84
78
  nested = @fields.nested do
85
79
  yield
@@ -113,10 +107,6 @@ module Super
113
107
  nested: {}
114
108
  )
115
109
  end
116
-
117
- def to_partial_path
118
- "super_schema_form"
119
- end
120
110
  end
121
111
  end
122
112
  end
@@ -0,0 +1,29 @@
1
+ module Super
2
+ class Form
3
+ class StrongParams
4
+ def initialize(form_schema)
5
+ @form_schema = form_schema
6
+ end
7
+
8
+ def require(model)
9
+ model.model_name.singular
10
+ end
11
+
12
+ def permit
13
+ unfurl(@form_schema)
14
+ end
15
+
16
+ private
17
+
18
+ def unfurl(responds_to_each_attribute)
19
+ responds_to_each_attribute.each_attribute.map do |name, type|
20
+ if type.nested_fields&.any?
21
+ { name => [:id, *unfurl(type)] }
22
+ else
23
+ name
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -89,9 +89,7 @@ module Super
89
89
  # @param query_params [Hash]
90
90
  # @return [ActiveRecord::Relation]
91
91
  def records_per_page(action:, query_params:)
92
- default_for(:records_per_page, action: action, query_params: query_params) do
93
- Super.configuration.index_records_per_page
94
- end
92
+ Super.configuration.index_records_per_page
95
93
  end
96
94
  end
97
95
 
@@ -103,14 +101,12 @@ module Super
103
101
  # @param query_params [Hash]
104
102
  # @return [Pagination]
105
103
  def initialize_pagination(action:, records:, query_params:)
106
- default_for(:initialize_pagination, action: action, records: records, query_params: query_params) do
107
- Pagination.new(
108
- total_count: records.size,
109
- limit: records_per_page(action: action, query_params: query_params),
110
- query_params: query_params,
111
- page_query_param: :page
112
- )
113
- end
104
+ Pagination.new(
105
+ total_count: records.size,
106
+ limit: records_per_page(action: action, query_params: query_params),
107
+ query_params: query_params,
108
+ page_query_param: :page
109
+ )
114
110
  end
115
111
 
116
112
  # Paginates
@@ -120,11 +116,9 @@ module Super
120
116
  # @param pagination [Pagination]
121
117
  # @return [ActiveRecord::Relation]
122
118
  def paginate_records(action:, records:, pagination:)
123
- default_for(:paginate_records, action: action, records: records, pagination: pagination) do
124
- records
125
- .limit(pagination.limit)
126
- .offset(pagination.offset)
127
- end
119
+ records
120
+ .limit(pagination.limit)
121
+ .offset(pagination.offset)
128
122
  end
129
123
  end
130
124
  end
@@ -5,31 +5,6 @@ module Super
5
5
  #
6
6
  # The various "schema types" are likely of more interest
7
7
  class Schema
8
- # @param schema_type [Display::SchemaTypes, Form::SchemaTypes]
9
- def initialize(schema_type)
10
- @schema_type = schema_type
11
- @fields = Fields.new
12
-
13
- @schema_type.before_yield(fields: @fields)
14
-
15
- if block_given?
16
- yield(@fields, @schema_type)
17
- end
18
-
19
- @schema_type.after_yield
20
- end
21
-
22
- attr_reader :fields
23
- attr_reader :schema_type
24
-
25
- def field_keys
26
- fields.keys
27
- end
28
-
29
- def to_partial_path
30
- @schema_type.to_partial_path
31
- end
32
-
33
8
  # This class can be thought of as a Hash, where the keys usually refer to
34
9
  # the model's column name and the value refers to the column type. Note
35
10
  # though that this isn't always the case—different `SchemaTypes` can do
@@ -0,0 +1,25 @@
1
+ module Super
2
+ class Schema
3
+ module Common
4
+ def each_attribute_name
5
+ if block_given?
6
+ @fields.keys.each do |key|
7
+ yield(key)
8
+ end
9
+ end
10
+
11
+ enum_for(:each_attribute_name)
12
+ end
13
+
14
+ def each_attribute
15
+ if block_given?
16
+ @fields.each do |key, value|
17
+ yield(key, value)
18
+ end
19
+ end
20
+
21
+ enum_for(:each_attribute)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,77 @@
1
+ module Super
2
+ class Schema
3
+ class Guesser
4
+ def initialize(model:, fields:, type:)
5
+ @model = model
6
+ @fields = fields
7
+ @type = type
8
+ @rejects = []
9
+ end
10
+
11
+ def limit
12
+ @limit = yield
13
+ self
14
+ end
15
+
16
+ def reject(&block)
17
+ @rejects.push(block)
18
+ self
19
+ end
20
+
21
+ def ignore_foreign_keys
22
+ @rejects.push(-> (attribute_name) { is_foreign_key[attribute_name] })
23
+ self
24
+ end
25
+
26
+ def assign_type(&block)
27
+ @assign_type = block
28
+ self
29
+ end
30
+
31
+ def call
32
+ result = sorted_attribute_names
33
+ result = @rejects.reduce(result) do |intermediary_result, rejection_proc|
34
+ intermediary_result.reject(&rejection_proc)
35
+ end
36
+
37
+ result = result.first(@limit) if @limit && @limit != Float::INFINITY
38
+
39
+ result.each do |name|
40
+ @fields[name] = @assign_type.call(name)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def sorted_attribute_names
47
+ @model
48
+ .attribute_names
49
+ .sort_by { |name| sort_weight[name] }
50
+ end
51
+
52
+ def sort_weight
53
+ @sort_weight ||= Hash.new do |hash, name|
54
+ hash[name] =
55
+ case name
56
+ when "id" then 0
57
+ when "title" then 1000
58
+ when "name" then 1300
59
+ when "created_at" then 9500
60
+ when "updated_at" then 9750
61
+ else
62
+ 2000 + hash.size
63
+ end
64
+ end
65
+ end
66
+
67
+ def is_foreign_key
68
+ @is_foreign_key ||=
69
+ @model
70
+ .reflect_on_all_associations
71
+ .select { |a| a.macro == :belongs_to }
72
+ .map { |a| [a.foreign_key, true] }
73
+ .to_h
74
+ end
75
+ end
76
+ end
77
+ end