diff --git a/Gemfile b/Gemfile index 2d8ccf1..3729d07 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,17 @@ +# frozen_string_literal: true + source 'https://rubygems.org' # database, ORM, server gem 'activerecord' +gem 'dotenv', groups: %i[development test] +gem 'forme' gem 'json' -gem 'rake' -gem 'sinatra-activerecord' +gem 'pry' gem 'puma' gem 'rackup' -gem 'forme' -gem 'pry' -gem 'dotenv', groups: [:development, :test] +gem 'rake' +gem 'sinatra-activerecord' # browser automation gem 'ferrum', git: 'https://github.com/rubycdp/ferrum.git', ref: '7cc1a63351232b10f9ce191104efe6e9c72acca2' @@ -18,15 +20,15 @@ gem 'puppeteer-ruby', '~> 0.45.6' # image processing gem 'mini_magick', '~> 4.12.0' -#db +# db gem 'sqlite3' group :development, :test do + gem 'database_cleaner-active_record' gem 'debug' gem 'nokogiri' - gem "rspec" - gem "rack-test" - gem "database_cleaner-active_record" + gem 'rack-test' + gem 'rspec' end group :test do diff --git a/Rakefile b/Rakefile index 96e3643..7fac84f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require './app' require 'sinatra/activerecord/rake' diff --git a/app.rb b/app.rb index 79930f1..d6614dd 100644 --- a/app.rb +++ b/app.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'dotenv/load' require 'sinatra' @@ -17,12 +19,12 @@ end helpers do - def create_forme(model, is_edit, attrs={}, options={}) + def create_forme(model, _is_edit, attrs = {}, options = {}) attrs[:method] = :post options = TailwindConfig.options.merge(options) - if model && model.persisted? + if model&.persisted? attrs[:action] += "/#{model.id}" if model.id - options[:before] = -> (form) { + options[:before] = lambda { |form| TailwindConfig.before.call(form) form.to_s << '' } @@ -30,7 +32,7 @@ def create_forme(model, is_edit, attrs={}, options={}) f = Forme::Form.new(model, options) f.extend(ExplicitFormePlugin) f.form_tag(attrs) - return f + f end end @@ -45,8 +47,8 @@ def create_forme(model, is_edit, attrs={}, options={}) # DEVICE MANAGEMENT def devices_form(device) create_forme(device, device.persisted?, - {autocomplete:"off", action: "/devices"}, - {namespace: "device"}) + { autocomplete: 'off', action: '/devices' }, + { namespace: 'device' }) end get '/devices/?' do @@ -84,7 +86,7 @@ def devices_form(device) end get '/' do - erb :"index" + erb :index end # FIRMWARE SETUP @@ -97,7 +99,7 @@ def devices_form(device) api_key = @device.api_key friendly_id = @device.friendly_id image_url = "#{ENV['BASE_URL']}/images/setup/setup-logo.bmp" - message = "Welcome to TRMNL BYOS" + message = 'Welcome to TRMNL BYOS' { status:, api_key:, friendly_id:, image_url:, message: }.to_json else @@ -112,7 +114,7 @@ def devices_form(device) if @device base64 = (env['HTTP_BASE64'] || params[:base64]) == 'true' - screen = ScreenFetcher.call(base64: base64) + screen = ScreenFetcher.call(base64:) { status: 0, # on core TRMNL server, status 202 loops device back to /api/setup unless User is connected, which doesn't apply here diff --git a/config/environments/test.rb b/config/environments/test.rb index 17495d1..1fa5dcb 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1 +1,3 @@ -config.active_record.maintain_test_schema = true \ No newline at end of file +# frozen_string_literal: true + +config.active_record.maintain_test_schema = true diff --git a/config/initializers/explicit_forme_plugin.rb b/config/initializers/explicit_forme_plugin.rb index 8634314..a543794 100644 --- a/config/initializers/explicit_forme_plugin.rb +++ b/config/initializers/explicit_forme_plugin.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ExplicitFormePlugin attr_reader :_form_tag @@ -5,24 +7,24 @@ def form_tag(attrs) @_form_tag = ::Forme::Tag.new(self, :form, attrs) end - def explicit_open() - @to_s << self.serialize_open(@_form_tag) - self.before_form_yield + def explicit_open + @to_s << serialize_open(@_form_tag) + before_form_yield @to_s end - def explicit_close() + def explicit_close form_len = @to_s.length - self.after_form_yield - @to_s << self.serialize_close(@_form_tag) + after_form_yield + @to_s << serialize_close(@_form_tag) @to_s[form_len..] end - + def form_output(&block) form_str = String.new - form_str << self.explicit_open() + form_str << explicit_open form_str << block.call if block_given? - form_str << self.explicit_close() + form_str << explicit_close form_str end -end \ No newline at end of file +end diff --git a/config/initializers/tailwind_form.rb b/config/initializers/tailwind_form.rb index 3eb1147..e186634 100644 --- a/config/initializers/tailwind_form.rb +++ b/config/initializers/tailwind_form.rb @@ -1,21 +1,22 @@ +# frozen_string_literal: true + require 'forme' -require 'pp' module TailwindConfig - def self.label_attr - { - class: 'block text-gray-700 text-md leading-4 font-medium mb-2' + def self.label_attr + { + class: 'block text-gray-700 text-md leading-4 font-medium mb-2' } end - def self.before - -> (form) { + def self.before + lambda { |form| form.to_s << '
' } end - def self.after - -> (form) { + def self.after + lambda { |form| form.to_s << '
' } end @@ -24,40 +25,40 @@ def self.options { labeler: :explicit, wrapper: :div, - before: self.before, - after: self.after, + before:, + after:, input_defaults: { text: { - label_attr: self.label_attr, + label_attr:, wrapper_attr: { class: 'p-6' }, - class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' , + class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' }, number: { - label_attr: self.label_attr, + label_attr:, wrapper_attr: { class: 'p-6' }, - class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' , + class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' }, time: { - label_attr: self.label_attr, + label_attr:, wrapper_attr: { class: 'p-6' }, - class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' , + class: 'block mt-2 min-h-12 px-4 rounded-xl text-black bg-white block w-full p-0 outline-none text-sm border border-gray-300 focus:outline-none ring-2 ring-transparent focus:ring-2 ring-offset-gray-200 focus:ring-offset-2 focus:ring-offset-gray-200 focus:ring-blue-500 focus:border-transparent transition duration-150' }, checkbox: { label_position: :before, - label_attr: self.label_attr, + label_attr:, wrapper_attr: { class: 'p-6' }, - class: 'h-8 w-8 rounded-full shadow', + class: 'h-8 w-8 rounded-full shadow' }, submit: { - label_attr: self.label_attr, + label_attr:, attr: { class: 'cursor-pointer font-medium rounded-lg text-sm px-3 py-2 inline-flex items-center transition duration-150 justify-center shrink-0 gap-1.5 whitespace-nowrap text-white bg-blue-500 hover:bg-blue-600 focus:outline-none' }, - wrapper_attr: { class: 'p-6 text-right' }, - }, - }, + wrapper_attr: { class: 'p-6 text-right' } + } + } } end end diff --git a/db/migrate/20241212172428_create_devices.rb b/db/migrate/20241212172428_create_devices.rb index 8d4833e..4f0ee50 100644 --- a/db/migrate/20241212172428_create_devices.rb +++ b/db/migrate/20241212172428_create_devices.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateDevices < ActiveRecord::Migration[8.0] def change create_table :devices do |t| diff --git a/db/schema.rb b/db/schema.rb index acc204a..b3c60d5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,14 +12,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_12_12_172428) do - create_table "devices", force: :cascade do |t| - t.string "name" - t.string "mac_address" - t.string "api_key" - t.string "friendly_id" - t.integer "refresh_interval", default: 900, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false +ActiveRecord::Schema[8.0].define(version: 20_241_212_172_428) do + create_table 'devices', force: :cascade do |t| + t.string 'name' + t.string 'mac_address' + t.string 'api_key' + t.string 'friendly_id' + t.integer 'refresh_interval', default: 900, null: false + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false end end diff --git a/models/device.rb b/models/device.rb index 5fbdf2f..912a410 100644 --- a/models/device.rb +++ b/models/device.rb @@ -1,6 +1,7 @@ -class Device < ActiveRecord::Base +# frozen_string_literal: true - DEFAULT_DEVICE_NAME = 'My TRMNL'.freeze +class Device < ActiveRecord::Base + DEFAULT_DEVICE_NAME = 'My TRMNL' MODEL = [ { name: 'og', @@ -21,4 +22,4 @@ def generate_friendly_id def generate_api_key self.api_key = SecureRandom.urlsafe_base64(nil, false) end -end \ No newline at end of file +end diff --git a/services/screen_fetcher.rb b/services/screen_fetcher.rb index 9192112..065a287 100644 --- a/services/screen_fetcher.rb +++ b/services/screen_fetcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ScreenFetcher class << self attr_reader :base64 @@ -16,7 +18,7 @@ def last_generated_image image_url = if base64 img = File.open("public/#{relative_img_path}") - 'data:image/png;base64,' + Base64.strict_encode64(img.read) + "data:image/png;base64,#{Base64.strict_encode64(img.read)}" else "#{base_domain}/#{relative_img_path}" end diff --git a/services/screen_generator.rb b/services/screen_generator.rb index 5802965..d0e9c01 100644 --- a/services/screen_generator.rb +++ b/services/screen_generator.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true + require 'ferrum' require 'mini_magick' require 'puppeteer-ruby' require 'base64' class ScreenGenerator - def initialize(html, image = false) self.input = html self.image = image @@ -31,11 +32,11 @@ def convert_to_image begin # context = browser_instance.create_incognito_browser_context page = firefox_browser.new_page - page.viewport = Puppeteer::Viewport.new(width: width, height: height) + page.viewport = Puppeteer::Viewport.new(width:, height:) # NOTE: Use below for chromium # page.set_content(input, wait_until: ['networkidle0', 'domcontentloaded']) # Note: Use below for firefox - page.set_content(input, timeout: 10000) + page.set_content(input, timeout: 10_000) page.evaluate(<<~JAVASCRIPT) () => { document.getElementsByTagName('html')[0].style.overflow = "hidden"; @@ -61,12 +62,12 @@ def convert_to_image # This will increase the throughput of our image rendering process by 60-70%, saving about ~1.5 second per image generation. # On local it takes < 1 second now to generate the subsequent image. def firefox_browser - @browser ||= Puppeteer.launch( + @firefox_browser ||= Puppeteer.launch( product: 'firefox', headless: true, args: [ "--window-size=#{width},#{height}", - "--disable-web-security" + '--disable-web-security' # "--hide-scrollbars" #works only on chrome, using page.evaluate for firefox ] ) diff --git a/spec/app_spec.rb b/spec/app_spec.rb index d90b43b..21b226a 100644 --- a/spec/app_spec.rb +++ b/spec/app_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require_relative 'spec_helper' RSpec.describe 'App base path tests' do it 'test_it_has_root_path' do browser = get '/' expect(browser.status).to be(200) - expect(browser.body).to match("To set up your device") + expect(browser.body).to match('To set up your device') end it 'test_it_has_device_list_path' do @@ -22,14 +24,14 @@ it 'test_it_has_api_setup_path_with_a_device' do mac = 'aa:bb:cc:00:00:01' dev = Device.create!({ - name: "Test Trmnl", mac_address: mac - }) - header("ID", mac) + name: 'Test Trmnl', mac_address: mac + }) + header('ID', mac) _, body = get_json '/api/setup/' expect(body['api_key']).to eq(dev.api_key) expect(body['friendly_id']).to eq(dev.friendly_id) - expect(body['image_url']).to eq(ENV['BASE_URL'] + '/images/setup/setup-logo.bmp') - expect(body['message']).to eq("Welcome to TRMNL BYOS") + expect(body['image_url']).to eq("#{ENV['BASE_URL']}/images/setup/setup-logo.bmp") + expect(body['message']).to eq('Welcome to TRMNL BYOS') end end @@ -42,13 +44,13 @@ it 'test_it_has_api_setup_path_with_a_device' do mac = 'aa:bb:cc:00:00:01' dev = Device.create!({ - name: "Test Trmnl", mac_address: mac - }) - header("ID", mac) + name: 'Test Trmnl', mac_address: mac + }) + header('ID', mac) _, body = get_json '/api/setup/' expect(body['api_key']).to eq(dev.api_key) expect(body['friendly_id']).to eq(dev.friendly_id) - expect(body['image_url']).to eq(ENV['BASE_URL'] + '/images/setup/setup-logo.bmp') - expect(body['message']).to eq("Welcome to TRMNL BYOS") + expect(body['image_url']).to eq("#{ENV['BASE_URL']}/images/setup/setup-logo.bmp") + expect(body['message']).to eq('Welcome to TRMNL BYOS') end -end \ No newline at end of file +end diff --git a/spec/display_spec.rb b/spec/display_spec.rb index a0a55d6..257d453 100644 --- a/spec/display_spec.rb +++ b/spec/display_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'spec_helper' RSpec.describe 'Display path tests' do diff --git a/spec/setup_spec.rb b/spec/setup_spec.rb index d45df3b..59a3ff9 100644 --- a/spec/setup_spec.rb +++ b/spec/setup_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'spec_helper' RSpec.describe 'API setup path tests' do @@ -9,13 +11,13 @@ it 'test_it_has_api_setup_path_with_a_device' do mac = 'aa:bb:cc:00:00:01' dev = Device.create!({ - name: "Test Trmnl", mac_address: mac - }) - header("ID", mac) + name: 'Test Trmnl', mac_address: mac + }) + header('ID', mac) _, body = get_json '/api/setup/' expect(body['api_key']).to eq(dev.api_key) expect(body['friendly_id']).to eq(dev.friendly_id) - expect(body['image_url']).to eq(ENV['BASE_URL'] + '/images/setup/setup-logo.bmp') - expect(body['message']).to eq("Welcome to TRMNL BYOS") + expect(body['image_url']).to eq("#{ENV['BASE_URL']}/images/setup/setup-logo.bmp") + expect(body['message']).to eq('Welcome to TRMNL BYOS') end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 75e7247..ddece62 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV['APP_ENV'] = 'test' ENV['RACK_ENV'] = 'test' @@ -5,8 +7,7 @@ require 'rack/test' require 'nokogiri' require 'database_cleaner/active_record' -require_relative "../app" - +require_relative '../app' # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| @@ -14,7 +15,7 @@ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Schema.verbose = false - load "db/schema.rb" + load 'db/schema.rb' config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true @@ -27,7 +28,7 @@ config.shared_context_metadata_behavior = :apply_to_host_groups DatabaseCleaner.strategy = :truncation - config.after do + config.after do DatabaseCleaner.clean end @@ -35,23 +36,23 @@ def app Sinatra::Application end - def get_and_parse(page, query_params={}, env={}) + def get_and_parse(page, query_params = {}, env = {}) get(page, query_params, env) @doc = Nokogiri::HTML(last_response.body) - return @doc + @doc end - def get_json(page, query_params={}, env={}) + def get_json(page, query_params = {}, env = {}) get(page, query_params, env) @doc = last_response - expect(@doc.headers['content-type']).to eq("application/json") - return [@doc, JSON.parse(@doc.body)] + expect(@doc.headers['content-type']).to eq('application/json') + [@doc, JSON.parse(@doc.body)] end - def post_json(page, data, params={}, env={}) + def post_json(page, data, params = {}, env = {}) post(page, JSON.generate(data), params, env) @doc = last_response - expect(@doc.headers['content-type']).to eq("application/json") - return [@doc, JSON.parse(@doc.body)] + expect(@doc.headers['content-type']).to eq('application/json') + [@doc, JSON.parse(@doc.body)] end end