super 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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