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,33 +1,15 @@
1
1
  module Super
2
2
  class Display
3
- # This schema type is meant to be used for +#index+ or +#show+ actions to
4
- # transform database fields into something that is human friendly.
5
- #
6
- # ```
7
- # class MembersController::Controls
8
- # # ...
9
- #
10
- # def show_schema
11
- # Super::Schema.new(Super::Display::SchemaTypes.new) do |fields, type|
12
- # fields[:name] = type.dynamic { |name| name }
13
- # fields[:rank] = type.dynamic { |rank| rank }
14
- # fields[:position] = type.dynamic { |position| position }
15
- # fields[:ship] = type.dynamic { |ship| "#{ship.name} (Ship ##{ship.id})" }
16
- # fields[:created_at] = type.dynamic { |created_at| created_at.iso8601 }
17
- # fields[:updated_at] = type.dynamic { |updated_at| updated_at.iso8601 }
18
- # end
19
- # end
20
- #
21
- # # ...
22
- # end
23
- # ```
24
3
  class SchemaTypes
25
4
  class Dynamic
26
- def initialize(transform_block)
5
+ def initialize(ignore_nil: true, &transform_block)
27
6
  @transform_block = transform_block
7
+ @ignore_nil = ignore_nil
28
8
  end
29
9
 
30
10
  def present(value)
11
+ return nil if value.nil? && @ignore_nil
12
+
31
13
  @transform_block.call(value)
32
14
  end
33
15
 
@@ -51,23 +33,27 @@ module Super
51
33
  end
52
34
  end
53
35
 
54
- def initialize(action_inquirer)
55
- @action_inquirer = action_inquirer
36
+ def initialize(fields:)
56
37
  @actions_called = false
38
+ @fields = fields
57
39
  end
58
40
 
59
- def before_yield(fields:)
60
- @fields = fields
41
+ def string
42
+ Dynamic.new(&:to_s)
43
+ end
44
+
45
+ alias text string
46
+
47
+ def timestamp
48
+ Dynamic.new(&:iso8601)
61
49
  end
62
50
 
63
- def after_yield
64
- return if !@action_inquirer.index?
65
- return if @actions_called
66
- @fields[:actions] = actions
51
+ def manual(&transform_block)
52
+ Dynamic.new(&transform_block)
67
53
  end
68
54
 
69
55
  def dynamic(&transform_block)
70
- Dynamic.new(transform_block)
56
+ Dynamic.new(&transform_block)
71
57
  end
72
58
 
73
59
  def actions
@@ -75,38 +61,9 @@ module Super
75
61
  Bypass.new(partial: "super_schema_display_actions", real: false)
76
62
  end
77
63
 
78
- def to_partial_path
79
- if @action_inquirer.index?
80
- "super_schema_display_index"
81
- elsif @action_inquirer.show?
82
- "super_schema_display_show"
83
- else
84
- "super_schema_display_#{@action_inquirer.action}"
85
- end
86
- end
87
-
88
64
  # @private
89
- def render_field(template:, record:, column:)
90
- formatter = @fields[column]
91
-
92
- formatted =
93
- if formatter.real?
94
- value = record.public_send(column)
95
- formatter.present(value)
96
- else
97
- formatter.present
98
- end
99
-
100
- if formatted.respond_to?(:to_partial_path)
101
- if formatted.respond_to?(:locals)
102
- formatted.locals[:record] ||= record
103
- template.render(formatted, formatted.locals)
104
- else
105
- template.render(formatted)
106
- end
107
- else
108
- formatted
109
- end
65
+ def actions_called?
66
+ @actions_called
110
67
  end
111
68
  end
112
69
  end
@@ -2,7 +2,7 @@ module Super
2
2
  # Configures the host Rails app to work with Super
3
3
  class Engine < ::Rails::Engine
4
4
  initializer "super.assets.precompile" do |app|
5
- if Super::Assets.sprockets_available?
5
+ if Super::Assets::Handler.sprockets_available?
6
6
  app.config.assets.precompile << "config/super_manifest.js"
7
7
  end
8
8
  end
@@ -1,137 +1,12 @@
1
1
  module Super
2
2
  class Filter
3
- module ControllerMethods
4
- def index
5
- super
6
- @filter_form = controls.initialize_filtering(params: params, query_params: request.GET)
7
- @records = controls.filter_records(filter_form: @filter_form, records: @records)
8
- @view.asides.push(:@filter_form)
9
- end
10
- end
11
-
12
- class FilterFormField
13
- def initialize(humanized_field_name:, field_name:, type:, params:)
14
- @humanized_field_name = humanized_field_name
15
- @field_name = field_name
16
- @field_type = type
17
- @params = params
18
- @specified_values =
19
- type.q
20
- .map do |query_field_name|
21
- [
22
- query_field_name,
23
- (params || {})[query_field_name],
24
- ]
25
- end
26
- .to_h
27
-
28
- @specified_values.each do |key, value|
29
- define_singleton_method(key) { value }
30
- end
31
- end
32
-
33
- attr_reader :humanized_field_name
34
- attr_reader :field_name
35
- attr_reader :field_type
36
- attr_reader :specified_values
37
-
38
- def op
39
- (@params || {})[:op]
40
- end
41
-
42
- def operators
43
- @field_type.operators
44
- .map { |o| [o.name, o.identifier] }
45
- .to_h
46
- end
47
-
48
- def to_partial_path
49
- @field_type.to_partial_path
50
- end
51
- end
52
-
53
- def initialize(model:, url:, schema:, params:)
54
- @model = model
55
- @url = url
56
- @schema = schema
57
- @params = params
58
-
59
- @form_fields = {}
60
- end
61
-
62
- def each_field
63
- @schema.fields.each do |field_name, _field_type|
64
- yield(form_field_for(field_name))
65
- end
66
- end
67
-
68
- def url
69
- @url
70
- end
3
+ def initialize
4
+ @schema_type = Filter::SchemaTypes.new
5
+ @fields = Schema::Fields.new
71
6
 
72
- def to_partial_path
73
- "filter"
7
+ yield(@fields, @schema_type)
74
8
  end
75
9
 
76
- def to_search_query(relation)
77
- each_field do |form_field|
78
- next if form_field.specified_values.values.map(&:to_s).map(&:strip).all? { |specified_value| specified_value == "" }
79
- next if !Super::Filter::Operator.registry.key?(form_field.op)
80
-
81
- operator = Super::Filter::Operator.registry[form_field.op]
82
- updated_relation = operator.filter(relation, form_field.field_name, *form_field.specified_values.values)
83
-
84
- if updated_relation.is_a?(ActiveRecord::Relation)
85
- relation = updated_relation
86
- end
87
- end
88
-
89
- relation
90
- end
91
-
92
- private
93
-
94
- def form_field_for(field_name)
95
- @form_fields[field_name] ||=
96
- FilterFormField.new(
97
- humanized_field_name: @model.human_attribute_name(field_name),
98
- field_name: field_name,
99
- type: @schema.fields[field_name],
100
- params: (@params || {})[field_name]
101
- )
102
- end
103
- end
104
-
105
- class Controls
106
- module Optional
107
- def filters_enabled?
108
- actual.respond_to?(:filter_schema)
109
- end
110
-
111
- def filter_schema
112
- actual.filter_schema
113
- end
114
- end
115
-
116
- module Steps
117
- def initialize_filtering(params:, query_params:)
118
- if filters_enabled?
119
- Super::Filter.new(
120
- model: model,
121
- schema: filter_schema,
122
- params: params[:q],
123
- url: query_params
124
- )
125
- end
126
- end
127
-
128
- def filter_records(filter_form:, records:)
129
- if filters_enabled? && records
130
- filter_form.to_search_query(records)
131
- else
132
- records
133
- end
134
- end
135
- end
10
+ attr_reader :fields
136
11
  end
137
12
  end
@@ -0,0 +1,97 @@
1
+ module Super
2
+ class Filter
3
+ class FormObject
4
+ class FilterFormField
5
+ def initialize(humanized_field_name:, field_name:, type:, params:)
6
+ @humanized_field_name = humanized_field_name
7
+ @field_name = field_name
8
+ @field_type = type
9
+ @params = params
10
+ @specified_values =
11
+ type.q
12
+ .map do |query_field_name|
13
+ [
14
+ query_field_name,
15
+ (params || {})[query_field_name],
16
+ ]
17
+ end
18
+ .to_h
19
+
20
+ @specified_values.each do |key, value|
21
+ define_singleton_method(key) { value }
22
+ end
23
+ end
24
+
25
+ attr_reader :humanized_field_name
26
+ attr_reader :field_name
27
+ attr_reader :field_type
28
+ attr_reader :specified_values
29
+
30
+ def op
31
+ (@params || {})[:op]
32
+ end
33
+
34
+ def operators
35
+ @field_type.operators
36
+ .map { |o| [o.name, o.identifier] }
37
+ .to_h
38
+ end
39
+
40
+ def to_partial_path
41
+ @field_type.to_partial_path
42
+ end
43
+ end
44
+
45
+ def initialize(model:, url:, schema:, params:)
46
+ @model = model
47
+ @url = url
48
+ @schema = schema
49
+ @params = params
50
+
51
+ @form_fields = {}
52
+ end
53
+
54
+ def each_field
55
+ @schema.fields.each do |field_name, _field_type|
56
+ yield(form_field_for(field_name))
57
+ end
58
+ end
59
+
60
+ def url
61
+ @url
62
+ end
63
+
64
+ def to_partial_path
65
+ "filter"
66
+ end
67
+
68
+ def to_search_query(relation)
69
+ each_field do |form_field|
70
+ next if form_field.specified_values.values.map(&:to_s).map(&:strip).all? { |specified_value| specified_value == "" }
71
+ next if !Super::Filter::Operator.registry.key?(form_field.op)
72
+
73
+ operator = Super::Filter::Operator.registry[form_field.op]
74
+ updated_relation = operator.filter(relation, form_field.field_name, *form_field.specified_values.values)
75
+
76
+ if updated_relation.is_a?(ActiveRecord::Relation)
77
+ relation = updated_relation
78
+ end
79
+ end
80
+
81
+ relation
82
+ end
83
+
84
+ private
85
+
86
+ def form_field_for(field_name)
87
+ @form_fields[field_name] ||=
88
+ FilterFormField.new(
89
+ humanized_field_name: @model.human_attribute_name(field_name),
90
+ field_name: field_name,
91
+ type: @schema.fields[field_name],
92
+ params: (@params || {})[field_name]
93
+ )
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,30 @@
1
+ module Super
2
+ class Filter
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
+ .call
15
+ end
16
+
17
+ private
18
+
19
+ def attribute_type_for(attribute_name)
20
+ type = @model.type_for_attribute(attribute_name).type
21
+ case type
22
+ when :datetime
23
+ @type.timestamp
24
+ else
25
+ @type.text
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ module Super
2
+ class Filter
3
+ module ControllerMethods
4
+ def index
5
+ super
6
+ @filter_form = controls.initialize_filtering(params: params, query_params: request.GET)
7
+ @records = controls.filter_records(filter_form: @filter_form, records: @records)
8
+ @view.asides.push(:@filter_form)
9
+ end
10
+ end
11
+ end
12
+
13
+ class Controls
14
+ module Optional
15
+ def filters_enabled?
16
+ true
17
+ end
18
+
19
+ def filter_schema
20
+ Filter.new do |fields, type|
21
+ Filter::Guesser.new(model: model, fields: fields, type: type).call
22
+ end
23
+ end
24
+ end
25
+
26
+ module Steps
27
+ def initialize_filtering(params:, query_params:)
28
+ if filters_enabled?
29
+ Super::Filter::FormObject.new(
30
+ model: model,
31
+ schema: filter_schema,
32
+ params: params[:q],
33
+ url: query_params
34
+ )
35
+ end
36
+ end
37
+
38
+ def filter_records(filter_form:, records:)
39
+ if filters_enabled? && records
40
+ filter_form.to_search_query(records)
41
+ else
42
+ records
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -13,7 +13,7 @@ module Super
13
13
  # # ...
14
14
  #
15
15
  # def filter_schema
16
- # Super::Schema.new(Super::Filter::SchemaTypes.new) do |fields, type|
16
+ # Super::Filter.new do |fields, type|
17
17
  # fields[:name] = type.text(operators: [
18
18
  # Super::Filter::Operator.eq,
19
19
  # Super::Filter::Operator.contain,
@@ -90,12 +90,6 @@ module Super
90
90
  end
91
91
  end
92
92
 
93
- def before_yield(fields:)
94
- end
95
-
96
- def after_yield
97
- end
98
-
99
93
  def select(collection:, operators: Filter::Operator.select_defaults)
100
94
  Select.new(
101
95
  collection: collection,