Skip to content

Commit

Permalink
Merge pull request #4 from Uaitt/refactor/minor-adjustements
Browse files Browse the repository at this point in the history
Refactor: minor adjustements
  • Loading branch information
Uaitt authored Jul 26, 2023
2 parents b60627d + a72f444 commit d7ec063
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 48 deletions.
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# AASM RBS Generator
Easily generate `RBS` signatures of all the `AASM`-automatically generated methods and constants of your ruby classes.
Easily generate RBS signatures for all the AASM automatically generated methods and constants of your ruby classes.

## Description
If you have found this gem, you probably are adding a type system with `RBS` on top of your `Ruby` project or `Rails` application, and you are managing the state of some of your classes with the `AASM` gem.
If you have found this gem, you probably are adding a type system with RBS on top of your Ruby project or Rails application, and you are managing the state of some of your classes with the AASM gem.

If you have no idea about what `AASM` is, you should take a look at their [README]() first.
If you have no idea about what AASM is, I encourage you to take a look at their [README](/~https://github.com/aasm/aasm) first.

You should now know that when you `include AASM` on a Ruby class and you define states, events and transitions, your classes will automatically get a few things, including:
You should now know that when you `include AASM` inside of a Ruby class and you define states, events and transitions, your classes will automatically get a few things, including:
- a constant for every state
- instance methods for every state
- scopes for every state if the class is an `ActiveRecord` model
- scopes for every state if the class is an `ActiveRecord` model and the [automatic scopes](/~https://github.com/aasm/aasm#automatic-scopes) feature was not disabled manually
- instance methods for every event

The problem is that when writing `RBS` you should write the signatures for the previous things one-by-one and it can get really frustrating/boring when dealing with large classes.
The problem is that when writing RBS you should write the signatures for the previous things one-by-one and it can get really frustrating/boring when dealing with large classes.

With this small gem, you can now generate all those signatures automatically with a single command, and save time for doing something more meaningful.

## Installation
Add the following line to your application's `Gemfile`:
Add the following line to your application's `Gemfile` in the `development` group:

```rb
gem 'aasm_rbs'
Expand All @@ -26,11 +26,13 @@ gem 'aasm_rbs'
Then, execute `bundle install` in order to load the gem's code.

## Usage
Generating the `RBS` signatures is as easy as launching the following command from the command-line:
Generating the RBS signatures is as easy as launching the following command from the command-line:
```
bundle exec aasm_rbs ClassName
```

The generated signatures should appear in `stdout`.
The generated signatures will appear in `stdout`.


## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/license/mit/).
2 changes: 1 addition & 1 deletion aasm-rbs.gemspec → aasm_rbs.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
spec.name = 'aasm_rbs'
spec.version = AasmRbs::VERSION
spec.summary = 'AASM RBS'
spec.description = 'Easily generate RBS signatures for all the Ruby classes that implement a state-machine with AASM'
spec.description = 'Easily generate RBS signatures for all the AASM automatically generated methods and constants of your ruby classes.'
spec.license = 'MIT'

spec.required_ruby_version = '>= 3.0.0'
Expand Down
6 changes: 3 additions & 3 deletions exe/aasm_rbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

require_relative '../lib/aasm_rbs'

puts ''
puts AasmRbs.run(ARGV[0] || '')
puts ''
$stdout.puts ''
$stdout.puts AasmRbs.run(ARGV[0] || '')
$stdout.puts ''
2 changes: 2 additions & 0 deletions lib/aasm_rbs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ def self.run(klass_name)
output.new_line
output.add_events(events)
output.finalize
rescue StandardError
print "aasm_rbs received an invalid class name."
end
end
11 changes: 6 additions & 5 deletions lib/aasm_rbs/output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

module AasmRbs
class Output
attr_reader :klass
attr_accessor :data

def initialize(klass)
@klass = klass
superclass = klass.superclass == Object ? nil : " < #{klass.superclass}"
Expand All @@ -14,7 +11,8 @@ def initialize(klass)
def add_states(states)
add_state_constants(states)
create_scopes = klass.aasm.state_machine.config.create_scopes
add_state_scopes(states) if klass.respond_to?(:aasm_create_scope) && create_scopes
active_record_model = klass.respond_to?(:aasm_create_scope)
add_state_scopes(states) if active_record_model && create_scopes
add_predicate_states_methods(states)
end

Expand All @@ -37,13 +35,16 @@ def finalize

private

attr_reader :klass
attr_accessor :data

def add_state_constants(states)
states.each { |state| self.data += " STATE_#{state.upcase}: String\n" }
self.data += "\n"
end

def add_state_scopes(states)
states.each { |state| self.data += " def self.#{state}: () -> #{klass}::ActiveRecord_Relation\n" }
states.each { |state| self.data += " def self.#{state}: () -> ::ActiveRecord_Relation\n" }
self.data += "\n"
end

Expand Down
8 changes: 4 additions & 4 deletions sig/aasm_rbs.rbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module AasmRbs
def self.run: (String) -> String
def self.run: (String) -> String?
end

# There is yet no official RBS signatures for the AASM module that gets
# included in a class. Therefore, the only thing we can do in order to not
# make Steep unhappy is to declare a dummy signature ourself.
# There are yet no official RBS signatures for the AASM module that gets
# included in our classes. Therefore, the only thing we can do in order to not
# make Steep unhappy is to declare a dummy signature ourself for the #aasm method.
class Class
def aasm: () -> untyped
end
8 changes: 5 additions & 3 deletions sig/aasm_rbs/output.rbs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
module AasmRbs
class Output
attr_reader klass: Class
attr_accessor data: String

def initialize: (Class) -> String

def add_states: (Array[String]) -> Array[String]
Expand All @@ -11,6 +8,11 @@ module AasmRbs
def new_line: () -> String
def finalize: () -> String

private

attr_reader klass: Class
attr_accessor data: String

def add_state_constants: (Array[String]) -> String
def add_state_scopes: (Array[String]) -> String
def add_predicate_states_methods: (Array[String]) -> Array[String]
Expand Down
73 changes: 58 additions & 15 deletions spec/aasm_rbs/output_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,22 @@ def cleaning?: () -> bool
it 'correctly adds the states signatures to the data' do
states = klass.aasm.states.map(&:name)
output.add_states(states)
output.finalize
expect(output.data).to eq(expected_rbs)
expect(output.finalize).to eq(expected_rbs)
end
end

context 'with an ActiveRecord model' do
context 'with an ActiveRecord model that enables AASM automatic scopes' do
let(:klass) { User }
let(:expected_rbs) do
<<~RBS
class User < ActiveRecord::Base
class User < ApplicationRecord
STATE_PENDING: String
STATE_APPROVED: String
STATE_REJECTED: String
def self.pending: () -> User::ActiveRecord_Relation
def self.approved: () -> User::ActiveRecord_Relation
def self.rejected: () -> User::ActiveRecord_Relation
def self.pending: () -> ::ActiveRecord_Relation
def self.approved: () -> ::ActiveRecord_Relation
def self.rejected: () -> ::ActiveRecord_Relation
def pending?: () -> bool
def approved?: () -> bool
Expand All @@ -57,8 +56,30 @@ def rejected?: () -> bool
it 'correctly adds the states signatures to the data' do
states = klass.aasm.states.map(&:name)
output.add_states(states)
output.finalize
expect(output.data).to eq(expected_rbs)
expect(output.finalize).to eq(expected_rbs)
end
end

context 'with an ActiveRecord model that disables AASM automatic scopes' do
let(:klass) { Refund }
let(:expected_rbs) do
<<~RBS
class Refund < ApplicationRecord
STATE_PENDING: String
STATE_PROCESSED: String
STATE_FAILED: String
def pending?: () -> bool
def processed?: () -> bool
def failed?: () -> bool
end
RBS
end

it 'correctly adds the states signatures to the data' do
states = klass.aasm.states.map(&:name)
output.add_states(states)
expect(output.finalize).to eq(expected_rbs)
end
end
end
Expand Down Expand Up @@ -88,16 +109,15 @@ def may_sleep?: (*untyped) -> bool
it 'correctly adds the events signatures to the data' do
events = klass.aasm.events.map(&:name)
output.add_events(events)
output.finalize
expect(output.data).to eq(expected_rbs)
expect(output.finalize).to eq(expected_rbs)
end
end

context 'with an ActiveRecord model' do
context 'with an ActiveRecord model that enables AASM automatic scopes' do
let(:klass) { User }
let(:expected_rbs) do
<<~RBS
class User < ActiveRecord::Base
class User < ApplicationRecord
def approve: (*untyped) -> bool
def approve!: (*untyped) -> bool
def approve_without_validation!: (*untyped) -> bool
Expand All @@ -113,8 +133,31 @@ def may_reject?: (*untyped) -> bool
it 'correctly adds the events signatures to the data' do
events = klass.aasm.events.map(&:name)
output.add_events(events)
output.finalize
expect(output.data).to eq(expected_rbs)
expect(output.finalize).to eq(expected_rbs)
end
end

context 'with an ActiveRecord model that disables AASM automatic scopes' do
let(:klass) { Refund }
let(:expected_rbs) do
<<~RBS
class Refund < ApplicationRecord
def process: (*untyped) -> bool
def process!: (*untyped) -> bool
def process_without_validation!: (*untyped) -> bool
def may_process?: (*untyped) -> bool
def fail: (*untyped) -> bool
def fail!: (*untyped) -> bool
def fail_without_validation!: (*untyped) -> bool
def may_fail?: (*untyped) -> bool
end
RBS
end

it 'correctly adds the events signatures to the data' do
events = klass.aasm.events.map(&:name)
output.add_events(events)
expect(output.finalize).to eq(expected_rbs)
end
end
end
Expand Down
42 changes: 37 additions & 5 deletions spec/aasm_rbs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require_relative '../lib/aasm_rbs'
require_relative 'classes/job'
require_relative 'classes/user'
require_relative 'classes/refund'

RSpec.describe AasmRbs do
describe '.run' do
Expand Down Expand Up @@ -43,18 +44,18 @@ def may_sleep?: (*untyped) -> bool
end
end

context 'with an ActiveRecord model' do
context 'with an ActiveRecord model that enables AASM automatic scopes' do
let(:klass_name) { 'User' }
let(:expected_rbs) do
<<~RBS
class User < ActiveRecord::Base
class User < ApplicationRecord
STATE_PENDING: String
STATE_APPROVED: String
STATE_REJECTED: String
def self.pending: () -> User::ActiveRecord_Relation
def self.approved: () -> User::ActiveRecord_Relation
def self.rejected: () -> User::ActiveRecord_Relation
def self.pending: () -> ::ActiveRecord_Relation
def self.approved: () -> ::ActiveRecord_Relation
def self.rejected: () -> ::ActiveRecord_Relation
def pending?: () -> bool
def approved?: () -> bool
Expand All @@ -77,5 +78,36 @@ def may_reject?: (*untyped) -> bool
expect(actual_output).to eq(expected_rbs)
end
end

context 'with an ActiveRecord model that disables AASM automatic scopes' do
let(:klass_name) { 'Refund' }
let(:expected_rbs) do
<<~RBS
class Refund < ApplicationRecord
STATE_PENDING: String
STATE_PROCESSED: String
STATE_FAILED: String
def pending?: () -> bool
def processed?: () -> bool
def failed?: () -> bool
def process: (*untyped) -> bool
def process!: (*untyped) -> bool
def process_without_validation!: (*untyped) -> bool
def may_process?: (*untyped) -> bool
def fail: (*untyped) -> bool
def fail!: (*untyped) -> bool
def fail_without_validation!: (*untyped) -> bool
def may_fail?: (*untyped) -> bool
end
RBS
end

it 'returns the right RBS' do
actual_output = described_class.run(klass_name)
expect(actual_output).to eq(expected_rbs)
end
end
end
end
7 changes: 7 additions & 0 deletions spec/classes/application_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

require 'active_record'

class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
end
22 changes: 22 additions & 0 deletions spec/classes/refund.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require 'aasm'
require_relative 'application_record'

class Refund < ApplicationRecord
include AASM

aasm column: :state, create_scopes: false do
state :pending, initial: true
state :processed
state :failed

event :process do
transitions from: :pending, to: :processed
end

event :fail do
transitions from: %i[pending processed], to: :failed
end
end
end
6 changes: 3 additions & 3 deletions spec/classes/user.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# frozen_string_literal: true

require 'aasm'
require 'active_record'
require_relative 'application_record'

class User < ActiveRecord::Base
class User < ApplicationRecord
include AASM

aasm column: 'state' do
aasm column: :state do
state :pending, initial: true
state :approved
state :rejected
Expand Down

0 comments on commit d7ec063

Please sign in to comment.