Seemingly trivial class example will not compile - crystal-lang

I am attempting to configure properties of a specific type and guaranteed not to be nil with getters. This works fine for String or URI instance variables, however when attempting to do the same thing with an HTTP::Client the compiler gives an error that the instance variable is not initialized in all initialize methods.
require "http/client"
class Server
getter uri : URI
getter foo : String
getter connnection : HTTP::Client
def initialize(#uri)
#foo = "Bar"
#connection = HTTP::Client.new #uri
end
end
The full error given by the compiler is:
Error in src/server.cr:6: expanding macro
getter connnection : HTTP::Client
^
in macro 'getter' expanded macro: macro_4613328608:113, line 4:
1.
2.
3.
> 4. #connnection : HTTP::Client
5.
6. def connnection : HTTP::Client
7. #connnection
8. end
9.
10.
11.
12.
instance variable '#connnection' of Server was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.
How can I appropriately initialize the #connection instance variable so that the crystal compiler is happy?

You have a typo there:
require "http/client"
class Server
getter uri : URI
getter foo : String
getter connnection : HTTP::Client
# ^
def initialize(#uri)
#foo = "Bar"
#connection = HTTP::Client.new #uri
end
end

This works for me. As pointed out above you have a typo, so it's probably not even necessary to allow it to be nilable.
require "http/client"
class Server
getter uri : URI
getter foo : String
getter connection : HTTP::Client?
def initialize(#uri)
#foo = "Bar"
#connection = HTTP::Client.new #uri
end
end
Server.new(URI.parse("https://www.google.com"))

Related

Use a method on a StateNotifier Riverpod for changing a bool [duplicate]

In the context of a Flutter 2.0.5 app whose state I'd like to manage with Riverpod, I thought I can declare a StateNotifierProvider like this:
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider<CounterStateNotifier>((ref) => CounterStateNotifier());
class CounterStateNotifier extends StateNotifier<int> {
CounterStateNotifier([int count = 0]) : super(count);
void increment() => state++;
}
But Android Studio (and later the Dart compiler as well) complains about the line where I declare the counterProvider variable:
The type 'StateNotifierProvider' is declared with 2 type parameters, but 1 type arguments were given.
Removing the <CounterStateNotifier> type parameter in StateNotifierProvider<CounterStateNotifier> removes the error. However, attempting to read the provider and call its increment method (setting () => context.read(counterProvider).increment() as the onPressed of an ElevatedButton, then pressing the button) gives the following runtime error:
'increment'
method not found
Receiver: 0
Arguments: []
Why is context.read(counterProvider) returning the int state instead of the notifier? And what is the reason behind the type parameter error mentioned in the first part of my question?
I should mention that I'm running my app on the web (with flutter run -d Chrome).
As of Riverpod 0.14.0, State is the default value exposed by StateNotifierProvider.
The syntax for declaring your StateNotifierProvider is now as follows:
final counterProvider = StateNotifierProvider<CounterStateNotifier, int>((ref) => CounterStateNotifier());
Accessing functions now requires adding .notifier (accessing the StateNotifier itself):
context.read(counterProvider.notifier).increment();
And like you've noticed, you now access the state like so:
final count = context.read(counterProvider);
More on the changes here.
You may also use dynamic to accept any type if value for the StateNotifierProvider
final modelProvider =
StateNotifierProvider.autoDispose<ModelClassName, dynamic>(
(ref) => ModelClassName());

Crystal compiler does not detect that object is not nil

I have the following class :
class X
property son, val
def initialize(#val : Int32)
#son = nil.as X?
end
def add(other : X?)
unless other.nil?
if #son.nil?
#son = other
else
#son.add(other)
end
end
end
end
x = X.new 5
x.add(nil)
x.add(X.new 3)
But when I try to build I get
Showing last frame. Use --error-trace for full trace.
In nil-test.cr:12:22
12 | #son.add(other)
^------
Error: undefined method 'include' for Nil (compile-time type is (X | Nil))
According to the manual, this is exactly the kind of situation where the compiler should recognize that #son cannot be nil in the else branch, yet it apparently fails to do so.
What am I doing wrong ?
Note : using #son.not_nil!.add(other) works, I'm just asking why the compiler can't do without.
That only works for local variables, not instance variables - since the instance variables may be mutated by another fiber between the condition and you accessing the variable. See this section (under "Limitations") in the Crystal docs.
You can do it like this, assigning the instance variable to a local variable that will not change out from under you:
def add(other : X?)
unless other.nil?
if s = #son
s.add(other)
else
#son = other
end
end
end

How do I access an object's eigenclass in Crystal?

In Ruby, it's possible to access the eigenclass (or "singleton class") of an object by reopening it. This is particularly useful for defining "private class methods":
class Foo
class << self
private
def declarative_method_name
end
end
declarative_method_name
end
# Foo.declarative_method_name => ERROR!
However, in Crystal this is not syntax:
Syntax error in ./test.cr:2: expecting token 'CONST', not '<<'
class << self
^
Is there another (or indeed any) way to achieve this in Crystal currently?
There's no eigenclass, or more commonly called singleton class in Ruby these days (given there's Object#singleton_class), in Crystal.
However defining class methods and calling them on the class level is supported:
class Foo
private def self.declarative_method_name
puts "hey"
end
declarative_method_name
end
https://carc.in/#/r/1316
The def self. construct here is specialized by the compiler and there's no more general concept beneath it, yet.
How would you make a super classes' new method private while still allowing it's subclasses' to be public?
class Foo
private self.new; end
end
class Bar < Foo
end
Bar.new #=> error: private method 'new' called for Foo:Class
It's also worth noting here that unlike in Ruby, class variables don't transcend inheritance. In Ruby the following code has a strange side effect...
class Foo
##var = 'foo'
def var
##var
end
end
class Bar < Foo
##var = 'bar'
end
puts Foo.new.var
It'll return 'bar' despite the fact that we modified the class variable on Bar. In crystal it returns 'foo' meaning that another reason we'd access the eiganclass, to store and read class level state safely, isn't necessary in crystal, we can just use class variables.

Prestashop - Fatal error: Undefined class constant 'self::TYPE_STRING'

I am developing a module in prestashop 1.6, i extend AdminController class, every thing is nice.but when i want to display the form that permit to add new instance an error message is displayed :
Fatal error: Undefined class constant 'self::TYPE_STRING' in C:\xampp\htdocs\prestashop\classes\controller\AdminController.php on line 1418
self::TYPE_STRING is a constant from ObjectModel, not AdminController.
I don't know why you'd want use it inside AdminController, but if really need to, change it to : ObjectModel::TYPE_STRING.
Keyword self referes to the current class you're in (in this it is AdminController)
With PHP you can create the missing constant with code like this:
class Test {
private const TYPE_STRING = 'string value';
public function getString() {
return self::TYPE_STRING;
}
}

scope not working on Mongoid (undefined method `to_criteria')

I invoke ReleaseSchedule.next_release in other controller
and got the following error
NoMethodError (undefined method `to_criteria' for #<ReleaseSchedule:0x007f9cfafbfe70>):
app/controllers/weekly_query_controller.rb:15:in `next_release'
releae_schedule.rb
class ReleaseSchedule
scope :next_release, ->(){ ReleaseSchedule.where(:release_date.gte => Time.now).without(:_id, :created_at, :updated_at).first }
end
That's not really a scope at all, that's just a class method wrapped up to look like a scope. There are two problems:
You're saying ReleaseSchedule.where(...) so you can't chain the "scope" (i.e. ReleaseSchedule.where(...).next_release won't do what it is supposed to do).
Your "scope" ends in first so it won't return a query, it just returns a single instance.
2 is probably where your NoMethodError comes from.
If you really want it to be a scope for some reason then you'd say:
# No `first` or explicit class reference in here.
scope :next_release, -> { where(:release_date.gte => Time.now).without(:_id, :created_at, :updated_at) }
and use it as:
# The `first` goes here instead.
r = ReleaseSchedule.next_release.first
But really, you just want a class method:
def self.next_release
where(:release_date.gte => Time.now).without(:_id, :created_at, :updated_at).first
end
The scope macro is, after all, just a fancy way to build class methods. The only reason we have scope is to express an intent (i.e. to build queries piece by piece) and what you're doing doesn't match that intent.