require "http/client"
response = HTTP::Client.get("http://www.example.com")
content = response.body.gets_to_end
You can read more at https://crystal-lang.org/api/0.28.0/HTTP/Client.html
I assume that several of the top backers use Crystal in productions: https://crystal-lang.org/sponsors/
Your not going to pay 250 to 2000 dollar / month in donations, if you do not use crystal to make money.
Most of my work with Crystal has evolved around testing and seeing how it stacks up against Go for our projects. Speed wise its in the same range as Go just the compiler being a lot slower.
Its most power for any company that has a Ruby background, is the more or less the "drop in replacement" ( with a bit or work). Its not like your going to rewrite your entire stack, unlike going to a other language.
My own tests comparing it to a few scripting languages ( like Ruby, PHP ) has Crystal crushing them ( no big surprise ). We are talking realistic routing, DB access reads from a cluster DB, Json REST Api etc... From this point of view its plenty production ready.
The main issue is frankly, the slow compilation speed ( compared to Go and the "quick and dirty" scripting languages ) and the slow development speed of the language. I like to see Crystal going to a fixed release schedule like Go/Rust, where you more can anticipate official feature releases.
Now if a company has the balls to put 2000 dollar per month on the line to sponsor the language, ... that sounds a lot like a ringing endorsement for the language in production.
From what I can tell, not fully. Which is really disappointing if they're going to call it a 1.0 release without supporting the world's most popular OS.
This is how you get written off as a toy language and not taken seriously. If I sound bitter, it's because I really am. I've tracked this project for years, donated to the core team, written a sideproject app in Kemal, and I would love to see an actual, production-ready version of Crystal. That's what 1.0 should signify. Instead, it seems like they're calling it 1.0... just because?
From the announcement that was (over a year ago at this point):
> "The challenge at hand is to get as quickly as possible to a 1.0 version of Crystal that is at the same time as faithful as possible to the current state of the language, stable enough for individuals and organizations to feel comfortable adopting it for even their highest impact projects, and a solid foundation for future major versions."
Not having Windows or concurrency support is neither of those things. Why is, "as quickly as possible," a goal?
To be perfectly clear, I'm not bitching about the speed or timeline. I'm bitching about the weird decision to designate a half-baked release as 1.0, which is going to turn off a lot of people that could otherwise consider using Crystal in production some day. Not to mention organizational support. I work as a cloud engineer, and we write a lot of scripts to drop onto (Windows AND Linux) servers to automate tasks. If I floated using this proposed 1.0 to my boss, he would just laugh. I wouldn't blame him either. Guess I'll stick with Go.
Glad you're excited about Crystal :)
FYI, In the "exception handling" section, it can be shortened further to just:
def self.mount?(path) stat_path = File.lstat(path) rescue Errno # It doesn't exist so not a mount point return false end
The begin
statement is implied in any block: https://crystal-lang.org/docs/syntax_and_semantics/exception_handling.html#short-syntax-form
include?
-> includes?
, key?
-> has_key?
) and keep an eye for the more subtle differences (like each
returning nil
in Crystal instead of self
).nil
unless you are explicit about it (not_nil!
, the getter!
macro, as
casts...).rescue => ex
-> rescue ex
my_var.tap(&:my_method)
-> my_var.tap(&.my_method)
my_var &.my_method1 &.my_method2
-> my_var.try &.my_method1.my_metod2
Porting a big Ruby app to Crystal is not something that can be done quickly and on a whim, but it certainly is easier that porting from any other language.
https://matrix.org/docs/api/client-server/#!/Room32participation/sync
Try using the timeout
, full_state
and since
parameters to limit polling and the size of the response. (I think this is what you're looking for..?)
Array(Array(Int))
is what you could use in a type restriction. For a method parameter or return value at least.
EDIT: Crystal has various chat channels that might be easier for quick questions like this: https://crystal-lang.org/community/#chat.
The more modern approach would be
file_data = {{ read_file "#{DIR}/README.md" }}
If I understood correctly, you need an array of the filenames so that they can later be used for selection.
Then (if I am correct), you can create an empty array of string outside the Zip::File.open block
And then inside the file.entries.each do |entry|
block, do Array#push (https://crystal-lang.org/api/0.32.1/Array.html#push(value:T)-instance-method)
Instance vars can be modified from outside the current scope in a concurrent environment. Thus the value of `@list` can theoretically change between the not-nil-check `@list` and the call to `@list.empty?`.
Local vars are only visible in the current scope and the compiler can make sure there is no chance of external modification.
See https://crystal-lang.org/reference/syntax_and_semantics/if_var.html#limitations
`HTTP::Server#listen` has a `reuse_port` option ( only works on linux I belive ). This lets you run multiple instances of your server that will all listen on the same port.
There's <code>IO#read_bytes</code> which allows you to specify a type and "byte format" (endian-ness).
No idea if it's any faster though.
https://crystal-lang.org/docs/syntax_and_semantics/macros/hooks.html does not mention the finished
hook:
class Abc macro finished def self.hi puts "Hello" end end end
Abc.hi
The above will print <code>Hello</code>. You could use this to for example implement proper JSON mappings (with functioning subclassing and all).
Thanks for the reply! What I have now is somewhat similar. I'm using the Tumblr API, by the way.
For now, I'm not worried about going through OAuth flow, I just have the access tokens right off the bat. My initialize looks something like this -
def initialize( consumer_key : String, consumer_secret : String, oauth_token : String, oauth_token_secret : String)
# create some nifty oauth stuff consumer = OAuth::Consumer.new(Host, consumer_key, consumer_secret) token = OAuth::AccessToken.new(oauth_token, oauth_token_secret)
@http_client = HTTP::Client.new(Host) consumer.authenticate(@http_client, token) end
I'm a little confused at the consumer.authenticate
part - is it modifying the @http_client
passed in as the parameter? Apologies for the newbie questions, I'm still getting used to how Crystal does stuff.
What would an example of getting the resource "/user/info"
look like? Right now I have @http_client.get("/user/info")
and it's giving me getaddrinfo: Name or service not known (Socket::Error)
By the way, I'm really digging how OAuth is built right into the stdlibs. Dealing with OAuth can be a mess sometimes, and I think this can help make things more consistent :)
Edit: just figured it out
I was using HTTP::Client.new("api.tumblr.com/v2"). Since it actually takes the host and not a URL, I just had to drop the v2
part and add it when I do get
requests. Works amazingly! OAuth can be difficult but this was one of the best experiences I've had in any language :)
I used @link
originally, because the hello world program did. However, it seems I recieved an error that I should use @@link
when using self.send
. After removing them, my code runs fine. Thank you.
cc: /u/valbaca
I've been using JSON::Serializable -- creating a class with known mappings, and then calling .from_json
.
Another option is to use the JSON::Any#as_*
methods on each field. JSON::Any docs here.
Not sure of how to convert it into a typed Hash.
Try with Dir.glob("*", match_hidden: true)
If you look at the method definition, the first arg is *patterns
which will match all positional arguments. To be able to specify the arg match_hidden
you need to call it using a named arg.
See https://crystal-lang.org/reference/syntax_and_semantics/default_values_named_arguments_splats_tuples_and_overloading.html for more info on that!
In case you didn't know, there is also property!
https://crystal-lang.org/api/0.25.1/Object.html#property!(*names)-macro that comes handy on lazy initialized properties. But I encourage to avoid lazy initialized properties when possible.
But zip
traverses both array in tandem. What you want is Array#product:
https://play.crystal-lang.org/#/r/4h5c
In your case:
st_from_site.product(st_from_oldr) do |s, ss|
s.last_dump = ss.last_dump if s.name == ss.name && s.id == ss.id
end
stations.concat(st_from_site)
The playground show the values after an actual execution. I think you don't want that. In some way the crystal type inference is more like a symbolic execution performed upon an invocation is reached.
There are two ways it might be helpful to you:
# file: foo.cr
def behaviour(a)
a + a
end
res = behaviour(2)
$ crystal tool types foo.cr | grep res
res : Int32
but you need at least one invocation in the main body. Otherwise the behaviour function is never compiled.
I strongly suggest you use <code>JSON.mapping</code> so that you can use to_json
on the character class directly.
Don't worry about a memory leak, it's the job of the GC to cleanup the Character
instances when they are no longer used.
And you might find using C with Crystal to be nice: • https://crystal-lang.org/docs/syntax_and_semantics/c_bindings/ • http://www.crystalforrubyists.com/book/book.html (scroll to bottom section "C Bindings")
Hi!
Check https://crystal-lang.org/api/0.21.1/HTTP/Params/Builder.html so you don't need to https://github.com/sb89/crystal-darksky/blob/master/src/darksky/client.cr#L15
In JSON.mapping, you can use field: Float32?
instead of field: {type: Float32, nilable: true}
Every dev I know that has a windows machine also has wsl on it. You can even use vscode remote with your local wsl pretty easily. That's an all-Microsoft solution, not a potentially scary hacky thing.
May I ask what you have in mind when you suggest Windows native being a big hurdle to adoption?
This is a cool article, but there are two things here that confused me a bit:
It's not a biggie. I was commenting on organization, not really a lack of features. For comparison, checkout https://golang.org/pkg/net/ - note how everything is neatly classified / organized there. Crystal's API reflects (pun!) Ruby's API, which is a bit disorganized in contrast.
Are you aware of https://crystal-lang.org/reference/guides/static_linking.html ?
The limitations (or hurdles) to static linking are solely based on libc. On Mac, you can't statically link libc at all. On Linux, the most commonly used glibc doesn't work well with static linking either. But you can perfectly fine compile fully statically linked Crystal binaries on Linux by linking against musl-libc. This works really well and many Crystal applications are distributed as statically linked executables.
Go avoids that by implementing their standard library without relying on libc. That's a huge effort to build on your own. And the downside is it complicates interoperability with C libraries.
https://snapcraft.io/crystal is nice as well. Also allows easily installing the nightly version.
sudo snap install crystal --classic
for stable
sudo snap install crystal --edge --classic
for nightly
Fair enough. Did you actually benchmark the two approaches and notice a perf difference? LLVM is pretty good in terms of optimization so I wouldn't be surprised if they're the same.
There's also https://crystal-lang.org/reference/syntax_and_semantics/annotations/built_in_annotations.html#alwaysinline which could be useful in some cases.
My apologies, I didn't see the rest of the file - I wonder why the (docs)[https://crystal-lang.org/api/1.0.0/Signal.html] mention fibers then
>Signals are dispatched to the event loop and later processed in a dedicated fiber. Some received signals may never be processed when the program terminates.
This looks like using crystal-imgui-sfml but linking against the "vanilla" version of libcimgui.so, rather than the one provided by crystal-imgui-sfml.
Would be glad to discuss it in some chat
> Truly static binary deployments seem to not be fully supported in Crystal just yet
I don't think this is entirely accurate. Static binaries in Crystal are possible, you just need to build the binary in an Alpine image. The reasoning is it uses musl-libc
which allows it to be statically linked, as opposed to gnu-libc
.
See https://crystal-lang.org/2020/02/02/alpine-based-docker-images.html
\cc \u\myringotomy
Fair enough. I think you would be better off making your own Log::Backend specific to fluentd
. Then anyone who also uses it could just plug in the backend and have everything just work as opposed to essentially maintaining your own implementation of what the stdlib does and requiring the user to use two separate implementations.
Alternatively, you can define a custom Log::Formatter to support converting the Log::Entry
into some (more readable) format.
I came here after seeing that they sponsor the Crystal project. The fact that they're pushing hard on this language makes me want to dive into it as well. It's the equivalent of DHH developing Ruby/Rails at Basecamp.
This is massive for the language and the long term support of it.
It's a bit weird, yeah. Maybe because our domain is crystal-lang.org and our GH org is crystal-lang? But then their source package is crystal
...
Well maybe the answer is the same as for anything else in Debian, it's policy :D
The secret sauce in this coding session is made of two ingredients * Crystal generics * Functions as first-class citizens
Gotcha, so in that case I'd do something like:
class Encipher def initialize(@message : String) puts "Your original message is: #{message}" end
def message : String @message.reverse end end
print "Enter your name: "
if (user_input = gets) && !user_input.blank? puts Encipher.new(user_input).message else STDERR.puts "Invalid input!" end
Using https://crystal-lang.org/api/String.html#reverse-instance-method
I'd also suggest joining the gitter channel. Would be easier than going back and forth on here.
Multithreading has already landed in the Crystal runtime, see [blog post](https://crystal-lang.org/2019/09/06/parallelism-in-crystal.html).
It's not yet 100% stable so it's hidden behind a feature flag. I don't think there's a tracking issue on GitHub for what's left to be done. /u/bcardiff knows more.
A good help would probably be to just using it experimentally to find bugs.
I would take a look at how [delegate](https://crystal-lang.org/api/master/Object.html#delegate(*methods,toobject\)-macro) works in the stdlib.
Essentially it expands into a method like what you're doing, but uses splats to pass the arguments.
delegate :foo, to: @var # => def foo(*args, **options) @var.foo(*args, **options) end
Although I can't say what you're doing would be considered a good/best practice.
You can (since I think 0.32) set a number of worker threads that fibers will be run in. https://crystal-lang.org/2019/09/06/parallelism-in-crystal.html
Still seems sorta new and experimental. I've only played around with it a bit.
Ah I think i see the issue.
https://github.com/oren/note/blob/master/src/note.cr#L45
Are you hitting this code? You might want to use File.expand_path with the home: true
option to resolve ~
to the home dir.
Ahh I think I see what you want to do, make the most recent entry appear at the top; in which case yea what you're doing is prob the easiest.
If I had to guess, the issue is that it'll create the file if it doesn't exist, but it does not handle creating the directory path TO that file.
So, I would try creating the directories first.
Also a few tips from glancing at your code.
crystal tool format
, and checkout the style guide.abort "Error trying to parse the config file. #{ex.message}"
which would handle writing the error to STDERR
and returning 1
exit code.File.open
to handle writing notes_file_path
, versus reading it to a string just to write it back to a file.Something like:
record Config, note_location : String do include JSON::Serializable end
...
con = Config.from_json File.read(@config_file_path)
con.note_location
EDIT: Add example of writing notes
# You can just open the file in append mode File.open(@notes_file_path, "a") do |notes_file| notes_file << Time.local.to_s("%Y-%m-%d") if @date notes_file << "\n\n" end
I started a smallish app using Kemal, and it felt great, to be honest. It felt like writing Ruby but much... safer? It's also much cleaner than using Sorbet. I have a lot of hope for Crystal, but it's just not quite there yet, unfortunately.
According to the official docs, there will be breaking changes to the API before hitting 1.0. For anything beyond a few scripts, that's a big no for me. Also, take a dive into the issue trackers for any 3rd party libs you think you might use; the community support just isn't quite there yet. If you're banking on using a lib for critical stuff like auth and an ORM, you're really playing with fire. What if your auth lib sees that v0.5 has some huge API change that requires a partial rewrite, then decides it's not worth the maintenance effort?
I ended up doing a rewrite in Go. Go is honestly fantastic once you get into the right mindset, and I would very highly recommend it. That said, I'm subscribed here to keep tabs on the project, and I get excited every time I see Crystal mentioned in the wild. If more Ruby devs catch wind of this, all it'd take is a big corporate sponsorship (Salesforce? Shopify?) to really get the ball rolling. In the meantime, I'll keep contributing my $5 per month and waiting for good news.
>but I could be wrong.
You're very wrong :) `{{` and `}}` are delimiters for macro expressions.
See https://crystal-lang.org/reference/syntax_and_semantics/macros.html
Hey u/pynix, excellent question. If object allocation is a bottleneck in your application, then struct seems to be the way to go - see Use structs when possible.
That said, I really suggest you profile your application with both, as passing objects by reference can sometimes lead to higher performance. It really depends on the memory allocation patterns showing in your application.
I also think we don't have enough benchmark on struct vs class when it comes to concurrent applications and passing objects over channels - something I'd like to look into in the future.
This PR lead to a pretty extensive conversation on the topic.
I hit a dead link from the Windows installation instructions it gives you a link for an installation on Ubuntu and just shows - puts " Page not found" this is the link https://crystal-lang.org/installation/on_wsl/on_ubuntu
From my point of view, it works as it should now (because NamedTuple is immutable). For now the best would be to add an example here: https://crystal-lang.org/reference/syntax_and_semantics/splats_and_tuples.html.
Neat! Some small suggestions from just doing a quick look over. Mainly just things that could be cleaned up using some already built in stuff.
I don't think you need the custom generate_random_bytes
method, there is a #random_bytes method on the Random
module you could use.
For the #time
method, you could probably use the block variant of the <code>getter</code> macro.
If a block is given to the macro, a getter is generated with a variable that is lazily initialized with the block's contents:
class Person getter(birth_date) { Time.local } end
Which would calculate it when the method is called, then use that cached version from then on.
Did you know that Crystal's doc generator already supports some keywords like `NOTE`? See https://crystal-lang.org/reference/conventions/documenting_code.html#flagging-classes-modules-and-methods
There should be more information on the exact failure in the output above that line. But you're most likely missing one or more of the libraries required for building your application. Make sure you have `libpcre`, `libgc`, `libpthread`, `libevent`, `librt`, `libdl` available.
Without any further information on your environment, I can only recommend looking at the installation instructions provided at https://crystal-lang.org/reference/installation/
I disagree that learning Ruby will necessarily help you when it comes to Crystal, and I didn't like Derek Banas's "tutorial"/demo. Crystal wasn't my first language, but I learned it by simply reading the book, front to back. I found that very effective.
That is true, but all it would do is mimic the functionality so it doesn't seem worth reinventing the wheel, so to say. Using JSON::Serializable also allows the library to take advantage of it's other features here: https://crystal-lang.org/api/0.25.1/JSON/Serializable.html
After looking how: https://crystal-lang.org/api/0.25.1/JSON/Serializable.html does it, I have been able to mimic this behaviour in my library (just requiring json). Better error handling during sql -> object.
Would still be interested to hear your thoughts
Cheers
Hi in the docs they do it like this
module Curses class Window end end
Curses::Window.new
Is this what you meant?
File
is an IO::FileDescriptor
, which includes the module IO::Buffered
.
I think the problem comes from the fact that every read/write operation on a File
is buffered.
The buffering can be disabled using f.read_buffering = false
for read, and f.sync = true
for write.
(doc: https://crystal-lang.org/api/0.34.0/IO/Buffered.html)
Note: I've opened an issue mentioning this question to improve the consistency when changing read/write buffering: https://github.com/crystal-lang/crystal/issues/9023