From 2b90f8df13bf966f694431c5217a25835e5d7712 Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 11:49:56 +0800 Subject: [PATCH 1/7] Plan the Nested Presentation and GraphQL Support --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 82b36df..f79941d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ 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 + +- 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. From f2d773409ed9e494e4cf2ad37b6992cd780e661f Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 11:56:13 +0800 Subject: [PATCH 2/7] More examples --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index f79941d..9dfa469 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,27 @@ 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.integer :age + end +end +``` + +This presenter will take its attributes `name` and `age` + ```ruby class UserPresenter < Feint::Presenter + attributes :name, :age end ``` +This presenter could be passed to the serializer + ```ruby class UsersController < ApplicationController def show @@ -49,6 +65,15 @@ class UsersController < ApplicationController end ``` +And render expected result + +```json +{ + "name": "USER NAME", + "age": "USER AGE", +} +``` + ## 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. From 5651f8267b2b23850170f4864aa30849999c0e5c Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 15:32:43 +0800 Subject: [PATCH 3/7] More README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9dfa469..92dbbd2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ is to 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 4b3e7a66ab0c3d3a78c3564e1b0202eaa938e548 Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 16:06:43 +0800 Subject: [PATCH 4/7] Prepare module for serialzation --- lib/feint.rb | 1 + lib/feint/serialization.rb | 9 +++++++++ spec/serialization_spec.rb | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 lib/feint/serialization.rb create mode 100644 spec/serialization_spec.rb 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/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/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 From 7a90e40ecda262788eff0da6bead254146413247 Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 16:29:32 +0800 Subject: [PATCH 5/7] Use parameter notation --- lib/feint/presenter.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/feint/presenter.rb b/lib/feint/presenter.rb index fe1fd8b..ccf03e3 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 From 784e16815cfaf8b478a7c51fa2551d6be19ba6b5 Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 16:45:26 +0800 Subject: [PATCH 6/7] Add attribute macro - Type Coercion - Transformation --- lib/feint/presenter.rb | 19 +++++++++++++++++++ spec/presenter_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/lib/feint/presenter.rb b/lib/feint/presenter.rb index ccf03e3..12b18af 100644 --- a/lib/feint/presenter.rb +++ b/lib/feint/presenter.rb @@ -22,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/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 From 8f55c08e077e5dfe3549afc0e3543c0eca97b8a8 Mon Sep 17 00:00:00 2001 From: shelling Date: Wed, 28 Dec 2016 18:14:03 +0800 Subject: [PATCH 7/7] More examples in README --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 92dbbd2..17f312e 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,10 @@ Consider we have a model `User`. 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 ``` @@ -53,6 +56,8 @@ 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 ``` @@ -71,8 +76,11 @@ And render expected result ```json { - "name": "USER NAME", - "age": "USER AGE", + "name": "NAME", + "age": "AGE", + "email": "EMAIL", + "nation": "COUNTRY", + "rank": "LEVEL", } ``` @@ -90,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). -