GithubHelp home page GithubHelp logo

yuba's Introduction

Yuba

Build Status Gem Version

warning

The version of this gem is now 0.0.x. It works, but there must be occasional breaking changes to the API.

Summary

Yuba adds new layers to rails.

  • Service
  • Form
  • ViewModel

It is convenient to use them in combination, but you can use them even individually. If you have difficulties with a large rails application, Yuba helps you.

Installation

Add this line to your application's Gemfile:

gem 'yuba'

And then execute:

$ bundle install

Support

  • Rails 5.2+
  • Ruby 2.5+

ViewModel

ViewModel is useful when there are many instance variables in controllers.

class PostViewModel < Yuba::ViewModel
  property :post
  property :author, public: true
  property :other, optional: true

  def title
    post.title
  end

  def body
    post.body
  end
end

Post = Struct.new(:title, :body)
post = Post.new('hello', 'world')

view_model = PostViewModel.new(post: post, author: 'willnet')
view.title #=> 'hello'
view.body #=> 'world'
view.author #=> 'willnet'
view.post #=> NoMethodError

property

.property method registers property to the class.

We need to pass those properties as arguments to the initialize except when optional: true is attached. You get ArgumentError if you don't pass property to initialize.

Property is default to private. It means you can use it in the internal instance. If you want to use it as public, use public: true option.

Auto Assign

You can use ViewModel in a controller like the following.

class PostsController < ApplicationController
  def show
    @view_model = PostViewModel.new(post: post, author: 'willnet')
  end
end

In view template, if you want to access post and author, you have to use @view_model instance variable like @view_model.post.title. if it feels troublesome, you can write like following

class PostsController < ApplicationController
  def show
    view_model = PostViewModel.new(post: post, author: 'willnet')
    render view_model: view_model
  end
end

view_model option of render takes ViewModel, which gets its public methods (include public property) and assigns them to instance variables in the view template. So you can write <%= @post.title %> instead of <%= @view_model.post.title %>

Service

Service is valuable when a controller has many application logic.

class PostController < ApplicationController
  def new
    @post = CreatePostService.call(user: current_user).post
  end

  def create
    service = CreatePostService.call(user: current_user, params: params)

    if service.success?
      redirect_to root_path
    else
      @post = service.post
      render :new
    end
  end
end

class CreatePostService < Yuba::Service
  property :user, public: true
  property :params, optional: true

  def call
    if post.save
      notify_to_admin
    else
      fail!
    end
  end

  def post
    user.posts.build(post_params)
  end

  private

  def notify_to_admin
    AdminMailer.notify_create_post(post).deliver_later
  end

  def post_params
    params.require(:post).permit(:title, :body)
  end
end
  • .property method registers property to the class like ViewModel.
  • .call invokes #call after assigning arguments as properties.
  • #success? returns true if you don't invoke #fail!

You have inspection methods for properties.

service = CreatePostService.new(user: someuser)
service.has_property?(:user) #=> true
service.has_value?(:user) #=> true
service.has_public_property?(:user) #=> true
service.has_private_property?(:user) #=> false
service.has_required_property?(:user) #=> true
service.has_optional_property?(:user) #=> false

Form

Form is just wrapper of reform-rails for now.

You can see documentation here.

Combination Sample

class ArtistsController < ApplicationController
  def new
    @view_model = Artist::CreateService.new(params: params).view_model
  end

  def create
    service = Artist::CreateService.call(params: params)

    if service.success?
      redirect_to artists_path
    else
      @view_model = service.view_model
      render :new
    end
  end
end
class Artist::CreateService < Yuba::Service
  property :params

  def call
    if form.validate(params)
      form.save
    else
      fail!
    end
  end

  def view_model
    Artist::CreateViewModel.new(form: form)
  end

  private

  def form
    @form ||= ArtistForm.new(Artist.new)
  end
end
class ArtistForm < Yuba::Form
  property :name

  validates :name, presence: true, length: { maximum: 100 }
end
class Artist::CreateViewModel < Yuba::ViewModel
  property :form, public: true
end

generators

You can use generators.

rails generate yuba:service create_artist
rails generate yuba:form artist
rails generate yuba:view_model artist_index

Contributing

You can try to test by doing as following.

git clone https://github.com/willnet/yuba.git
cd yuba
bundle
bundle exec rake

License

The gem is available as open source under the terms of the MIT License.

yuba's People

Contributors

takuyan avatar willnet avatar y-yagi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

y-yagi takuyan

yuba's Issues

Improve generator

like rails generator, generate property

./bin/rails g yuba:service create_comment creator:public
class CreateCommentService < Yuba::Service
  property creator, public: true
end

auto coercion

  • read schema definition
  • if same column name on property, use the type for coercion automatically

predicate methods raise error

class UserShowViewModel < Yuba::ViewModel
  property :current_user
  property :params

  def user
    @user ||= User.find_by_screen_name!(params[:screen_name])
  end

  def myself?
    current_user == user
  end
end

view_model = UserShowViewModel.new(
  current_user: current_user, params: params
)
render view_model: view_model

#<NameError: `@myself?' is not allowed as an instance variable name>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.