Inversion of Control is still a concept that applies well. You don't need to be doing dependency injection to apply IoC, although in .NET they do tend to go together often.
The big drive behind dependency injection in .NET is avoiding depending on concrete implementations. In Ruby, as you mentioned, things are much more open and you can replace implementation of a class at run time, so there's much less need to create "interfaces" and explicitly inject dependencies.
It can be argued that interfaces and IoC/DI aren't strictly necessary in .NET either (even when it comes to testing -- there are mocking frameworks that will allow you to eliminate the need to interface everything under the sun), but it's more pronounced in Ruby.
What stays the same? What changes?
The patterns are the same. The language techniques change.
Are there guiding principles like SOLID,
Yes. Indeed, they remain the guiding principles. Nothing changes.
or canonical patterns (perhaps entirely new ones) that a dynamic language newbie should know?
Some things are unique. Mostly the impact is that the implementation techniques change.
A pattern is -- well -- a pattern. Not a law. Not a subroutine. Not a macro. It's just a good idea that gets repeated because it's a good idea.
Good ideas don't go out of style or change dramatically.
Other notes. Python is not "weakly typed". It's more strongly-typed than Java or C++ because there's no cast operation. [Yes, there is a way to fudge the class associated with an object, but it's not the kind of thing that's done except to prove a fussy, legalistic point.]
Also. Most design patterns are based on different ways to exploit polymorphism.
Look at State or Command or Memento as examples. They have class hierarchies to create a polymorphic states, commands or mementos of state changes. Nothing changes significantly when you do this in Python. Minor changes include the relaxation of the precise class hierarchy because polymorphism in Python depends on common methods not common ancestors.
Also, some patterns are simply an attempt to achieve late binding. Most Factory-related patterns are an attempt to allow easy change to a class hierarchy without recompiling every C++ module in the application. This isn't as interesting optimization in a dynamic language. However, a Factory as a way to conceal implementation details still has huge value.
Some patterns are an attempt to drive the compiler and linker. Singleton, for example, exists to create confusing globals but at least encapsulate them. Python singleton classes aren't a pleasant prospect. But Python modules already are singletons, so many of us just use a module and avoid trying to mess with a Singleton class.
Best Answer
This was answered in the context of C# interfaces and Ruby on stackoverflow: https://stackoverflow.com/questions/3505521/in-ruby-what-is-the-equivalent-to-an-interface-in-c.
Summarized: there is no exact equivalent in Ruby since duck typing makes a formal interface unnecessary. Instead, consider testing for compliance to an "interface" or contract using
respond_to?
.