diff --git a/README.md b/README.md index 82b36df..17f312e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ is to - Convert keys of parameters from one name to another name for model initialization. - Lock mandatory and optional columns used in a presenter. +Advanced features will include + +- Data Conversion. Pass a callback to convert data to a certain format +- Type Coercion. Ensure the result is a certain type. Implicit data conversion. +- Nested Presentation. If some attributes are from associations, delegate them to presenters of associations. +- GraphQL Support. Being the glue of GraphQL type definitions and legacy models + From perspective of a serializer, a Feint preseter acts as the model it presents except for different attribute names. You can replace a model instance with its Feint presenter and it will work seamelessly. @@ -30,11 +37,32 @@ Or install it yourself as: ## Usage +Consider we have a model `User`. + +```ruby +class User < ActiveRecord::Base + connection.create_table table_name do |f| + f.string :name + f.string :email + f.string :country + f.integer :age + f.integer :level + end +end +``` + +This presenter will take its attributes `name` and `age` + ```ruby class UserPresenter < Feint::Presenter + attributes :name, :age + attribute :email, type: String + transform country: :nation, level: :rank end ``` +This presenter could be passed to the serializer + ```ruby class UsersController < ApplicationController def show @@ -44,6 +72,18 @@ class UsersController < ApplicationController end ``` +And render expected result + +```json +{ + "name": "NAME", + "age": "AGE", + "email": "EMAIL", + "nation": "COUNTRY", + "rank": "LEVEL", +} +``` + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. @@ -58,4 +98,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/shelli ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). - diff --git a/lib/feint.rb b/lib/feint.rb index 07e2e4b..01849a0 100644 --- a/lib/feint.rb +++ b/lib/feint.rb @@ -2,4 +2,5 @@ module Feint autoload :Presenter, "feint/presenter" + autoload :Serialization, "feint/serialization" end diff --git a/lib/feint/presenter.rb b/lib/feint/presenter.rb index fe1fd8b..12b18af 100644 --- a/lib/feint/presenter.rb +++ b/lib/feint/presenter.rb @@ -5,7 +5,7 @@ def initialize(model) # :nodoc: end class << self - # Takes a list of columns + # @param columns [Symbol] list def attributes(*columns) columns.each do |column| define_method column do @@ -14,8 +14,7 @@ def attributes(*columns) end end - # Take a list of mappiings - # { old_column_name => new_column_name } + # @param mappings [Hash] keys for old names and values for new names def transform(mappings) mappings.each do |old, new| define_method new do @@ -23,6 +22,25 @@ def transform(mappings) end end end + + # @param name [Symbol] + # @param options [Hash] + # options[:key] is used as field name + # options[:type] is used to check type + def attribute(name, options = {}) + type = options[:type] || Object + key = options[:key] || name + + define_method key do + value = @model.send(name) + + if value.is_a?(type) + return value + else + raise TypeError, "#{name} should be #{type} and is #{value.class}" + end + end + end end end end diff --git a/lib/feint/serialization.rb b/lib/feint/serialization.rb new file mode 100644 index 0000000..5f41de0 --- /dev/null +++ b/lib/feint/serialization.rb @@ -0,0 +1,9 @@ +module Feint + module Serialization + def as_json(options = nil) + end + + def serializable_hash(options = nil) + end + end +end diff --git a/spec/presenter_spec.rb b/spec/presenter_spec.rb index fc6f07f..98f2be9 100644 --- a/spec/presenter_spec.rb +++ b/spec/presenter_spec.rb @@ -5,6 +5,7 @@ class User < ActiveRecord::Base establish_connection(adapter: "sqlite3", database: "feint.db") connection.create_table table_name, force: true do |t| t.string :name + t.string :email t.integer :age end end @@ -17,6 +18,12 @@ class UserTransformer < Feint::Presenter transform name: :handle, age: :year end +class UserConstraintPresenter < Feint::Presenter + attribute :name, key: :handle + attribute :email + attribute :age, type: String +end + describe Feint::Presenter do let(:user) { User.create(name: "shelling", age: 30) } @@ -31,4 +38,21 @@ class UserTransformer < Feint::Presenter expect(user_transformer.handle).to eq(user.name) expect(user_transformer.year).to eq(user.age) end + + it "can declare single field" do + user_constraint_presenter = UserConstraintPresenter.new(user) + expect(user_constraint_presenter.handle).to eq(user.name) + end + + it "can declare single field and guess field name" do + user_constraint_presenter = UserConstraintPresenter.new(user) + expect(user_constraint_presenter.email).to eq(user.email) + end + + it "can declare single field and check its type" do + user_constraint_presenter = UserConstraintPresenter.new(user) + expect { + user_constraint_presenter.age + }.to raise_error(TypeError, "age should be String and is Fixnum") + end end diff --git a/spec/serialization_spec.rb b/spec/serialization_spec.rb new file mode 100644 index 0000000..ac27c43 --- /dev/null +++ b/spec/serialization_spec.rb @@ -0,0 +1,4 @@ +require "spec_helper" + +describe Feint::Serialization do +end