[HN Gopher] Ruby 3.4 frozen string literals: What Rails develope...
___________________________________________________________________
Ruby 3.4 frozen string literals: What Rails developers need to know
Author : thomas_witt
Score : 190 points
Date : 2025-07-06 09:58 UTC (3 days ago)
(HTM) web link (www.prateekcodes.dev)
(TXT) w3m dump (www.prateekcodes.dev)
| teddyh wrote:
| TIL that Ruby has mutable strings, and (until the announced
| change) even had them mutable _by default_ (and the change only
| affects literal strings; non-literal strings are _still_
| mutable). Python has always only ever had immutable strings.
| llamataboot wrote:
| just dont ask about unicode
| neallindsay wrote:
| Unicode support in Ruby has been great since the beginning.
| yxhuvud wrote:
| No, it was not great during 1.x times. But it has been
| fairly good since 2.0
| WJW wrote:
| So for at least 12 years then. 2.0 was released in 2013.
| steveklabnik wrote:
| It's a bit weirder than that, in my opinion. Ruby didn't
| really gain "unicode support" in the sense we mean it today
| until 1.9.
|
| Before that, Ruby did "support encodings" in a sense, but a
| lot of the APIs were byte oriented. It was awkward in
| general.
|
| https://web.archive.org/web/20180331093051/http://graysofti
| n...
| llamataboot wrote:
| right it was the python string transition i was talking
| about
| 0x457 wrote:
| Only if you count 1.9.2 as the beginning. What is being
| talked about is Unicode by default and maybe Unicode
| tooling (i.e. can correctly iterate over emojis and not
| just bytes)
| thibaut_barrere wrote:
| We have mutable default arguments in Python
| (https://docs.python-guide.org/writing/gotchas/#mutable-
| defau...), by default too, though.
| teddyh wrote:
| Not if they are strings, which is what this article is about.
| skywhopper wrote:
| Cool, good for you! I learned this in 2005.
| WJW wrote:
| Cool, good for you!
| tangus wrote:
| Strings are going to keep being mutable by default. Only
| strings created by string literals won't be.
| teddyh wrote:
| Thanks for the clarification! I have adjusted my wording.
| __s wrote:
| In Ruby you tend to use :symbol for small immutable strings
|
| << is inplace append operator for strings/arrays, while + is
| used to make copy. So += will make new string & rebind variable
| corytheboyd wrote:
| I'll just kill the comment. It said Symbol isn't garbage
| collected. It has been since 2.2 and I wasn't aware. Sorry.
|
| Good reminder that anyone can go on the internet, just say
| stuff, and be wrong.
| jashmatthews wrote:
| Symbols have been GCed since CRuby 2.2 https://bugs.ruby-
| lang.org/issues/9634
| corytheboyd wrote:
| Well thats great, guess I have carried that baggage with
| me as misinformation for years now.
| hinkley wrote:
| One of the things I've said I would do and never did is
| create a set of test suites for as many facts about a
| language as I know and then run it for every new release
| to see what I need to unlearn.
|
| Most but not all of these were performance related. If it
| took a few days to run that's fine. Major versions don't
| come out _that_ often.
| zht wrote:
| dynamically created symbols have been garbage collected for
| almost 10 years
| corytheboyd wrote:
| Yeah I am a moron. Updated.
| dragonwriter wrote:
| Strings will still be mutable by default after the change which
| only makes string _literals_ always frozen (which has been a
| file-level opt-in for a while.)
| ttoinou wrote:
| Is it the future path of any successful JIT / dynamic typed /
| scripting language to realize they needed all optimizations from
| compiled / statically typed / lower level languages ?
|
| Would Ruby be as successful if they had all those complicated
| features right from the start ?
|
| Or do all languages start from a nice simple clean slate tabula
| rasa to get developers hooked, until the language is enough
| famous to get well developed and starts to be similar to all
| others big programming languages ?
| OskarS wrote:
| I would actually say it's the opposite in this case: it's
| extremely common in scripting languages for strings to be
| immutable, mutable strings are usually only available in lower
| level languages. I'm very surprised Ruby had this feature at
| all.
| TkTech wrote:
| All strings in python are immutable, as an example.
| masklinn wrote:
| Also JavaScript.
| fud101 wrote:
| Java too. Rust has only 99 string types, some of those
| are immutable also.
| masklinn wrote:
| It does not really matter in rust anyway: literals are
| &'static str so naturally immutable and essentially free,
| and mutable strings require jumping hoops to share, so
| it's hard to unwittingly have one mutate from under you.
| steveklabnik wrote:
| Rust the language has one string type: &str
|
| The standard library also has String, CString, CStr,
| OsString, and OsStr.
|
| The latter four are for niche situations. 99.9% of the
| time, it's similar to Java: &str is Java's String, String
| is Java's StringBuffer/StringBuilder.
| ameliaquining wrote:
| I would add Path and PathBuf to that list.
| steveklabnik wrote:
| Paths are not strings, that's the whole point!
| shikon7 wrote:
| There is also &mut str. The nice thing about Rust is that
| the same memory area can be used as a "StringBuilder"
| with &mut str, and then as a "String" with &str. Once you
| have a &str reference, Rust statically guarantees that no
| one else cam mutate it.
| ameliaquining wrote:
| All of those languages except JavaScript have mutable
| strings; they just have different names and API shapes.
| Python has io.StringIO, Java has StringBuilder, Rust has
| String. (JavaScript has less need for this because
| JavaScript strings are ropes in most widely used
| implementations, reducing the overhead of concatenation,
| though the language spec doesn't require this.)
| superjan wrote:
| It's true that many languages have immutable strings, but for
| a web-focused scripting language it makes sense to default to
| mutable strings. I think the comment is about that you now
| need to choose mutable vs immutable, and that is framed as a
| consequence of broader adoption. Which is a development I
| have also seen before.
| empthought wrote:
| > for a web-focused scripting language it makes sense to
| default to mutable strings
|
| Why do you say that? I would say the opposite.
| superjan wrote:
| Because there'll likely be a lot of string concatenation
| in a language whose job is to output HTML. Adding a
| single char to a 1k string will require allocating a
| modified copy, in mutable strings it's just appending in
| a preallocated buffer.
| stouset wrote:
| If you are using string concatenation to build HTML in
| your application layer, you are fundamentally doing it
| wrong.
|
| Ruby isn't making all strings immutable here. Just string
| literals. You are free to allocate mutable strings that
| can be appended to, to your heart's content. It is
| _extremely_ rare that modifying a literal is intended
| behavior, since their contents are permanently persisted
| throughout the lifetime of your program. With your
| example, this would be like having one shared global
| buffer for your final document.
| ttoinou wrote:
| Exactly thank you. Not sure why you're downvoted
| copirate wrote:
| > for a web-focused scripting language
|
| Ruby is not a web focused scripting language.
| wavemode wrote:
| Even in C, string literals are immutable, and mutating them
| is undefined behavior.
| ttoinou wrote:
| But in the syntax of scripting language its very easy to
| create a new string from the old string, destroy old string,
| replace variable by new string. Which appears mutable from
| the point of view of the developer
| SomeoneOnTheWeb wrote:
| Can't you do the exact same in compiled languages e.g. C or
| Rust?
| shikon7 wrote:
| You can, but allocating and deallocating is more tedious
| without a GC, and if you care about performance, you
| might want to have mutable strings.
| fiddlerwoaroof wrote:
| Common Lisp and Smalltalk have mutable strings, I think. So
| it's not too surprising that ruby does too, since it was
| pretty heavily influenced by these languages.
| riffraff wrote:
| Mutable strings were part of the perl heritage, which was one
| of the direct ancestors for ruby.
| bastawhiz wrote:
| > all optimizations from compiled / statically typed / lower
| level languages
|
| Mutable strings are totally possible (and not even especially
| hard) in compiled, statically typed, and lower-level languages.
| They're just not especially performant, and are sometimes a
| footgun.
|
| > all those complicated features right from the start
|
| Arguably, mutable strings are the more complicated feature.
| Removing them by default simplifies the language, or at least
| forces you to go out of your way to find the complexity.
| IshKebab wrote:
| > They're just not especially performant
|
| What? Mutable strings are _more_ performant generally.
| Sometimes immutability allows you to use high level
| algorithms that provide better performance, but most code
| doesn 't take advantage of that.
| vnorilo wrote:
| Not in general. Immutable strings can be deduplicated,
| leading to a different performance tradeoff that is often
| quite good. This is mentioned in TFA.
| IshKebab wrote:
| Mutable strings can be duplicated too. You can use
| reference counting, or borrow checking in Rust.
| spankalee wrote:
| Mutable string literals can't be easily deduplicated,
| unless your language semantics are that a literal is a
| singleton and all mutations are visible by all other
| evaluations of that literal. But no sane language would
| do that.
| ttoinou wrote:
| Its not about possible / not possible its about what the
| language does by default and how language syntax changes to
| switch between different variants of strings
| throwawaymaths wrote:
| erlang is jitted and dynamic and all terms are immutable.
| ttoinou wrote:
| A lot of functional programming languages are like this and
| that can make them not efficient
| llamataboot wrote:
| We learned nothing from Python 2->3
|
| An obviously good change, actually massive performance
| improvements not hard to implement but its still gonna be such a
| headache and dependency hell
| hnlmorg wrote:
| This change doesn't really compare to Py 2 vs 3
| skywhopper wrote:
| ??? This is nothing like the Python transition. In Python there
| were two incompatible language versions side by side for years
| that made it really hard on library maintainers. Ruby is giving
| a 7-8 year transition period before this even hits, with years
| of warnings built-in to the plan. What more would you have them
| do?
| lloeki wrote:
| Not to mention that in addition to the opt-in warning that
| came with 3.4, if you've been using any reasonable linter
| such as Rubocop for the past 10ish years then you're already
| being yelled at for lack of `# frozen_string_literal: true`
| magic comment.
| neallindsay wrote:
| Ruby has been extremely slow and deliberate in rolling out
| frozen string literals. They added a magic comment to opt in to
| them on a per-file basis all the way back in Ruby 2.3--almost a
| decade ago.
|
| https://www.ruby-lang.org/en/news/2015/12/25/ruby-2-3-0-rele...
|
| Most linting setups I've seen since then have required this
| line. I don't expect many libraries to run afoul of this, and
| this warning setting will make finding them easy and safe. This
| will be nothing like the headache Python users faced
| transitioning to 3.
| llamataboot wrote:
| I hope this is corect - i do agree it has been a long and
| slow migration path and migrating is fairly easy - migrating
| python 2 to 3 code was fairly easy as well anyone could do it
| in their codebase, it remains a big deal and possibly very
| impactful to make such breaking changes to the behavior of
| primitives in mature ecosystems. How many gems does the
| average rails app have, okay they all need to be updated and
| they sohld be being updated for other reasons, I remain
| skeptical of how smooth the change is going to be over all
| ecosystem wise but time will tell.
|
| I agree it has been a well advertised and loudly migration
| path and timeframe for it
| arp242 wrote:
| Most of the problems in Python 3 were the string encoding
| changes. It was very pervasive, fixing things was often not so
| straight-forward, and writing programs that worked well with
| Python 2 and 3 was possible but somewhat difficult and error-
| prone.
|
| The rest of the changes were a bit annoying but mostly boring;
| some things could have been done better here too, but the
| string encoding thing was the main issue that caused people to
| hold on to Python 2 for a long time.
|
| The frozen string literal changes are nothing like it. It's
| been "good practise" to do this for years, on errors fixing
| things is trivial, there is a long migration path, and AFAIK
| there are no plans to remove "frozen_string_literal: false".
| It's just a change in the default from false to true, not a
| change in features.
|
| "Learning lessons" doesn't mean "never do anything like this
| ever again". You're the one who failed to learn from Python 3,
| by simply saying "language change bad" without deeper
| understanding of _what_ went wrong with Python 3, and how to do
| things better. Other languages like Go also make incompatible
| changes to the language, but do so in a way that learned the
| lessons from Python 3 (which is why you 're not seeing people
| complain about it).
| byroot wrote:
| Even if you have an incompatible codebase that you don't wish
| to convert, you'll be able to set `RUBYOPT="--disable-frozen-
| string-literal"` so it keeps running.
|
| And since that flag really doesn't require lots of work in the
| VM, it's likely to be kept around pretty much forever.
| skywhopper wrote:
| This looks like a really thoughtful and well-planned transition,
| with years of opt-in warnings followed by years of opt-out
| warnings before the actual cutover. I'm constantly impressed with
| the methodical approach of the Ruby team to constantly improving
| the language in ways that disrupt their users as little as
| possible.
| _flux wrote:
| Btw, OCaml also transitioned from read-write Strings to read-only
| Strings, and Buffer to be that read-write string. It was
| introduced in 4.02 released September 2014.
|
| I recall it was a bit bumpy, but not all that rough in the end. I
| suppose static type checking helps here to find all the ways how
| it could be used. There was a switch to allow running old code
| (to make strings and buffers interchangeable).
| debugnik wrote:
| Nitpick: `bytes` is the read-write string, `Buffer` is the
| StringBuilder-like.
| dragonwriter wrote:
| > Btw, OCaml also transitioned from read-write Strings to read-
| only Strings
|
| Ruby is not doing that, it's transitioning from mutable strings
| that can be frozen with no special treatment of literals
| (unless you opt-in to literals being frozen on per file basis)
| to mutable strings with all string literals frozen.
| samgranieri wrote:
| This should hopefully go over easier than the keywords arguments
| transition.
| bitbckt wrote:
| Just a bit under 15 years after we did this at Twitter.
| ksec wrote:
| I would assume if Shopify and Github are on board then Rails is
| pretty well tested.
| jbverschoor wrote:
| The amount of misconceptions in this thread about mutable
| strings...
| ttoinou wrote:
| Like what ?
| meisel wrote:
| How does this work under the hood? Does Ruby keep a giant map of
| all strings in the application to check new strings against to
| see if it can dedupe? Does it keep a reference count to each
| unique string that requires a set lookup to update on each string
| instance's deallocation? Set lookups in a giant set can be pretty
| expensive!
| shagie wrote:
| The literals would be identified at parse time.
| fooLit = "foo" fooVar = "f".concat("o").concat("o")
|
| This would have fooLit be frozen at parse time. In this
| situation there would be "foo", "f", and "o" as frozen strings;
| and fooLit and fooVar would be two different strings since
| fooVar was created at runtime.
|
| Creating a string that happens to be present in the frozen
| strings wouldn't create a new one.
| meisel wrote:
| Got it, so this could not be extended to non-literal strings
| shagie wrote:
| You can freeze strings that are created at runtime.
| irb(main):001> str = "f".concat("o").concat("o") =>
| "foo" irb(main):002> str.frozen? => false
| irb(main):003> str.freeze => "foo"
| irb(main):004> str.frozen? => true
| irb(main):005> str = str.concat("bar") (irb):5:in
| 'String#concat': can't modify frozen
| #<Class:#<String:0x000000015807ec58>>: "foo" (FrozenError)
| from (irb):5:in '<main>' from
| <internal:kernel>:168:in 'Kernel#loop' from /opt/h
| omebrew/Cellar/ruby/3.4.4/lib/ruby/gems/3.4.0/gems/irb-1.14
| .3/exe/irb:9:in '<top (required)>' from
| /opt/homebrew/opt/ruby/bin/irb:25:in 'Kernel#load'
| from /opt/homebrew/opt/ruby/bin/irb:25:in '<main>'
| tangus wrote:
| Even if it didn't dedupe strings, mutable string literals means
| that it has to create a new string every time it encounters a
| literal in run time. If you have a literal string in a method,
| every time you call the method a new string is created. If you
| have one inside a loop, every iteration a new string is
| created. You get the idea.
|
| With immutable strings literals, string literals can be reused.
| hinkley wrote:
| Here's a more concrete example:
|
| You make an arrow function that takes an object as input, and
| calls another with a string and a field from the object, for
| instance to populate a lookup table. You probably don't want
| someone changing map keys out from under you, because you'll
| break resize. So copies are being made to ensure this?
| masklinn wrote:
| The way it works in Python is that string literals are stored
| in a constant slot of their parent object, so at runtime the VM
| just returns the value at that index.
|
| Though since Ruby already has symbols which act as immutable
| interned strings, frozen literals might just piggyback on that,
| with frozen strings being symbols under the hood.
| steveklabnik wrote:
| > How does this work under the hood? Does Ruby keep a giant map
| of all strings in the application to check new strings against
| to see if it can dedupe?
|
| 1. Strings have a flag (FL_FREEZE) that are set when the string
| is frozen. This is checked whenever a string would be mutated,
| to prevent it.
|
| 2. There is an interned string table for frozen strings.
|
| > Does it keep a reference count to each unique string that
| requires a set lookup to update on each string instance's
| deallocation?
|
| This I am less sure about, I poked around in the implementation
| for a bit, but I am not sure of this answer. It appears to me
| that it just deletes it, but that cannot be right, I suspect
| I'm missing something, I only dig around in Ruby internals once
| or twice a year :)
| byroot wrote:
| There's no need for ref counting, since Ruby has a mark &
| sweep GC.
|
| The interned string table uses weak references. Any string
| added to the interned string tables has the `FL_FSTR` flag
| set to it, and when a string a freed, if it has that flag the
| GC knowns to remove it from the interned string table.
|
| The keyword to know to search for this in the VM is
| `fstring`, that's what interned strings are called
| internally:
|
| - https://github.com/ruby/ruby/blob/b146eae3b5e9154d3fb692e8f
| e...
|
| - https://github.com/ruby/ruby/blob/b146eae3b5e9154d3fb692e8f
| e...
| steveklabnik wrote:
| Ah, the value of FL_FSTR was what I was missing, I had
| followed this code into rb_gc_free_fstring without
| realizing what FL_FSTR meant. Thank you!
| baggy_trough wrote:
| I wonder what the basis is for the description of the 3.7 / 4
| ruby releases is. I haven't seen this transition plan with
| version numbers described outside of this blog post.
| MallocVoidstar wrote:
| It's used as an example here: https://bugs.ruby-
| lang.org/issues/20205
|
| But not actually stated it's the plan. I'd bet whatever LLM
| wrote the article took it as a stronger statement than it is.
| prateekkish wrote:
| Hey there. I wrote the article. While I know the version
| numbers aren't concrete, I added the proposal anyways as a
| way for readers to visualise what the maintainers had in
| mind. Since we're only at 3.4 with 3.5 in preview, it can't
| be claimed concretely what the future holds. I just didn't
| make that super obvious in the post.
|
| I had to explain the same reasoning in Reddit the other day.
| Perhaps it's time to take this as a feedback and update the
| blog.
|
| Btw I just asked gpt to write an article on the same topic,
| with a reference to the Ruby issues page. And it DID NOT add
| the future proposal part. So LLMs are definitely smarter than
| me.
| maximegarcia wrote:
| Well it is not quite a mutable vs immutable strings war, nor Ruby
| being late to the party or something like that.
|
| The move is so we can avoid allocating a string each we declare
| and use it since it will be frozen by default. It is a big
| optimization for GC mainly. Before we had to do such optimization
| by hand if we intend not to modify it: # before
| def my_method do_stuff_with("My String") # 1 allocation
| at each call end # before, optim
| MY_STRING = "My String".freeze # this does 2 allocations with 1
| at init being GC quite early def my_method
| do_stuff_with(MY_STRING) end # after
| def my_method do_stuff_with("My String") # 1 allocation
| first time end
|
| But this move also complicates strings manipulation in the sense
| of it will lean users toward immutable ops that tend to allocate
| a lot of strings. foo.upcase.reverse #
| VS bar = foo.dup bar.upcase! bar.reverse!
|
| So now we have to be deliberate about it:
| my_string = +"My String" # it is not frozen
|
| We have frozen string literals for quite a while now, enabled
| file by file with the "frozen_string_literal: true" comment and
| I've seen it as the recommended way by the community and the de-
| facto standard in most codebase I've seen. It is generally
| enforced by code quality tools like Rubocop.
|
| So the mutable vs immutable is well known, and as it is part of
| the language, well, people should know the ins and outs.
|
| I'm just a bit surprised that they devised this long path toward
| real frozen string literals, because it is already ongoing for
| years with the "frozen_string_literal: true" comment. Maybe to
| add proper warnings etc. in a way that does not "touch" code ? I
| prefer the explicit file by file comment. And for deps, well, the
| version bump of Ruby adding frozen string literals by default is
| quite a filter already.
|
| Well, Ruby is well alive and it is what matters)
| taeric wrote:
| It is sorta late to the party. Common Lisp has similar with
| regards to how lists are done. Specifically, it is not uncommon
| to make a static list like `'(1 2 3)`. Doing this, however, has
| implications on what operations you can do on the data
| elsewhere.
|
| I say sorta late to the party, as I think it is more than fair
| to say there was not much of a party that folks were interested
| in in the lisp world. :D
| byroot wrote:
| > I'm just a bit surprised that they devised this long path
|
| The original plan was to make the breaking change in 3.0, but
| that plan was canceled because it broke too much code all at
| once.
|
| Hence why I proposed this multi-step plan to ease the
| transition.
|
| See the discussion on the tracker if you are curious:
| https://bugs.ruby-lang.org/issues/20205
| hinkley wrote:
| How's the htmlsafe trick going to work if strings are immmutable?
| byroot wrote:
| First, only literal strings are concerned. Second, Rails's
| `String#html_safe` method's return value is a new instance, it
| doesn't mutate the receiver in place.
| phendrenad2 wrote:
| Has anyone actually benchmarked the use of frozen string
| literals? I feel like this is one of those micro-optimizations
| that everyone does, but they're probably accomplishing a
| diminishingly small performance improvement, while making the
| codebase less readable. On net, a negative.
| byroot wrote:
| Yes: https://bugs.ruby-lang.org/issues/20205#note-34
| lcnPylGDnU4H9OF wrote:
| I often do something like SUB_ME =
| ':sub_me'.freeze def my_method(method_argument) foo
| = 'foo_:sub_me' foo.sub!(SUB_ME, method_argument)
| foo end
|
| which, without `# frozen_string_literal: true`, I believe
| allocates a string when the application loads (it sounds like it
| might be 2) and another string at runtime and then mutate that.
|
| That seems like it's better than doing #
| frozen_string_literal: true FOO = 'foo_:sub_me'
| SUB_ME = ':sub_me' def my_method(method_argument)
| FOO.sub(SUB_ME, method_argument) end
|
| because that will allocate the frozen string to `FOO` when the
| application loads, then make a copy of it to `foo` at runtime,
| then mutate that copy. That means two strings that never leave
| memory (FOO, SUB_ME) and one that has to be GCed (return value)
| instead of just one that never leaves memory (SUB_ME) and one
| that has to be GCed (foo/return value).
|
| This is true in particular when FOO is only used in `my_method`.
| If it's also used in `my_other_method` and it logically makes
| sense for both methods to use the same base string, then it's
| beneficial to use the wider-scope constant.
|
| (The reason this seems reasonable in an application is that the
| method defines the string, mutates it, and sends it along, which
| primarily works because I work on a small team. Ostensibly it
| should send a frozen string, though I rarely do that in practice
| because my rule is don't mutate a string outside the context in
| which it was defined, and that seems sensible enough.)
|
| Am I mistaken and/or is there another, perhaps more common
| pattern that I'm not thinking about that makes this desirable?
| Presumably I can just add # frozen_string_literal: false to my
| files if I want so this isn't a complaint. I'm just curious to
| know the reasoning since it is not obvious to me.
| temporallobe wrote:
| We implemented this recently on a Rails project as part of a
| Rubocop integration. It actually uncovered a lot of subtle bugs,
| though I will say that the Ruby language lends itself to buggy
| code. Thankfully we have sophisticated tooling these days that
| (mostly) mitigates this.
| flufluflufluffy wrote:
| The phrase "Frozen String Literals" is kind of weird to me. When
| I assign a string literal to a variable, I do not think of the
| variable itself as a "string literal." That phrase is for the
| _literal_ characters in between quotes in the code, which by
| definition are already "frozen." They're a static part of the
| code itself. This change makes it so that you cannot mutate a
| _variable_ which was _initialized using a string literal._ (if I
| understand correctly!)
| byroot wrote:
| That's not quite how Ruby and similar languages like Python or
| JS work.
|
| Variables don't "contain" a string, they just point to objects
| on the heap.
|
| So: my_string = same_string = "Hello World"
|
| Here both variables are essentially pointers to a pre-existing
| object on the heap, and that object is immutable.
| flufluflufluffy wrote:
| Yeah it's just the naming is weird. The string literal is not
| the object on the heap, it's part of the program's code
| itself, which was (assumedly) never mutable to begin with.
| chowells wrote:
| In ruby, "frozen" is a property of some values that makes them
| immutable. I mean, other than the part where you can mutably
| unfreeze objects of many classes. (At least you can't unfreeze
| strings.) This change makes string _values_ that come from
| literals initially frozen. It has nothing to do with variable
| bindings.
| dorianmariecom wrote:
| great post thanks
| dorianmariecom wrote:
| btw byroot in this thread is a ruby code committer and rails core
| committer as well
___________________________________________________________________
(page generated 2025-07-09 23:00 UTC)