How to setup sorbet to work with a Rails 7 application? - ruby-on-rails

note: I'm not using and I want to know if it is possible without sorbet-rails.
I'm trying to add sorbet to an existing/standard Rails 7 application.
I followed the instruction here: https://sorbet.org/docs/adopting.
I added the gems to the Gemfile followed by bundle install.
gem 'sorbet', :group => :development
gem 'sorbet-runtime'
gem 'tapioca', require: false, :group => :development
After that:
bundle exec tapioca init
bin/tapioca dsl
bin/tapioca gem
bin/tapioca require
bin/tapioca gem --all
bin/tapioca annotations
After running all that, if I ran bundle exec srb tc, I get about 23 errors. Most of them related to generated rbi files. For exemple:
...
sorbet/rbi/dsl/sessions_controller.rbi:19: Unable to resolve constant ActionHelper https://srb.help/5002
19 | include ::Turbo::Streams::ActionHelper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sorbet/rbi/dsl/rails/conductor/base_controller.rbi:22: Unable to resolve constant ActionHelper https://srb.help/5002
22 | include ::Turbo::Streams::ActionHelper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sorbet/rbi/dsl/rails/conductor/base_controller.rbi:22: Unable to resolve constant ActionHelper https://srb.help/5002
22 | include ::Turbo::Streams::ActionHelper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sorbet/rbi/dsl/rails/conductor/action_mailbox/incinerates_controller.rbi:19: Unable to resolve constant ActionHelper https://srb.help/5002
19 | include ::Turbo::Streams::ActionHelper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sorbet/rbi/dsl/rails/conductor/action_mailbox/incinerates_controller.rbi:19: Unable to resolve constant ActionHelper https://srb.help/5002
19 | include ::Turbo::Streams::ActionHelper
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Errors: 23
After applying a workaround for the Turbo issues described here (https://github.com/Shopify/tapioca/issues/671), I'm left with issues related to .require method not found.
app/controllers/people_controller.rb:48: Method permit does not exist on String component of T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) https://srb.help/7003
48 | params.require(:person).permit(
^^^^^^
Got T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) originating from:
app/controllers/people_controller.rb:48:
48 | params.require(:person).permit(
^^^^^^^^^^^^^^^^^^^^^^^
Did you mean format? Use `-a` to autocorrect
app/controllers/people_controller.rb:48: Replace with format
48 | params.require(:person).permit(
^^^^^^
https://github.com/sorbet/sorbet/tree/c77e3a550a6bb9c1a0772fabf433d6763a86f33d/rbi/core/kernel.rbi#L1547: Defined here
1547 | def format(format, *args); end
^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/people_controller.rb:48: Method permit does not exist on Numeric component of T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) https://srb.help/7003
48 | params.require(:person).permit(
^^^^^^
Got T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) originating from:
app/controllers/people_controller.rb:48:
48 | params.require(:person).permit(
^^^^^^^^^^^^^^^^^^^^^^^
Did you mean format? Use `-a` to autocorrect
app/controllers/people_controller.rb:48: Replace with format
48 | params.require(:person).permit(
^^^^^^
https://github.com/sorbet/sorbet/tree/c77e3a550a6bb9c1a0772fabf433d6763a86f33d/rbi/core/kernel.rbi#L1547: Defined here
1547 | def format(format, *args); end
^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/people_controller.rb:48: Method permit does not exist on T::Array[T.untyped] component of T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) https://srb.help/7003
48 | params.require(:person).permit(
^^^^^^
Got T.any(String, Numeric, T::Array[T.untyped], ActionController::Parameters) originating from:
app/controllers/people_controller.rb:48:
48 | params.require(:person).permit(
^^^^^^^^^^^^^^^^^^^^^^^
Did you mean format? Use `-a` to autocorrect
app/controllers/people_controller.rb:48: Replace with format
48 | params.require(:person).permit(
^^^^^^
https://github.com/sorbet/sorbet/tree/c77e3a550a6bb9c1a0772fabf433d6763a86f33d/rbi/core/kernel.rbi#L1547: Defined here
1547 | def format(format, *args); end
^^^^^^^^^^^^^^^^^^^^^^^^^
Errors: 3
Is there anything missing? What is the proper way of doing this?
Note: workaround for the .permit issue can be found here https://github.com/Shopify/tapioca/issues/1122

The turbo ones are a known issue, you can take a look at https://github.com/Shopify/tapioca/issues/671 for a workaround.

Related

Why am I getting strong parameter errors in the Rails Console in rails 5?

I want to call this in my console (ap is the awesome print gem):
ap Purchase.last(10)
but I get this error:
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
It works like this:
irb(main):020:0> ap Purchase.last
#<Purchase:0x00007f86b792a320> {
:id => 28445,
:user_id => 10177,
:product_id => nil,
:product_type => nil,
:price => 9.0,
:gateway_code => nil,
:gateway_msg => nil,
:gateway_response => nil,
:created_at => Fri, 18 May 2018 22:20:10 UTC +00:00,
:updated_at => Fri, 18 May 2018 22:20:10 UTC +00:00,
:checkout_total => 9.0,
:successful => true,
:cart_id => 17242,
:report_errors => nil,
:transacted_value_of_products => 9.0,
:comp_credits_applied => 0.0
}
And without ap like this:
irb(main):022:0> Purchase.last(10)
D, [2018-05-25T20:58:54.692575 #70552] DEBUG -- : Purchase Load (0.5ms) SELECT "purchases".* FROM "purchases" ORDER BY "purchases"."id" DESC LIMIT $1 [["LIMIT", 10]]
+-------+---------+------------+-------------+-------+-------------+-------------+-------------+-------------+--------------+-------------+------------+---------+-------------+-------------+-------------+
| id | user_id | product_id | product_... | price | gateway_... | gateway_msg | gateway_... | created_at | updated_at | checkout... | successful | cart_id | report_e... | transact... | comp_cre... |
+-------+---------+------------+-------------+-------+-------------+-------------+-------------+-------------+--------------+-------------+------------+---------+-------------+-------------+-------------+
| 28436 | 10471 | | | 5.0 | | Completed | {"mc_gro... | 2018-05-... | 2018-05-1... | 5.0 | true | 17228 | {} | 5.0 | 0.0 |
| 28437 | 9754 | | | 1.99 | | Completed | {"mc_gro... | 2018-05-... | 2018-05-1... | 2.48 | true | 15273 | {} | 1.99 | 0.0 |
| 28438 | 10472 | | | 9.0 | | | {\n "id... | 2018-05-... | 2018-05-1... | 9.0 | true | 17231 | {} | 9.0 | 0.0 |
| 28439 | 10348 | | | 9.0 | | | | 2018-05-... | 2018-05-1... | 9.0 | true | 17235 | | 9.0 | 0.0 |
But not with an argument and ap
irb(main):021:0> ap Purchase.last(3)
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):21
It turns out I can't do much of anything:
irb(main):023:0> ap Purchase.find(28444)
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):23
irb(main):024:0> ap Purchase.find(28444).gateway_response
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):24
What's going on?
What's Happening and Why
ActionController::Parameters (which is what params works with in controllers) used to inherit from HashWithIndifferentAccess which inherits from Hash. So ActionController::Parameters < Hash used to be true as would something like:
params.require(:x).permit(some_hash: %i[key1 key2]).is_a? Hash
If you were digging a Hash out of params:
some_hash = params.require(:x).permit(some_hash: ...)
and serializing that in a model:
class M < ApplicationRecord # Or ActiveRecord::Base in the past
serialize :h, Hash
end
#...
m.h = some_hash
you could end up with some YAML like this in your database:
--- !ruby/object:ActionController::Parameters
...
rather than the expected plain YAMLized hash.
But then Rails5 comes along and ActionController::Parameters no longer inherits from Hash:
Make ActionController::Parameters no longer inherits from HashWithIndifferentAccess.
and calling to_h or to_hash on an ActionController::Parameters now raises an exception.
If you upgrade your code and try to load a model with serialized data in it:
serialize :h, Hash
then the model will load the text from h, parse the YAML to get an ActionController::Parameters instance, and call to_h on it to make sure it has a Hash, and you get an exception.
What To Do About It
There are a couple things you need to do:
Fix your controllers to make sure they get real hashes out of params.
Fix your data so that you have serialized hashes rather than ActionController::Parameters instances.
Fixing the controllers is a simple matter of calling to_unsafe_h on the parameters that aren't yet real hashes.
Fixing the data is uglier. I'd probably go through the tables using the low level database interface (i.e. no ActiveRecord anywhere), read the YAML out from each row, YAML.load it, convert it to a hash by calling to_unsafe_h on it, and then writing back the_real_hash.to_yaml text. You could use a like '--- !ruby/object:ActionController::Parameters%' filter in the WHERE clause to only deal with the broken rows.
I'd also strongly recommend that you stop using serialize while you're there. serialize is a bit of a kludge and there's no sane way to work with YAML inside the database; there's also little need for it now that both PostgreSQL and MySQL have native JSON support (but I'm not sure how well ActiveRecord supports MySQL's JSON).

postgres 9.4 ,pgcrypto 0.4.1 , rails 4.2.0, ruby 2.2.0, quoting expected String, got Arel::Nodes::BindParam

problem when trying to create encrypted column. OS X 10.9.5
Created public/private keys with gnupg 2.0.22:
gpg -a --export > myKey .key.pub
gpg -a --export-secret-key myKey > .key.prv
Any pointers on what might be going on here? I've got a model with a ssn field
migration added as:
add_column :users, :ssn, :binary
In user model
has_encrypted_column :ssn
added pgcrpyto extension to development database
In config/initializers/pgcrypto.rb
PGCrypto.keys[.public] = {path: '.key.pub'}
PGCrypto.keys[.private] = {path: '.key.prv',armored: true, password: 'myPassword'}
rails c
User.create(username:'x',ssn:'123-45-6789')
Get this error, column is ssn so it looks like it is doing the right thing
TypeError: wrong argument type Arel::Nodes::BindParam (expected String)
from ... /vendor/ruby/2.2.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql/quoting.rb:19:in `escape_string'
from ... /vendor/ruby/2.2.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql/quoting.rb:19:in `quote_string'
from ... /vendor/ruby/2.2.0/gems/pgcrypto-0.4.1/lib/pgcrypto/adapter.rb:51:in `pgcrypto_encrypt_string'
The following **** is where I assume the bug is but not sure what method to call here:
Can't find much documentation about Arel::Nodes::BindParam. I assume this should set value to '123-45-6789', then encrypt it with pgcrypto_encrypt_string call. I verified that the key is correct.
57 def pgcrypto_insert(arel)
58 if table = PGCrypto[arel.ast.relation.name.to_s]
59 arel.ast.columns.each_with_index do |column, i|
60 if options = table[column.name.to_sym]
61 key = options[:key] || PGCrypto.keys[:public]
62 next unless key
63 # Encrypt encryptable columns
64 ***** value = arel.ast.values.expressions[i]
65 arel.ast.values.expressions[i] = pgcrypto_encrypt_string(value, key)
66 end
67 end
68 end
69 end
Any pointers greatly appreciated. Happy to fork and do a pull request when figured out.
Guessing it is due to Arel 6.0 in Rails 4.2.0
In rails 4.1.0, Arel 5.0
Arel::Nodes::BindParams < Arel::Nodes::SqlLiteral < String < Object < BasicObject
In rails 4.2.0 Arel 6.0
Arel::Nodes::BindParams < Arel::Nodes::Node < Object < BasicObject
Any tips on how to pull a value from Arel::Nodes::Node

Unknown Argument Error "-p" when deploying to heroku

We are deploying a rails app to Heroku. The app should be making a youtube api call, using the Trollop Gem as a command line parser. We keep getting this error back.
2014-07-30T23:17:57.526014+00:00 app[web.1]: Error: unknown argument '-p'.
2014-07-30T23:17:57.526020+00:00 app[web.1]: Try --help for help.
2014-07-30T23:17:57.526541+00:00 app[web.1]: Completed 500 Internal Server Error in 7466ms
This is what our Trollop code looks like.
def self.youtube_search(query)
youtube_service_api_name = "youtube"
youtube_api_version = "v3"
# opts = HTTParty.get("https://www.youtube.com/results?search_query=russia")
opts = Trollop::options do
opt :q, 'Search term', :source => String, :default => query
opt :maxResults, 'Max results', :source => :int, :default => 25
end
What's much stranger is that it was working an hour ago and now it's not. Does anyone have any ideas? This doesn't seem to be documented anywhere.
Trollop will fail when provided with undefined arguments. In your code the -q option is defined but -p is not.
Here are the relevant lines in the parse method:
trollop.rb
339 unless sym
340 next 0 if ignore_invalid_options
341 raise CommandlineError, "unknown argument '#{arg}'" unless sym
342 end
As for why it was working previously, perhaps you were passing -q then and inadvertently passing -p now?
yeah i got the same problem. unknown argument -p and the 500 internal error.
i decided to give up on Trollop and just hard code my own opts.
so i just initialized an opts hash and inserted key value pairs and no more problems.

Is there a way in the Rails Console to print a table of database contents?

I'm looking for a clean and simple way to print in the Rails Console the contents of my 5 row database with 2 columns.
Any ideas? I Googled around but didn't find much.
I think you should first use the hirb gem which provides a very pleasant way to print your tables columns.
Install hirb gem: gem install hirb
Add this gem to your project's Gemfile: gem 'hirb'
Go to your project's root folder and run Rails console: rails c
Enable hirb in the console:
require 'hirb'
Hirb.enable
If you want to limit the number of rows to display, you can do:
Model.limit(n)
For instance:
User.limit(5)
You can also specify the fields that you want to display using select:
User.select("name, email").limit(5)
You can also checkout table_print, it'll work something like this:
$ gem install table_print
$ rails c
> require 'table_print'
> tp Book.all
AUTHOR | SUMMARY | TITLE
-----------------------------------------------------------------------
Michael Connelly | Another book by Michael Con... | The Fifth Witness
Manning Mardale | From acclaimed historian Ma... | Malcolm X
Tina Fey | Worth it. -Trees | Bossypants
With hirb:
require 'hirb'
puts Hirb::Helpers::Table.render(ARRAY_OF_OBJECT_OR_HASHES)
# Examples:
puts Hirb::Helpers::Table.render([[1, "Terminator I"], [2, "Terminator II"]])
+---+---------------+
| 0 | 1 |
+---+---------------+
| 1 | Terminator I |
| 2 | Terminator II |
+---+---------------+
puts Hirb::Helpers::Table.render([{ id: 1, name: "Terminator I" }, { id: 2, name: "Terminator II" }])
+----+---------------+
| id | name |
+----+---------------+
| 1 | Terminator I |
| 2 | Terminator II |
+----+---------------+
# specifying the order of the fields
puts Hirb::Helpers::Table.render([{ id: 1, name: "Terminator I" }, { id: 2, name: "Terminator II" }], fields: [:name, :id])
+---------------+----+
| name | id |
+---------------+----+
| Terminator I | 1 |
| Terminator II | 2 |
+---------------+----+
Yes. Check out hirb gem. Also worth trying is wirble and awesome_print.

How to count lines of code?

I tried rake stats but that seems highly inaccurate. Perhaps it ignores several directories?
I use the free Perl script cloc. Sample usage:
phrogz$ cloc .
180 text files.
180 unique files.
77 files ignored.
http://cloc.sourceforge.net v 1.56 T=1.0 s (104.0 files/s, 19619.0 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Javascript 29 1774 1338 10456
Ruby 61 577 185 4055
CSS 10 118 133 783
HTML 1 13 3 140
DOS Batch 2 6 0 19
Bourne Shell 1 4 0 15
-------------------------------------------------------------------------------
SUM: 104 2492 1659 15468
-------------------------------------------------------------------------------
Here's a simple solution. It counts the lines of code in your rails project's app folder - CSS, Ruby, CoffeeScript, and all. At the root of your project, run this command:
find ./app -type f | xargs cat | wc -l
EDIT
Read the comments. Then try this instead:
find ./app -type f -name "*.rb" | xargs cat | sed "/^\s*\(#\|$\)/d" | wc -l
You can try out these two options:
Hack rake stats
Rakestats snippet from blogpost:
namespace :spec do
desc "Add files that DHH doesn't consider to be 'code' to stats"
task :statsetup do
require 'code_statistics'
class CodeStatistics
alias calculate_statistics_orig calculate_statistics
def calculate_statistics
#pairs.inject({}) do |stats, pair|
if 3 == pair.size
stats[pair.first] = calculate_directory_statistics(pair[1], pair[2]); stats
else
stats[pair.first] = calculate_directory_statistics(pair.last); stats
end
end
end
end
::STATS_DIRECTORIES << ['Views', 'app/views', /\.(rhtml|erb|rb)$/]
::STATS_DIRECTORIES << ['Test Fixtures', 'test/fixtures', /\.yml$/]
::STATS_DIRECTORIES << ['Email Fixtures', 'test/fixtures', /\.txt$/]
# note, I renamed all my rails-generated email fixtures to add .txt
::STATS_DIRECTORIES << ['Static HTML', 'public', /\.html$/]
::STATS_DIRECTORIES << ['Static CSS', 'public', /\.css$/]
# ::STATS_DIRECTORIES << ['Static JS', 'public', /\.js$/]
# prototype is ~5384 LOC all by itself - very hard to filter out
::CodeStatistics::TEST_TYPES << "Test Fixtures"
::CodeStatistics::TEST_TYPES << "Email Fixtures"
end
end
task :stats => "spec:statsetup"
metric_fu - A Ruby Gem for Easy Metric Report Generation
PS: I haven't tried any of the above, but metric_fu sounds interesting, see the screenshots of the output.
This one calculates number of files, total lines of code, comments, and average LOC per file. It also excludes files inside directories with "vendor" in their name.
Usage:
count_lines('rb')
Code:
def count_lines(ext)
o = 0 # Number of files
n = 0 # Number of lines of code
m = 0 # Number of lines of comments
files = Dir.glob('./**/*.' + ext)
files.each do |f|
next if f.index('vendor')
next if FileTest.directory?(f)
o += 1
i = 0
File.new(f).each_line do |line|
if line.strip[0] == '#'
m += 1
next
end
i += 1
end
n += i
end
puts "#{o.to_s} files."
puts "#{n.to_s} lines of code."
puts "#{(n.to_f/o.to_f).round(2)} LOC/file."
puts "#{m.to_s} lines of comments."
end
If your code is hosted on GitHub, you can use this line count website. Just enter your GitHub URL and wait for the result.
Example for Postgres: https://line-count.herokuapp.com/postgres/postgres
File Type Files Lines of Code Total lines
Text 1336 0 472106
C 1325 1069379 1351222
Perl 182 23917 32443
Shell 5 355 533
...

Resources