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,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,