vident 0.6.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "better_html"
4
- require "better_html"
5
- require "cgi/util"
6
-
7
- module Vident
8
- module RootComponent
9
- module UsingBetterHTML
10
- # Return the HTML `data-controller` attribute for the given controllers
11
- def with_controllers(*controllers_to_set)
12
- helpers.html_attributes("data-controller" => controller_list(controllers_to_set)&.html_safe)
13
- end
14
-
15
- # Return the HTML `data-target` attribute for the given targets
16
- def as_targets(*targets)
17
- attrs = build_target_data_attributes(parse_targets(targets))
18
- helpers.html_attributes(attrs.transform_keys! { |k| "data-#{k}" })
19
- end
20
- alias_method :as_target, :as_targets
21
-
22
- # Return the HTML `data-action` attribute for the given actions
23
- def with_actions(*actions_to_set)
24
- actions_str = action_list(actions_to_set)
25
- actions_str.present? ? helpers.html_attributes("data-action" => actions_str) : nil
26
- end
27
- alias_method :with_action, :with_actions
28
-
29
- private
30
-
31
- # Complete list of actions ready to be use in the data-action attribute
32
- def action_list(actions_to_parse)
33
- return nil unless actions_to_parse&.size&.positive?
34
- # `html_attributes` will escape '->' thus breaking the stimulus action, so we need to do our own escaping
35
- actions_str_raw = parse_actions(actions_to_parse).join(" ")
36
- CGI.escapeHTML(actions_str_raw).gsub("->", "->").html_safe
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "phlex"
4
- require "phlex"
5
-
6
- module Vident
7
- module RootComponent
8
- class UsingPhlexHTML < Phlex::HTML
9
- include Base
10
- if Gem.loaded_specs.has_key? "better_html"
11
- include UsingBetterHTML
12
- end
13
-
14
- VALID_TAGS = Set[
15
- *(Phlex::HTML::VoidElements::REGISTERED_ELEMENTS.keys + Phlex::HTML::StandardElements::REGISTERED_ELEMENTS.keys)
16
- ].freeze
17
-
18
- # Create a tag for a target with a block containing content
19
- def target_tag(tag_name, targets, **options, &block)
20
- parsed = parse_targets(Array.wrap(targets))
21
- options[:data] ||= {}
22
- options[:data].merge!(build_target_data_attributes(parsed))
23
- generate_tag(tag_name, **options, &block)
24
- end
25
-
26
- # Build a tag with the attributes determined by this components properties and stimulus
27
- # data attributes.
28
- def template(&block)
29
- # Generate tag options and render
30
- tag_type = @element_tag.presence&.to_sym || :div
31
- raise ArgumentError, "Unsupported HTML tag name #{tag_type}" unless VALID_TAGS.include?(tag_type)
32
- options = @html_options&.dup || {}
33
- data_attrs = tag_data_attributes
34
- data_attrs = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
35
- options = options.merge(id: @id) if @id
36
- options.except!(:data)
37
- options.merge!(data_attrs.transform_keys { |k| "data-#{k}" })
38
- generate_tag(tag_type, **options, &block)
39
- end
40
-
41
- private
42
-
43
- def generate_tag(tag_type, **options, &block)
44
- send((tag_type == :template) ? :template_tag : tag_type, **options, &block)
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "view_component"
4
- require "view_component"
5
-
6
- module Vident
7
- module RootComponent
8
- class UsingViewComponent < ::ViewComponent::Base
9
- include Base
10
- if Gem.loaded_specs.has_key? "better_html"
11
- include UsingBetterHTML
12
- end
13
-
14
- SELF_CLOSING_TAGS = Set[:area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr].freeze
15
-
16
- def target_tag(tag_name, targets, **options, &block)
17
- parsed = parse_targets(Array.wrap(targets))
18
- options[:data] ||= {}
19
- options[:data].merge!(build_target_data_attributes(parsed))
20
- content = view_context.capture(&block) if block
21
- view_context.content_tag(tag_name, content, options)
22
- end
23
-
24
- def call
25
- # Generate outer tag options and render
26
- tag_type = content_tag_type
27
- options = content_tag_options
28
- if SELF_CLOSING_TAGS.include?(tag_type)
29
- view_context.tag(tag_type, options)
30
- else
31
- view_context.content_tag(tag_type, content, options)
32
- end
33
- end
34
-
35
- private
36
-
37
- def content_tag_options
38
- options = @html_options&.dup || {}
39
- data_attrs = tag_data_attributes
40
- options[:data] = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
41
- return options unless @id
42
- options.merge(id: @id)
43
- end
44
-
45
- def content_tag_type
46
- @element_tag.presence || :div
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # TODO: what about when used with Phlex?
4
- module Vident
5
- class TestCase < ::ViewComponent::TestCase
6
- include Vident::Testing::AutoTest
7
- end
8
- end
@@ -1,176 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- module Testing
5
- class AttributesTester
6
- def initialize(test_configurations)
7
- @test_configurations = test_configurations
8
- end
9
-
10
- # Generates attribute hashes for all permutations of the given valid values.
11
- def valid_configurations
12
- # Expands any auto generated values and returns all attributes and their values
13
- test_attrs = prepare_attributes_to_test
14
-
15
- # The first permutation is the initial state
16
- initial_state = prepare_initial_test_state(test_attrs)
17
-
18
- # Create remaining permutations
19
- test_attrs.flat_map do |attr_name, values|
20
- values.map { |v| initial_state.merge(attr_name => v) }
21
- end
22
- end
23
-
24
- # Generates attribute hashes for all permutations of the given invalid values.
25
- def invalid_configurations
26
- return [] unless invalid_configured?
27
-
28
- # prepare a valid initial state, then add any attrs that have :invalid
29
- initial_state = prepare_initial_test_state(prepare_attributes_to_test)
30
-
31
- # Merge in the invalid permutations
32
- test_configurations.inject([]) do |memo, attr|
33
- key, opts = attr
34
- next memo += nil if opts == :strict_boolean
35
- if opts.is_a?(Hash)
36
- values = if opts[:invalid].nil? && opts[:valid].is_a?(Hash)
37
- # If no invalid key specified we generate based on whats valid
38
- config = invalid_attribute_test_values_for(opts[:valid][:type], opts[:valid])
39
- (config&.fetch(:invalid, []) || []) + (config&.fetch(:converts, []) || [])
40
- elsif opts[:invalid].is_a?(Array)
41
- opts[:invalid]
42
- end
43
-
44
- memo += values.map { |i| initial_state.merge(key => i) } if values
45
- end
46
- memo
47
- end
48
- end
49
-
50
- private
51
-
52
- attr_reader :test_configurations
53
-
54
- def invalid_configured?
55
- test_configurations.values.any? { |v| v.respond_to?(:key?) && v.key?(:invalid) }
56
- end
57
-
58
- def prepare_attributes_to_test
59
- test_configurations.transform_values do |attr_config|
60
- next [true, false, nil] if attr_config == :boolean
61
- next [true, false] if attr_config == :strict_boolean
62
- valid = attr_config[:valid]
63
- raise "Ensure :valid attributes configuration is provided" unless valid
64
- next valid if valid.is_a?(Array)
65
- attribute_test_values_for(valid)
66
- end
67
- end
68
-
69
- def prepare_initial_test_state(test_attrs)
70
- initial_state = {}
71
- test_attrs.each { |attr_name, values| initial_state[attr_name] = values.first }
72
- initial_state
73
- end
74
-
75
- def attribute_test_values_for(options)
76
- type = parse_type(options[:type])
77
- return options[:in] if options[:in]
78
- values =
79
- case type
80
- when :string, "String"
81
- s = (1..8).map { |l| ::Faker::String.random(length: l) }
82
- s.prepend "test string"
83
- s = s.select(&:present?)
84
- s << "" if options[:allow_blank]
85
- s
86
- when :boolean
87
- [false, true]
88
- when :float, "Float"
89
- (1..3).map { Faker::Number.positive } + (1..3).map { Faker::Number.negative }
90
- when :numeric, "Numeric"
91
- (1..3).map { Faker::Number.positive } + [1, 5]
92
- when :integer, "Integer"
93
- min = options[:min] || -10_000
94
- max = options[:max] || 10_000
95
- (1..3).map { Kernel.rand(min..max) }
96
- when :array, "Array"
97
- a =
98
- if options[:sub_type] == Numeric
99
- [[1, 2, 3], [0.3, 2, 0.002]]
100
- elsif options[:sub_type]
101
- [[options[:sub_type].new]]
102
- else
103
- [%i[a b c], [1, 2, 3], %w[a b]]
104
- end
105
- a << [] if options[:allow_blank]
106
- a
107
- when :any
108
- [false, 1245, {}, :df, "hi"]
109
- when :hash, "Hash"
110
- a = [{a: 1}]
111
- a << {} if options[:allow_blank]
112
- a
113
- when :symbol, "Symbol"
114
- %i[a b c]
115
- else
116
- raise StandardError, "Attribute type not understood (#{type})"
117
- end
118
-
119
- if options[:allow_nil] || !options[:default].nil? || (options[:allow_blank] && options[:allow_nil].nil?)
120
- values << nil
121
- end
122
- values
123
- end
124
-
125
- def invalid_attribute_test_values_for(type, options)
126
- values = case parse_type(type)
127
- when :string, "String"
128
- # All values are also convertable to string
129
- a = {converts: [false, 1245, 1.0, {}, :df, []], invalid: []}
130
- a[:invalid] << "" if options[:allow_blank] == false
131
- a
132
- when :boolean
133
- # All values are also convertable to boolean with !!
134
- {converts: ["sdf", 234, 3.5, {}, :sdf, []], invalid: []}
135
- when :float, "Float"
136
- # Not all values are convertable to float
137
- {converts: [234, "12.2"], invalid: ["sdf", 234, {}, :sdf, [], false]}
138
- when :numeric, "Numeric"
139
- {converts: ["12.2"], invalid: ["sdf", {}, :sdf, [], false]}
140
- when :integer, "Integer"
141
- # Not all values are convertable to integer
142
- {converts: [234.0, "123", "sdf"], invalid: [{}, :sdf, [], false]}
143
- when :array, "Array"
144
- # Not all values are convertable to array
145
- a = if options[:sub_type]
146
- {converts: [{}, [{}]], invalid: ["sdf", [123], [Class.new]]}
147
- else
148
- {converts: [{}], invalid: ["sdf", 234, 3.5, :sdf, false]}
149
- end
150
- a[:invalid] << [] if options[:allow_blank] == false
151
- a
152
- when :any
153
- # There are no invalid values
154
- {converts: [], invalid: []}
155
- when :hash, "Hash"
156
- a = {converts: [[], [[:a, 1], [:b, 2]]], invalid: [:sdf, false, "boo", 123]}
157
- a[:invalid] << {} if options[:allow_blank] == false
158
- a
159
- when :symbol, "Symbol"
160
- {converts: ["foo"], invalid: [{}, false, [], 123]}
161
- else
162
- raise StandardError, "Attribute type not understood (#{type})"
163
- end
164
-
165
- if options[:default].nil? && (!options[:allow_nil] || (!options[:allow_blank] && options[:allow_nil] != true))
166
- values[:invalid] << nil
167
- end
168
- values
169
- end
170
-
171
- def parse_type(type)
172
- type.is_a?(Symbol) ? type : type.name
173
- end
174
- end
175
- end
176
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "minitest/hooks"
4
-
5
- module Vident
6
- module Testing
7
- module AutoTest
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- include Minitest::Hooks
12
-
13
- def before_all
14
- @results_content = []
15
- ::Vident::StableId.set_current_sequence_generator
16
- end
17
-
18
- def after_all
19
- html = <<~HTML
20
- <!doctype html>
21
- <html lang="en">
22
- <head>
23
- <meta charset="UTF-8">
24
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
25
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
26
- <title>Test run output</title>
27
- </head>
28
- <body>
29
- #{@results_content.map(&:to_s).join("<hr>\n").html_safe}
30
- </body>
31
- </html>
32
- HTML
33
-
34
- # TODO: configurable layout
35
- filename = self.class.name.gsub("::", "_")
36
- File.write("render_results_#{filename}.html", html) # TODO: path for outputs (eg default tmp/)
37
- end
38
- end
39
-
40
- class_methods do
41
- def auto_test(class_under_test, **param_config)
42
- attribute_tester = Vident::Testing::AttributesTester.new(param_config)
43
- attribute_tester.valid_configurations.each_with_index do |test, index|
44
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
45
- def test_renders_with_valid_attrs_#{index}
46
- test_attrs = #{test}
47
- begin
48
- @results_content << render_inline(#{class_under_test}.new(**test_attrs))
49
- rescue => error
50
- assert(false, "Should not raise with #{test.to_s.tr("\"", "'")} but did raise \#{error}")
51
- end
52
- end
53
- RUBY
54
- end
55
-
56
- attribute_tester.invalid_configurations.each_with_index do |test, index|
57
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
58
- def test_raises_with_invalid_attrs_#{index}
59
- test_attrs = #{test}
60
- assert_raises(StandardError, "Should raise with #{test.to_s.tr("\"", "'")}") do
61
- @results_content << render_inline(#{class_under_test}.new(**test_attrs))
62
- end
63
- end
64
- RUBY
65
- end
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if Gem.loaded_specs.has_key? "dry-struct"
4
- require_relative "./attributes/typed"
5
-
6
- module Vident
7
- module TypedComponent
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- include Vident::Base
12
- include Vident::Attributes::Typed
13
-
14
- attribute :id, String, delegates: false
15
- attribute :html_options, Hash, delegates: false
16
- attribute :element_tag, Symbol, delegates: false
17
-
18
- # StimulusJS support
19
- attribute :controllers, Array, default: [], delegates: false
20
- attribute :actions, Array, default: [], delegates: false
21
- attribute :targets, Array, default: [], delegates: false
22
- attribute :data_maps, Array, default: [], delegates: false
23
-
24
- # TODO normalise the syntax of defining actions, controllers, etc
25
- attribute :named_classes, Hash, delegates: false
26
- end
27
-
28
- def initialize(attrs = {})
29
- before_initialise(attrs)
30
- prepare_attributes(attrs)
31
- # The attributes need to also be set as ivars
32
- attributes.each do |attr_name, attr_value|
33
- instance_variable_set(self.class.attribute_ivar_names[attr_name], attr_value)
34
- end
35
- after_initialise
36
- super()
37
- end
38
- end
39
- end
40
- else
41
- module Vident
42
- module TypedComponent
43
- def self.included(base)
44
- raise "Vident::TypedComponent requires dry-struct to be installed"
45
- end
46
- end
47
- end
48
- end
data/sig/vident.rbs DELETED
@@ -1,4 +0,0 @@
1
- module Vident
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end