SlideShare ist ein Scribd-Unternehmen logo
1 von 124
BEYOND THE BASICS
                           Regular Expressions in Ruby

                                 @nellshamrell




Thursday, February 7, 13
^4[0-9]{12}(?:[0-9]{3})?$



                           Source: regular-expressions.info

Thursday, February 7, 13
Regular Expressions are Patterns




Thursday, February 7, 13
Test

                       Extract

                       Change


Thursday, February 7, 13
Test

                           Extract

                       Change


Thursday, February 7, 13
Test

                           Extract

                       Change


Thursday, February 7, 13
In Ruby, regular
                           expressions are objects




Thursday, February 7, 13
You program from a regular
                        expression to a result



                               Source: The Well Grounded Rubyist

Thursday, February 7, 13
Oniguruma




Thursday, February 7, 13
Shorthand for Hexadecimals

                              /h and /H




Thursday, February 7, 13
Onigmo




Thursday, February 7, 13
Thursday, February 7, 13
=~

Thursday, February 7, 13
/force/ =~ “Use the force”




Thursday, February 7, 13
“Use the force” =~ /force/




Thursday, February 7, 13
“Use the force” =~ /force/

        => 8




Thursday, February 7, 13
/dark side/ !~ “Use the force”




Thursday, February 7, 13
/dark side/ !~ “Use the force”

        => true




Thursday, February 7, 13
MatchData




Thursday, February 7, 13
.match




Thursday, February 7, 13
string = “The force will be with
                           you always.”




Thursday, February 7, 13
string = “The force will be with
                           you always.”
           m =/force/.match(string)




Thursday, February 7, 13
string = “The force will be with
                           you always.”
           m =/force/.match(string)

            => #<MatchData “force” >



Thursday, February 7, 13
string = “The force will be with
                           you always.”
           m =/force/.match(string,5)




Thursday, February 7, 13
string = “The force will be with
                           you always.”
           m =/force/.match(string,5)

            => nil



Thursday, February 7, 13
What can you do with MatchData?




Thursday, February 7, 13
m.to_s




Thursday, February 7, 13
m.to_s
       => “force”




Thursday, February 7, 13
m.to_s
       => “force”

       m.pre_match




Thursday, February 7, 13
m.to_s
       => “force”

       m.pre_match
       => “The ”




Thursday, February 7, 13
m.to_s
       => “force”

       m.pre_match
       => “The ”

       m.post_match

Thursday, February 7, 13
m.to_s
       => “force”

       m.pre_match
       => “The ”

       m.post_match
       => “ be with you”
Thursday, February 7, 13
Capture Groups




Thursday, February 7, 13
/(.*)force(.*)/




Thursday, February 7, 13
m = /(.*)force(.*)/.match(string)




Thursday, February 7, 13
m = /(.*)force(.*)/.match(string)

           m.captures




Thursday, February 7, 13
m = /(.*)force(.*)/.match(string)

           m.captures

           => [“The ”,

                           “will be with you always”]

Thursday, February 7, 13
You access capture groups with []




Thursday, February 7, 13
m[1]




Thursday, February 7, 13
m[1]
       => “The ”




Thursday, February 7, 13
m[1]
       => “The ”

       m[2]




Thursday, February 7, 13
m[1]
       => “The ”

       m[2]
       => “ will be with you always ”




Thursday, February 7, 13
m[0]




Thursday, February 7, 13
m[0]
       => “The force will be with
          you always.”




Thursday, February 7, 13
Match Objects are not arrays




Thursday, February 7, 13
m.each do |match|
             puts match.upcase
           end




Thursday, February 7, 13
m.each do |match|
             puts match.upcase
           end
           => NoMethodError




Thursday, February 7, 13
m.to_a.each do |match|
             puts match.upcase
           end




Thursday, February 7, 13
m.to_a.each do |match|
             puts match.upcase
           end
           => “THE FORCE WILL BE
              WITH YOU ALWAYS”
                           “THE ”
                           “WILL BE WITH YOU
                           ALWAYS”
Thursday, February 7, 13
Thursday, February 7, 13
LookArounds Define Context




Thursday, February 7, 13
string = “Who’s the more
                      foolish? The fool or
                      the fool who follows
                      him?”




Thursday, February 7, 13
string = “Who’s the more
                      foolish? The fool or
                      the fool who follows
                      him?”

             /fool/



Thursday, February 7, 13
string.scan(/fool/)




Thursday, February 7, 13
string.scan(/fool/)

          => [“fool”, “fool”, “fool”]




Thursday, February 7, 13
Positive Lookahead




Thursday, February 7, 13
?=




Thursday, February 7, 13
string.scan(/fool(?=ish)/)




Thursday, February 7, 13
string.scan(/fool(?=ish)/)

          => [“fool”]




Thursday, February 7, 13
string.gsub(/fool(?=ish)/, “self”)




Thursday, February 7, 13
string.gsub(/fool(?=ish)/, “self”)

          => “Who’s the more
                           selfish? The fool or
                           the fool who follows
                           him?”



Thursday, February 7, 13
Zero Width Positive
                           Lookahead Assertion




Thursday, February 7, 13
Zero width means it does not
                         consume characters




Thursday, February 7, 13
Positive means a match for the
                 lookahead should be present




Thursday, February 7, 13
Lookahead means it is looking
                   ahead of your main match.




Thursday, February 7, 13
Assertion means the lookahead only
    determines whether a match exists




Thursday, February 7, 13
string = “Who’s the more
                      foolish? The fool or
                      the fool who follows
                      him?”




Thursday, February 7, 13
Negative Lookahead




Thursday, February 7, 13
Negative means a match for the
         lookahead should not be present




Thursday, February 7, 13
?!




Thursday, February 7, 13
string.scan(/fool(?!ish)/)




Thursday, February 7, 13
string.scan(/fool(?!ish)/)

          => [“fool”, “fool”]




Thursday, February 7, 13
string.gsub(/fool(?!ish)/, “self”)




Thursday, February 7, 13
string.gsub(/fool(?!ish)/, “self”)

          => “Who’s the more
                           foolish? The self or
                           the self who follows
                           him?”



Thursday, February 7, 13
Positive Lookbehind




Thursday, February 7, 13
string = “For my ally is the
                      force, and a powerful
                      ally it is”




Thursday, February 7, 13
string = “For my ally is the
                      force, and a powerful
                      ally it is”


             /ally/



Thursday, February 7, 13
?<=




Thursday, February 7, 13
/(?<=powerful )ally/




Thursday, February 7, 13
string.gsub(/(?<=powerful )ally/,
        “friend”)




Thursday, February 7, 13
string.gsub(/(?<=powerful )ally/,
        “friend”)
        => “For my ally is the
                           force, and a powerful
                           friend it is”




Thursday, February 7, 13
Negative Lookbehind




Thursday, February 7, 13
?<!




Thursday, February 7, 13
/(?<!powerful )ally/




Thursday, February 7, 13
string.gsub(/(?<!powerful )ally/,
        “friend”)




Thursday, February 7, 13
string.gsub(/(?<!powerful )ally/,
        “friend”)
        => “For my friend is the
                           force, and a powerful
                           ally it is”




Thursday, February 7, 13
Thursday, February 7, 13
Regular Expressions have
                              distinct behaviors




Thursday, February 7, 13
Greedy

                           Lazy

                       Possessive


Thursday, February 7, 13
Greedy

                           Lazy

                       Possessive


Thursday, February 7, 13
Greedy

                           Lazy

                           Possessive


Thursday, February 7, 13
Quantifiers




Thursday, February 7, 13
+


Thursday, February 7, 13
+
                           /.+/

Thursday, February 7, 13
Quantifiers are greedy by default




Thursday, February 7, 13
Greedy Quantifiers match
                             as much as possible




Thursday, February 7, 13
Greedy Quantifiers use maximum
         effort for maximum return




Thursday, February 7, 13
string = “This is no time to
                      talk about time we
                      don’t have the time”




Thursday, February 7, 13
string = “This is no time to
                      talk about time we
                      don’t have the time”


             /.+time/



Thursday, February 7, 13
/.+time/.match(string)




Thursday, February 7, 13
/.+time/.match(string)

          => “This is no time to
                           talk about time we
                           don’t have the time”




Thursday, February 7, 13
Greedy regular expressions
                             try to match the whole
                            string, then backtrack




Thursday, February 7, 13
Oniguruma makes
                           backtracking quicker




Thursday, February 7, 13
Lazy Quantifiers




Thursday, February 7, 13
Lazy Quantifiers match
                            as little as possible




Thursday, February 7, 13
Lazy Quantifiers use minimum
              effort for minimum return




Thursday, February 7, 13
/.+?time/




Thursday, February 7, 13
/.+?time/.match(string)




Thursday, February 7, 13
/.+?time/.match(string)

          => “This is no time”




Thursday, February 7, 13
Lazy regular expressions
                              use less resources




Thursday, February 7, 13
Possessive Quantifiers




Thursday, February 7, 13
Possessive Quantifiers are
                                all or nothing




Thursday, February 7, 13
Possessive Quantifiers try to
                        match the entire string
                       with no backtracking




Thursday, February 7, 13
Possessive Quantifiers use
                             minimum effort for
                              maximum return




Thursday, February 7, 13
/.++time/




Thursday, February 7, 13
/.++time/.match(string)




Thursday, February 7, 13
/.++time/.match(string)

          => nil




Thursday, February 7, 13
Possessive Quantifiers
                                fail faster




Thursday, February 7, 13
Thursday, February 7, 13
Write regular expressions
                               in small chunks




Thursday, February 7, 13
Rubular




Thursday, February 7, 13
Regular expressions come in drafts




Thursday, February 7, 13
Move beyond your fear
                           of regular expressions




Thursday, February 7, 13
Nell Shamrell
             Software Development Engineer
                           Blue Box Group
                           @nellshamrell


Thursday, February 7, 13

Weitere ähnliche Inhalte

Mehr von Nell Shamrell-Harrington

This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!Nell Shamrell-Harrington
 
Higher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with HabitatHigher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with HabitatNell Shamrell-Harrington
 
Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!Nell Shamrell-Harrington
 
Creating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef HabitatCreating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef HabitatNell Shamrell-Harrington
 
First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)Nell Shamrell-Harrington
 
A Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef SupermarketA Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef SupermarketNell Shamrell-Harrington
 

Mehr von Nell Shamrell-Harrington (20)

This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!
 
The Rust Borrow Checker
The Rust Borrow CheckerThe Rust Borrow Checker
The Rust Borrow Checker
 
Higher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with HabitatHigher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with Habitat
 
Habitat Service Discovery
Habitat Service DiscoveryHabitat Service Discovery
Habitat Service Discovery
 
Web Operations101
Web Operations101Web Operations101
Web Operations101
 
Rust Traits And You: A Deep Dive
Rust Traits And You: A Deep DiveRust Traits And You: A Deep Dive
Rust Traits And You: A Deep Dive
 
Rust, Redis, and Protobuf - Oh My!
Rust, Redis, and Protobuf - Oh My!Rust, Redis, and Protobuf - Oh My!
Rust, Redis, and Protobuf - Oh My!
 
Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!
 
Chef Vault: A Deep Dive
Chef Vault: A Deep DiveChef Vault: A Deep Dive
Chef Vault: A Deep Dive
 
Open Source Governance 101
Open Source Governance 101Open Source Governance 101
Open Source Governance 101
 
DevOps in Politics
DevOps in PoliticsDevOps in Politics
DevOps in Politics
 
Open Source Governance - The Hard Parts
Open Source Governance - The Hard PartsOpen Source Governance - The Hard Parts
Open Source Governance - The Hard Parts
 
Creating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef HabitatCreating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef Habitat
 
Refactoring terraform
Refactoring terraformRefactoring terraform
Refactoring terraform
 
Refactoring Infrastructure Code
Refactoring Infrastructure CodeRefactoring Infrastructure Code
Refactoring Infrastructure Code
 
Devops: A History
Devops: A HistoryDevops: A History
Devops: A History
 
First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)
 
First Do No Harm: Surgical Refactoring
First Do No Harm: Surgical RefactoringFirst Do No Harm: Surgical Refactoring
First Do No Harm: Surgical Refactoring
 
A Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef SupermarketA Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef Supermarket
 
Public Supermarket: The Insider's Tour
Public Supermarket: The Insider's TourPublic Supermarket: The Insider's Tour
Public Supermarket: The Insider's Tour
 

Kürzlich hochgeladen

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 

Kürzlich hochgeladen (20)

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 

Beyond the Basics: Regular Expressions in Ruby