Runtime assertions for Ruby literal.fun
ruby
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' into Adrian/fix_type_error_context

authored by

Stephen Ierodiaconou and committed by
GitHub
1d97a33d 4def0050

+283 -49
+1 -1
Gemfile
··· 1 1 # frozen_string_literal: true 2 2 3 - source "https://rubygems.org" 3 + source "https://gem.coop" 4 4 5 5 gemspec 6 6
+8
lib/literal/data.rb
··· 2 2 3 3 class Literal::Data < Literal::DataStructure 4 4 class << self 5 + def [](...) = new(...) 6 + 7 + def define(**properties) 8 + Class.new(self) do 9 + properties.each { |name, type| prop(name, type) } 10 + end 11 + end 12 + 5 13 def prop(name, type, kind = :keyword, reader: :public, predicate: false, default: nil) 6 14 super(name, type, kind, reader:, writer: false, predicate:, default:) 7 15 end
+8 -1
lib/literal/enum.rb
··· 199 199 end 200 200 201 201 alias_method :inspect, :name 202 - alias_method :to_s, :name 202 + 203 + def to_sym 204 + self.class.names[self] 205 + end 206 + 207 + def to_s 208 + to_sym.to_s 209 + end 203 210 204 211 def deconstruct 205 212 [@value]
+7 -11
lib/literal/errors/type_error.rb
··· 44 44 45 45 def to_h 46 46 { 47 - receiver: @receiver, 48 - method: @method, 49 - label: @label, 50 - expected: @expected, 51 - actual: @actual, 52 - children: @children, 53 - } 47 + receiver: @receiver, 48 + method: @method, 49 + label: @label, 50 + expected: @expected, 51 + actual: @actual, 52 + children: @children.map(&:to_h), 53 + } 54 54 end 55 55 56 56 alias to_hash to_h ··· 84 84 end 85 85 end 86 86 message 87 - end 88 - 89 - def deconstruct 90 - to_h.values 91 87 end 92 88 93 89 def deconstruct_keys(keys)
+59
lib/literal/properties/introspection.rb
··· 1 + # frozen_string_literal: true 2 + 3 + module Literal::Properties::Introspection 4 + def positional_properties 5 + literal_properties.filter(&:positional?) 6 + end 7 + 8 + def keyword_properties 9 + literal_properties.filter(&:keyword?) 10 + end 11 + 12 + def required_properties 13 + literal_properties.filter(&:required?) 14 + end 15 + 16 + def required_positional_properties 17 + required_properties.filter(&:positional?) 18 + end 19 + 20 + def required_keyword_properties 21 + required_properties.filter(&:keyword?) 22 + end 23 + 24 + def optional_positional_properties 25 + positional_properties - required_positional_properties 26 + end 27 + 28 + def optional_keyword_properties 29 + keyword_properties - required_keyword_properties 30 + end 31 + 32 + def positional_property_names 33 + positional_properties.map(&:name) 34 + end 35 + 36 + def keyword_property_names 37 + keyword_properties.map(&:name) 38 + end 39 + 40 + def required_property_names 41 + required_properties.map(&:name) 42 + end 43 + 44 + def required_positional_property_names 45 + required_positional_properties.map(&:name) 46 + end 47 + 48 + def required_keyword_property_names 49 + required_keyword_properties.map(&:name) 50 + end 51 + 52 + def optional_positional_property_names 53 + optional_positional_properties.map(&:name) 54 + end 55 + 56 + def optional_keyword_property_names 57 + optional_keyword_properties.map(&:name) 58 + end 59 + end
+1
lib/literal/property.rb
··· 51 51 end 52 52 53 53 def default? 54 + return true if splat? || double_splat? 54 55 nil != @default 55 56 end 56 57
+4 -12
lib/literal/rails/relation_type.rb
··· 3 3 module Literal::Rails 4 4 class RelationType 5 5 def initialize(model_class) 6 - unless Class === model_class && model_class < ActiveRecord::Base 7 - raise Literal::TypeError.new( 8 - context: Literal::TypeError::Context.new( 9 - expected: ActiveRecord::Base, actual: model_class 10 - ) 11 - ) 12 - end 13 - 14 6 @model_class = model_class 15 7 end 16 8 ··· 18 10 19 11 def ===(value) 20 12 case value 21 - when ActiveRecord::Relation, ActiveRecord::Associations::CollectionProxy, ActiveRecord::AssociationRelation 22 - @model_class == value.model || value.model < @model_class 23 - else 24 - false 13 + when ActiveRecord::Relation, ActiveRecord::Associations::CollectionProxy, ActiveRecord::AssociationRelation 14 + @model_class == value.model || value.model < @model_class 15 + else 16 + false 25 17 end 26 18 end 27 19 end
+1 -1
lib/literal/version.rb
··· 1 1 # frozen_string_literal: true 2 2 3 3 module Literal 4 - VERSION = "1.8.0" 4 + VERSION = "1.8.1" 5 5 end
+7 -2
lib/ruby_lsp/literal/addon.rb
··· 39 39 end 40 40 41 41 case args 42 - in [Prism::SymbolNode[unescaped: String => prop_name], *] 42 + in [Prism::SymbolNode[unescaped: String => prop_name], Prism::Node => prop_type, *] 43 + prop_type_location = prop_type.location 44 + prop_type_indentation = prop_type_location.source_lines[prop_type_location.start_line - 1][/\A\s*/] 45 + 46 + prop_signature = prop_type_location.slice.lines.map { |line| line.delete_prefix(prop_type_indentation) }.join 47 + 43 48 @listener.instance_exec do 44 49 @index.add(RubyIndexer::Entry::InstanceVariable.new( 45 50 "@#{prop_name}", 46 51 @uri, 47 52 RubyIndexer::Location.from_prism_location(node.location, @code_units_cache), 48 - collect_comments(node), 53 + [collect_comments(node), "**Type:**\n```ruby\n#{prop_signature}\n```"].join("\n\n"), 49 54 owner, 50 55 )) 51 56 end
+14
test/data.test.rb
··· 82 82 assert_equal(empty.eql?(other_empty), false) 83 83 assert_equal(empty.hash != other_empty.hash, true) 84 84 end 85 + 86 + test "define" do 87 + person_with_define = Literal::Data.define(name: String).new(name: "John") 88 + person = Person.new(name: "John") 89 + 90 + assert_equal(person_with_define.to_h, person.to_h) 91 + end 92 + 93 + test "initialize with [] method" do 94 + person_a = Person.new(name: "John") 95 + person_b = Person[name: "John"] 96 + 97 + assert_equal(person_a, person_b) 98 + end
+14
test/enum.test.rb
··· 130 130 ] 131 131 end 132 132 133 + test ".to_s" do 134 + assert_equal Color::Red.to_s, "Red" 135 + assert_equal Color::SPRING_GREEN.to_s, "SPRING_GREEN" 136 + assert_equal Switch::On.to_s, "On" 137 + assert_equal SymbolTypedEnum::A.to_s, "A" 138 + end 139 + 140 + test ".to_sym" do 141 + assert_equal Color::Red.to_sym, :Red 142 + assert_equal Color::SPRING_GREEN.to_sym, :SPRING_GREEN 143 + assert_equal Switch::On.to_sym, :On 144 + assert_equal SymbolTypedEnum::A.to_sym, :A 145 + end 146 + 133 147 test "#succ" do 134 148 assert_equal Color::Red.succ, Color::Green 135 149 assert_equal Color::Green.succ, Color::Blue
+2
test/properties.test.rb
··· 46 46 refute_raises { example.new } 47 47 refute_raises { example.new("Hello") } 48 48 refute_raises { example.new("Hello", "World") } 49 + refute example.literal_properties[:example].required? { "Expected example to not be required" } 49 50 end 50 51 51 52 test "keyword splats are optional" do ··· 56 57 refute_raises { example.new } 57 58 refute_raises { example.new(example: "Hello") } 58 59 refute_raises { example.new(example: "Hello", world: "World") } 60 + refute example.literal_properties[:example].required? { "Expected example to not be required" } 59 61 end 60 62 61 63 test "block params are required by default" do
+157
test/properties/introspection.test.rb
··· 1 + # frozen_string_literal: true 2 + 3 + class BasicIntrospection 4 + extend Literal::Properties 5 + extend Literal::Properties::Introspection 6 + 7 + prop :id, Integer, :positional 8 + prop :name, String, :positional 9 + prop :age, Integer 10 + prop :email, _Nilable(String) 11 + prop :friends, _Array(String), :* 12 + prop :options, _Hash(Symbol, String), :** 13 + prop :block, _Nilable(Proc), :& 14 + end 15 + 16 + class WithOptionals 17 + extend Literal::Properties 18 + extend Literal::Properties::Introspection 19 + 20 + prop :required_pos, String, :positional 21 + prop :optional_pos, String, :positional, default: "default" 22 + prop :optional_name, _Nilable(String), :positional 23 + prop :required_kw, Integer 24 + prop :optional_kw, Integer, default: 42 25 + prop :tags, _Array(String), default: -> { [] } 26 + prop :created_at, _Nilable(Time) 27 + end 28 + 29 + class EmptyWithIntrospection 30 + extend Literal::Properties 31 + extend Literal::Properties::Introspection 32 + end 33 + 34 + test "positional_properties returns all positional properties" do 35 + props = BasicIntrospection.positional_properties 36 + assert_equal props.map(&:name), [:id, :name] 37 + assert props.all?(&:positional?) 38 + end 39 + 40 + test "positional_property_names returns all positional property names" do 41 + names = BasicIntrospection.positional_property_names 42 + assert_equal names, [:id, :name] 43 + end 44 + 45 + test "keyword_properties returns all keyword properties" do 46 + props = BasicIntrospection.keyword_properties 47 + assert_equal props.map(&:name), [:age, :email] 48 + assert props.all?(&:keyword?) 49 + end 50 + 51 + test "keyword_property_names returns all keyword property names" do 52 + names = BasicIntrospection.keyword_property_names 53 + assert_equal names, [:age, :email] 54 + end 55 + 56 + test "required_properties returns all required properties" do 57 + props = BasicIntrospection.required_properties 58 + assert_equal props.map(&:name), [:id, :name, :age] 59 + assert props.all?(&:required?) 60 + end 61 + 62 + test "required_property_names returns all required property names" do 63 + names = BasicIntrospection.required_property_names 64 + assert_equal names, [:id, :name, :age] 65 + end 66 + 67 + test "required_positional_properties returns only required positional properties" do 68 + props = BasicIntrospection.required_positional_properties 69 + assert_equal props.map(&:name), [:id, :name] 70 + assert props.all? { |p| p.required? && p.positional? } 71 + end 72 + 73 + test "required_positional_property_names returns only required positional property names" do 74 + names = BasicIntrospection.required_positional_property_names 75 + assert_equal names, [:id, :name] 76 + end 77 + 78 + test "required_keyword_properties returns only required keyword properties" do 79 + props = BasicIntrospection.required_keyword_properties 80 + assert_equal props.map(&:name), [:age] 81 + assert props.all? { |p| p.required? && p.keyword? } 82 + end 83 + 84 + test "required_keyword_property_names returns only required keyword property names" do 85 + names = BasicIntrospection.required_keyword_property_names 86 + assert_equal names, [:age] 87 + end 88 + 89 + test "optional_positional_properties returns positional properties that are not required" do 90 + props = WithOptionals.optional_positional_properties 91 + assert_equal props.map(&:name), [:optional_pos, :optional_name] 92 + assert props.all? { |p| p.positional? && !p.required? } 93 + end 94 + 95 + test "optional_positional_property_names returns optional positional property names" do 96 + names = WithOptionals.optional_positional_property_names 97 + assert_equal names, [:optional_pos, :optional_name] 98 + end 99 + 100 + test "optional_keyword_properties returns keyword properties that are not required" do 101 + props = WithOptionals.optional_keyword_properties 102 + assert_equal props.map(&:name), [:optional_kw, :tags, :created_at] 103 + assert props.all? { |p| p.keyword? && !p.required? } 104 + end 105 + 106 + test "optional_keyword_property_names returns optional keyword property names" do 107 + names = WithOptionals.optional_keyword_property_names 108 + assert_equal names, [:optional_kw, :tags, :created_at] 109 + end 110 + 111 + test "empty class returns empty arrays for all methods" do 112 + assert_equal EmptyWithIntrospection.positional_properties, [] 113 + assert_equal EmptyWithIntrospection.keyword_properties, [] 114 + assert_equal EmptyWithIntrospection.required_properties, [] 115 + assert_equal EmptyWithIntrospection.required_positional_properties, [] 116 + assert_equal EmptyWithIntrospection.required_keyword_properties, [] 117 + assert_equal EmptyWithIntrospection.optional_positional_properties, [] 118 + assert_equal EmptyWithIntrospection.optional_keyword_properties, [] 119 + 120 + assert_equal EmptyWithIntrospection.positional_property_names, [] 121 + assert_equal EmptyWithIntrospection.keyword_property_names, [] 122 + assert_equal EmptyWithIntrospection.required_property_names, [] 123 + assert_equal EmptyWithIntrospection.required_positional_property_names, [] 124 + assert_equal EmptyWithIntrospection.required_keyword_property_names, [] 125 + assert_equal EmptyWithIntrospection.optional_positional_property_names, [] 126 + assert_equal EmptyWithIntrospection.optional_keyword_property_names, [] 127 + end 128 + 129 + test "splat parameters are not included in positional or keyword properties" do 130 + positional_names = BasicIntrospection.positional_property_names 131 + refute positional_names.include?(:friends) 132 + 133 + keyword_names = BasicIntrospection.keyword_property_names 134 + refute keyword_names.include?(:options) 135 + end 136 + 137 + test "block parameters are not included in any property lists" do 138 + all_names = BasicIntrospection.positional_property_names + BasicIntrospection.keyword_property_names 139 + refute all_names.include?(:block) 140 + end 141 + 142 + test "nilable positional properties are considered optional" do 143 + required_names = WithOptionals.required_positional_property_names 144 + optional_names = WithOptionals.optional_positional_property_names 145 + 146 + assert_equal required_names, [:required_pos] 147 + assert_equal optional_names, [:optional_pos, :optional_name] 148 + end 149 + 150 + test "mixed property types with defaults and nilables" do 151 + assert_equal WithOptionals.positional_property_names, [:required_pos, :optional_pos, :optional_name] 152 + assert_equal WithOptionals.keyword_property_names, [:required_kw, :optional_kw, :tags, :created_at] 153 + assert_equal WithOptionals.required_positional_property_names, [:required_pos] 154 + assert_equal WithOptionals.required_keyword_property_names, [:required_kw] 155 + assert_equal WithOptionals.optional_positional_property_names, [:optional_pos, :optional_name] 156 + assert_equal WithOptionals.optional_keyword_property_names, [:optional_kw, :tags, :created_at] 157 + end
-10
test/rails.test.rb
··· 1 - # frozen_string_literal: true 2 - 3 - test "ActiveRecord::Relation with non-ActiveRecord::Base child" do 4 - assert_raises(Literal::TypeError) do 5 - Class.new do 6 - extend Literal::Properties 7 - prop :example, ActiveRecord::Relation(String) 8 - end 9 - end 10 - end
-11
test/type_error.test.rb
··· 20 20 actual: 17, 21 21 } 22 22 assert_equal error.deconstruct_keys(key_names), exp_keys 23 - 24 - # deconstruct 25 - expected_value = error.deconstruct 26 - assert_equal expected_value.size, 6 27 - assert_equal expected_value[0], nil 28 - assert_equal expected_value[1], nil 29 - assert_equal expected_value[2], nil # "_Constraint(Integer, 18..)" 30 - assert_equal expected_value[3].class, Literal::Types::ConstraintType 31 - assert_equal expected_value[4], 17 32 - assert_equal expected_value[5].class, Array 33 - assert_equal expected_value[5].first.class, Literal::TypeError::Context 34 23 end