[solved] weird issue when auto importing classes - using BYTEPATH tutorial

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
rd_
Prole
Posts: 5
Joined: Fri Jul 26, 2019 9:12 pm

[solved] weird issue when auto importing classes - using BYTEPATH tutorial

Post by rd_ »

I'm going through the BYTEPATH tutorial and it's asking me to work out a function that automatically imports my module files. After searching and fiddling, I have something that works. Just one weird thing I can't figure out.

Any global modules get their class-table-object imported in camelcase, just like the in the actual file. (The modules all extend rsi's class module as 'Obect') However local modules all come in as lowercase just like the filename.


This is what I mean:

Image
Image

Any idea why this is happening? Ideally, I'd like them to come in to match the class/table style in camelcase. But mostly, I'd like to understand what is going on here.

**main.lua**

Code: Select all

--! file: main.lua
    
    Object = require "libraries.classic"
    
    local object_files = {}
    local c1, h1
    
    function string:split( inSplitPattern )
        local outResults = {}
        local theStart = 1
        local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
        while theSplitStart do
            table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
            theStart = theSplitEnd + 1
            theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
        end
        table.insert( outResults, string.sub( self, theStart ) )
        return outResults
    end
    
    function love.keypressed(key, u)
       --Debug
       if key == "rctrl" then
          debug.debug()
       end
    end
    
    function recursiveEnumerate(folder, file_list)
        local filesTable = love.filesystem.getDirectoryItems(folder)
        for i,v in ipairs(filesTable) do
            local file = folder .. '/' .. v
            if love.filesystem.getInfo(file, 'file') then
                table.insert(file_list, file)
            elseif love.filesystem.getInfo(file, 'directory') then
                recursiveEnumerate(file, file_list)
            end
        end
    end
    
    function requireFiles(files)
      for _,filepath in ipairs(files) do
        local filepath = filepath:sub(1, -5)
        -- using a split function because lua doesn't have one built-in
        local parts = filepath:split("/")
        local class = parts[#parts]
        _G[class] = require(filepath)
      end
    end
    
    function love.load()
       recursiveEnumerate('objects', object_files)
       requireFiles(object_files)
       image = love.graphics.newImage('image.png')
       c1 = Circle(400, 300, 50)
       hc1 = HyperCircle(400, 300, 50, 50, 50)
    end
    
    function love.update(dt)
        c1:update(dt)
        hc1:update(dt)
    end
    
    function love.draw()
        for i,v in ipairs(object_files) do
            love.graphics.print(object_files[i], 20, 20 + (i * 10))
        end
        c1:draw()
        hc1:draw()
    end
**circle.lua (global, comes in like camelcase class/table object)**

Code: Select all

Circle = Object:extend()    
    function Circle:new(x, y, radius)
        self.x = x
        self.y = y
        self.radius = radius
        self.creation_time = love.timer.getTime()
    end    
    function Circle:update(dt)        
    end    
    function Circle:draw()
        love.graphics.circle('fill', self.x, self.y, self.radius)
    end
**hypercircle.lua (global, comes in camelcase like class/table object)**

Code: Select all

 HyperCircle = Circle:extend()
    local distance
    local widthy
    function HyperCircle:new(x, y, radius, outer_radius_distance, line_width)
        HyperCircle.super.new(self, x, y)
        self.radius = radius
        self.outer_radius = self.radius + outer_radius_distance
        self.line_width = line_width
        distance = self.outer_radius
        widthy = self.line_width
    end
    function HyperCircle:update(dt)
        self.outer_radius = love.math.random(self.radius, distance + 200)
        self.line_width = love.math.random(1, widthy)
    end
    function HyperCircle:draw()
        love.graphics.setLineWidth(self.line_width)
        love.graphics.circle('line', self.x, self.y, self.outer_radius)
    end
**testclass.lua (local, comes in lowercase like file)**

Code: Select all

 local TestClass = Object:extend()   
    function TestClass:new()    
    end    
    function TestClass:update()        
    end    
    function TestClass:draw()      
    end    
    return TestClass
Last edited by rd_ on Thu Nov 26, 2020 11:52 pm, edited 1 time in total.
MrFariator
Party member
Posts: 559
Joined: Wed Oct 05, 2016 11:53 am

Re: weird issue when auto importing classes - using BYTEPATH tutorial

Post by MrFariator »

Your requireFiles() function in your main.lua is explictly taking the filename of your classes, and tries to assign whatever they return into the global state (_G). Because hypercircle.lua doesn't have a return statement (ie. it returns nil in lua speak), _G['hypercircle'] gets assigned nil, which in turn means that 'hypercircle' won't show up in your little debug print in the first screenshot. But because hypercircle.lua defines a global HyperCircle class, you will see it show up in _G just fine. On the other hand, because testclass.lua does return a value, it gets assigned into _G['testclass'], which shows up in your debug print as 'testclass'.

If you want to use this approach to recursively load all classes, without using the classes' filenames as identifiers, you'll have to check that the require() call does return a value, and maybe even identifier with which to assign it to global state. Like maybe the classes you would load have some "globalClassName" field, which you could refer to like the following:

Code: Select all

function requireFiles(files)
  for _,filepath in ipairs(files) do
    local filepath = filepath:sub(1, -5)
    -- using a split function because lua doesn't have one built-in
    local parts = filepath:split("/")
    local class = parts[#parts]
    local classObject = require(filepath)
    -- assign class object to global state only if it returns an object we can use, and it has a specific field in it
    if classObject and classObject.globalClassName then
      _G[classObject.globalClassName] = classObject
    end
  end
end
rd_
Prole
Posts: 5
Joined: Fri Jul 26, 2019 9:12 pm

Re: weird issue when auto importing classes - using BYTEPATH tutorial

Post by rd_ »

ah, OKAY, that is helping a ton. One of the terms I didn't get a handle on yet was 'identifier'. Thanks for explaining the difference between file name as an identifier and class name as an identifier.

With that in mind, what's the point of making all these classes local if I end up requiring them as part of the global table anyway? For example if I'm not using the automated function then I'd just manually type:

main.lua

Code: Select all

Circle = require 'objects.circle.lua'
I'm not understanding why I would do this if I just end up putting it into _G.
MrFariator
Party member
Posts: 559
Joined: Wed Oct 05, 2016 11:53 am

Re: weird issue when auto importing classes - using BYTEPATH tutorial

Post by MrFariator »

Local vs global is basically a case-by-case kind of thing, and in general you may not want to pollute the global table (_G) with too many things. This is because those globals may create some nasty dependencies or introduce bugs that are hard to troubleshoot (eq. accidentally overwriting a global variable in different places), as well as potentially make the overall code less flexible in places due to tight coupling.

As to why you might want to keep the classes local in their own files is ease of use across different projects. In one project, you might already have a class called XYZ, while in another you may not, so it's better to let the project in question handle how to encapsulate the given module you're loading in. Kikito has a pretty good write up that touches up on this, it's a recommended read.

There is nothing wrong with having some globally accessible things, like rxi's class module in your case, but moderation is key.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 7 guests