Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
johnpanos committed Aug 5, 2024
1 parent 8bf5c45 commit 204c2c2
Showing 1 changed file with 194 additions and 11 deletions.
205 changes: 194 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,207 @@
# Typesmith

TODO: Delete this and the text below, and describe your gem

Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/typesmith`. To experiment with that code, run `bin/console` for an interactive prompt.
Typesmith is a Ruby gem that provides a simple and flexible way to define and validate data structures in your Ruby applications. It's particularly useful for creating type-safe parameter objects and data transfer objects (DTOs) in Rails applications. Additionally, Typesmith includes a powerful generator that can automatically create corresponding TypeScript types for your frontend, ensuring type consistency across your full-stack application.

## Installation

TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
Add this line to your application's Gemfile:

```ruby
gem 'typesmith'
```

Install the gem and add to the application's Gemfile by executing:
And then execute:

$ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
$ bundle install

If bundler is not being used to manage dependencies, install the gem by executing:
Or install it yourself as:

$ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
$ gem install typesmith

## Usage

TODO: Write usage instructions here
Typesmith allows you to define structured data objects with type checking. Here's a basic example:

```ruby
class UserParams < Typesmith::Definition
property :name, type: :string
property :age, type: :number
property :email, type: :string
property :is_active, type: :boolean
end

user_params = UserParams.new(
name: "John Doe",
age: 30,
email: "[email protected]",
is_active: true
)

puts user_params.name # Output: John Doe
puts user_params.age # Output: 30
```

### Nested Structures

You can also define nested structures:

```ruby
class AddressParams < Typesmith::Definition
property :street, type: :string
property :city, type: :string
property :country, type: :string
end

class UserWithAddressParams < Typesmith::Definition
property :name, type: :string
property :address, type: AddressParams
end

user_params = UserWithAddressParams.new(
name: "Jane Doe",
address: {
street: "123 Main St",
city: "Anytown",
country: "USA"
}
)

puts user_params.address.city # Output: Anytown
```

### Optional Properties

You can make properties optional:

```ruby
class OptionalParams < Typesmith::Definition
property :required_field, type: :string
property :optional_field, type: :string, optional: true
end

params = OptionalParams.new(required_field: "Hello")
puts params.optional_field # Output: nil
```

### Array Properties

You can define properties that are arrays of a specific type:

```ruby
class ArrayParams < Typesmith::Definition
property :tags, type: [:string]
end

params = ArrayParams.new(tags: ["ruby", "rails", "typesmith"])
puts params.tags # Output: ["ruby", "rails", "typesmith"]
```

### Using in Controllers

Typesmith is particularly useful in Rails controllers for validating and structuring incoming parameters:

```ruby
class UsersController < ApplicationController
def create
user_params = UserParams.new(params.require(:user).permit!)
User.create!(user_params.attributes)
render json: { status: 'success' }
rescue Typesmith::BaseProperty::InvalidTypeError => e
render json: { error: e.message }, status: :unprocessable_entity
end
end
```

## TypeScript Generator

One of Typesmith's most powerful features is its ability to automatically generate TypeScript type definitions that correspond to your Ruby definitions. This ensures type consistency between your backend and frontend, reducing errors and improving developer productivity.

### Generating TypeScript Types

To generate TypeScript types from your Typesmith definitions, you can use the Typesmith generator. Here's how to use it:

1. Define your Typesmith classes in Ruby:

```ruby
class UserParams < Typesmith::Definition
property :name, type: :string
property :age, type: :number
property :email, type: :string
property :is_active, type: :boolean
end

class AddressParams < Typesmith::Definition
property :street, type: :string
property :city, type: :string
property :country, type: :string
end

class UserWithAddressParams < Typesmith::Definition
property :name, type: :string
property :address, type: AddressParams
end
```

2. Run the Typesmith generator:

```
$ rails generate typesmith:typescript
```

This command will scan your Ruby files for Typesmith definitions and generate corresponding TypeScript types in the `app/javascript/types/__generated__` directory.

### Generated TypeScript Types

For the above Ruby definitions, Typesmith will generate the following TypeScript types:

```typescript
// app/javascript/types/__generated__/user_params.ts
export interface UserParams {
name: string;
age: number;
email: string;
isActive: boolean;
}

// app/javascript/types/__generated__/address_params.ts
export interface AddressParams {
street: string;
city: string;
country: string;
}

// app/javascript/types/__generated__/user_with_address_params.ts
import { AddressParams } from './address_params';

export interface UserWithAddressParams {
name: string;
address: AddressParams;
}
```

### Using Generated Types in Frontend

You can now use these generated types in your TypeScript frontend code:

```typescript
import { UserWithAddressParams } from '../types/__generated__/user_with_address_params';

const user: UserWithAddressParams = {
name: "Jane Doe",
address: {
street: "123 Main St",
city: "Anytown",
country: "USA"
}
};

// TypeScript will enforce type checking based on the generated definitions
```

### Keeping Types in Sync

It's recommended to run the Typesmith generator as part of your continuous integration (CI) process to ensure that your TypeScript types are always in sync with your Ruby definitions. You can add a step in your CI pipeline to run the generator and fail the build if there are any uncommitted changes to the generated files.


## Development

Expand All @@ -28,12 +211,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/typesmith. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/typesmith/blob/main/CODE_OF_CONDUCT.md).
Bug reports and pull requests are welcome on GitHub at https://github.com/johnpanos/typesmith. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/johnpanos/typesmith/blob/main/CODE_OF_CONDUCT.md).

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

## Code of Conduct

Everyone interacting in the Typesmith project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/typesmith/blob/main/CODE_OF_CONDUCT.md).
Everyone interacting in the Typesmith project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/johnpanos/typesmith/blob/main/CODE_OF_CONDUCT.md).

0 comments on commit 204c2c2

Please sign in to comment.