Git Product home page Git Product logo

ruby-building-applications-requiring-files-london-web-030920's Introduction

Requiring Files

Learning Goals

  • Recognize how code on different files can be loaded when running a single file
  • Understand how to require files when running a Ruby application

Introduction

So far, most of the labs we've seen have a similar file structure - they typically have a lib folder and a spec folder and some files like README.md:

├── lib
│   └── ruby_file.rb
└── spec
    ├── ruby_file_spec.rb
    └── spec_helper.rb
├── Gemfile
├── README.md

As we expand our understanding of programming in Ruby and start to work with larger, more complex applications, we'll start to see more pieces and components being added - run files, environment files, to start. Then, we'll start seeing things like database folders and before long, we'll see many files in many folders, all doing their small part for the application.

With all these files separated out into different folders, how is it that they are able to work together? In this lesson, we're going to provide an answer to this question and take a deeper look at requiring files.

Classes Defined in the Same File

Let's take a look at a pair of example classes to get started. If we define two classes in the same file, we can interact with both classes immediately after they are defined. Imagine if the following 'belongs-to' / 'has-many' example between Garden and Plant classes:

class Garden
  attr_accessor :name

  def initialize(name:)
    @name = name
  end

  def plants
    Plant.all.select do |plant|
      plant.garden == self
    end
  end
end

class Plant
  attr_accessor :garden, :name

  @@all = []

  def initialize(name:)
    @name = name
    @@all << self
  end

  def self.all
    @@all
  end
end

lawn = Garden.new(name: 'Front Lawn') # we can call Plant.new because Garden is defined above

basil = Plant.new(name: 'Basil') # we can call Plant.new because Plant is defined above
basil.garden = lawn

cucumber = Plant.new(name: 'Cucumber')
cucumber.garden = lawn # we can associate a plant and a garden because both are defined above

p lawn.plants
# => [#<Plant:0x00007fa4440ab0c8 @name="Basil", @garden=#<Garden:0x00007fa4440997b0 @name="Front Lawn">>, #<Plant:0x00007fa4440b8bd8 @name="Cucumber", @garden=#<Garden:0x00007fa4440997b0 @name="Front Lawn">>]

Note: This code is available in lib/example.rb and can be tested by running ruby lib/example.rb.

In the above code, we can call both Garden and Plant at the end because both classes have been defined in the file. Notice, too, that Garden includes a method, plants, that calls Plant.all. Garden needs to know about the Plant class, and with the setup above, it does.

Classes Defined in Different Files

If we define the example classes in separate files, they won't automatically know about each other or have access to one another. In the lib folder, Plant and Garden are separated into their own files, plant.rb and garden.rb.

├── lib
│   └── example.rb
│   └── garden.rb
│   └── plant.rb
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md

In lib/garden.rb, below the Garden class, the same code from the previous example is written:

# lib/garden.rb

# ...

lawn = Garden.new(name: 'Front Lawn')

basil = Plant.new(name: 'Basil')
basil.garden = lawn

cucumber = Plant.new(name: 'Cucumber')
cucumber.garden = lawn

p lawn.plants

At the moment, if we try to run the file (ruby lib/garden.rb), we get an error regarding an uninitialized constant Plant (NameError). For one file to have access to code written in another file, we need to tell Ruby to require that other file.

Define require_relative and require

By requiring a file, we are telling Ruby, 'go load the that code exists in this file.' We have two ways to tell Ruby to do this: require_relative and require.

require_relative

require_relative accepts a relative file path to the file we want to require. This means we're providing a file path that starts from the file in which the require_relative statement is called.

require_relative '../lib/ruby_file.rb'

In the example above, .. would mean that inside the parent folder of the current file being run, there should be a lib folder with a file inside it, ruby_file.rb. When dealing with applications that have many neighboring files, we'll be using require_relative.

Since an application can be installed in many places on a computer, any internal file requirements should be relative to each other.

require

require accepts an absolute file path, meaning a file location relative to the root directory. require will also accept shortened names, and checks if any matches are found in the directories located in your shell's PATH variable.

We don't often require files using their absolute path, but we will frequently require files using a shortened name. You may have already seen this:

require 'pry'

Pry is a Gem, a bundle of preexisting code that we can install on our computers. When we write require 'pry', we are telling Ruby to load that bundle of code up before continuing. Once loaded, we will have access to whatever classes and modules are defined in pry.

Requiring plant.rb from garden.rb

Now that we know the syntax for requiring files, we can apply it to our example of lib/garden.rb and lib/plant.rb. At the top of lib/garden.rb, add the following line:

require_relative './plant.rb'

Here, we're indicating to Ruby that there is a file, plant.rb located in the same folder relative to the current file. With this added, if you try ruby lib/garden.rb again, you should see it print out the expected Plant instances.

In this example, we only have two classes, but with require_relative, we can have as many classes as we want, each in a separate file. As long as they are all required in the file that we run, they'll get loaded.

Local Variables

It is important to note that both require and require_relative will not load local variables - only functions, modules, and classes.

Conclusion

Ruby applications are often divided up among many files, and when an application runs, it typically needs a way to load those various files so it has access to all the code.

You've experienced this many times already - every time you work on a Ruby lab and run the tests, the files where you write your solution are being loaded into the files that are running the tests. This is done through a file called spec_helper.rb.

The exact workings of RSpec and the spec_helper.rb file are beyond the scope of this lesson, but the underlying premise is the same as what we did in this lesson.

This is so common and so critical to Ruby applications that it became common to include a file dedicated to the task of requiring files and loading up any needed code when an application is run. In the next lesson, we'll take a closer look at this file, typically known as environment.rb.

ruby-building-applications-requiring-files-london-web-030920's People

Contributors

maxwellbenton avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.