GithubHelp home page GithubHelp logo

procore-oss / blueprinter Goto Github PK

View Code? Open in Web Editor NEW
1.1K 1.1K 101.0 670 KB

Simple, Fast, and Declarative Serialization Library for Ruby

License: MIT License

Ruby 100.00%
json presenter rails resource-serializer ruby serializer

blueprinter's People

Contributors

allpurposename avatar amalarayfreshworks avatar amayer171 avatar bdlinick avatar bhooshiek-narendiran avatar cagmz avatar caws avatar claudioprocore avatar dannyporrello avatar dependabot[bot] avatar dlcarter avatar hparker avatar hugopeixoto avatar jhollinger avatar jmeridth avatar kurtaking avatar lessthanjacob avatar mcclayton avatar narendranvelmurugan avatar njbbaer avatar noobcit avatar pabhinaya avatar philipqnguyen avatar ritikesh avatar spencerneste avatar supremebeing7 avatar toddnestor avatar tpltn avatar vinaya-procore avatar wlkrw 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blueprinter's Issues

Add Caching

Endpoints with data that are not changed often, may benefit in performance by having Blueprinter return a previously cached JSON rather than running through the whole process of extracting the data from the passed object and serializing it to JSON.

How Blueprinter should do this is fully up for discussion.

Will this have CLI?

Hey guys I actually love this gem. But I wonder will this be heavily maintained? and will it have a CLI to generate the blueprints?

view terminology

Hey guys, just checking out your library for the first time, so feel free to close this due to early ignorance, but I just wanted to discuss your concept of the "view" terminology.

Since views are a well established keyword/role in Rails, why do you override the term with the view method, instead of using something more unique? FactoryBot uses the term traits for this same purpose. Or you could use the "branding" of a blueprint from the construction industry and do something like "layer", "cross section", or "floor".

😃

Polymorphic relations?

Hi,

Does blueprinter support polymorphic relations? Is there a publicly available code example demonstrating this functionality?

Thank you,

Dan

Local Method Support

We need to be able to allow end developers to add local methods in their blueprints.

For example:

class UserBlueprint < Blueprinter::Base
  field :full_name

  def full_name
    "#{object.first_name} #{object.last_name}"
  end
end

Aliasing fields?

Is there a way to alias fields besides creating completely new fields in the Blueprint?

class UserBlueprint < Blueprinter::Base
  identifier :uuid

  fields {first_name: :firstName}, :last_name, :email
  field first_name, as: :firstName
  ...

Stubbing associated blueprints

I'm writing unit tests for my blueprints. For some blueprints I have declared associations with other blueprints.
Here's an example:

class BookBlueprint < Blueprinter::Base
  association :author, blueprint: AuthorBlueprint
end

Which method should I stub on AuthorBlueprint ?

Serializing Timestamps as UNIX time.

Hi,

I'm just wondering if there's a way to set the default formatting for datetime/time/date objects to be a conversion to unix time, e.g. time_object.to_i

I've looked through the source, and the documentation but can't see an example of this, but it would be very useful. More than happy to dig in and submit a PR for this, but didn't want to duplicate work without checking first :)

`Base.object_to_hash` should be moved

We should experiment with moving jsonify/object_to_hash to a another class. Whether it should be in an existing class like ViewCollection or whether it should be in a new class specific to transforming the object to a hash and json.

Serialize datetime field

How should Blueprinter serialize datetime fields?

Should Blueprinter automatically check each fields to see if it is a DateTime or Time object?
Or should we provide a way for the user to specify that a field is of DateTime or Time?

Either way, we also would need to allow users to customize the datetime format that they would like to serialize to.

Pass a block to an association

Sometimes I'd like to expose an association which is not directly callable on the record itself. My main case is calling the association on a belongs_to or a has_one "middle" object.

I thought I could pass a block to the association like I do for the normal fields but it does not happen to work.

Do you think it would be possible for the associationto work like the field and accept an optional block returning an AR::Relation ?

Move body of `Base.prepare` to somewhere else?

Body of Base.prepare should be moved somewhere… maybe in ViewCollection.

https://github.com/procore/blueprinter/blob/e4c5daa77e869dea518618a74e058ca78d46aada/lib/blueprinter/base.rb#L132-L147

This is just a possible example, but I'm sure there's something better:

class Base
  ### Other code ###
  def self.prepare(object, view:)
    view_collection.prepare(object, view)
  end
  ### Other code ###
end

This is being brought up because Base class is knowing too much about the internals of other classes.

FactoryGirl -> FactoryBot

Should have made an issue first - my bad. But submitted a PR to switch FactoryGirl to FactoryBot to get rid of deprecation warnings and provide better future support

#74

Set up rubydoc

After we come up with a permanent name for this gem, we need to set this up with yardoc so that it can be parsed by rubydoc.

We would then need to document all the public methods using the yardoc tags.

Lets test Optimizer

Although Optimizer is a private class, we should at least test the public method to be sure that it is working. The reason why we need to test this is because whether optimizer runs correctly or whether it fails to run, it won't fail existing tests.

Ruby version / circleci 2.0

Working on upgrading the circleci config to 2.0, I noted that the required ruby version is 2.2.2, which is not supported any more.

Should this be bumped to 2.3, or even 2.4? Not sure what the backwards compatibility policy is.

Related: Should I test for multiple ruby versions in circleci? Right now I have a working config that runs on 2.3.7, 2.4.4 and 2.5.1, but this may be overkill.

Rails app inside spec

There's a rails app living inside spec/dummy. From what I can understand, this is so that we can use ActiveRecord and FactoryBot in spec/integrations/base_spec.rb / context 'Inside Rails project'.

If this is the case, could we replace it with setting up ActiveRecord in a test helper?

Idea : provide a mapper

I've had a little time to work on my problem described in #90 and I made a better solution (at leats to my eyes )

Here is the code of my patch (which I execute at the start of my app)

module BlueprintDelegation

  def mapping(&block)
    delegator_class.class_eval(&block)
  end

  #below this is private API

  # this is the tricky part, this method must store a different delegator class in each blueprint
  def delegator_class
    @delegator_class ||= Class.new(SimpleDelegator)
  end

  def build_object_delegator(object)
    delegator_class.new(object)
  end
  
  def object_to_hash(object, **named_params)
    object_delegator = build_object_delegator(object)
    super(object_delegator, **named_params)
  end
  
end

Blueprinter::Base.singleton_class.prepend BlueprintDelegation

The usage is done like this :

module PublicApi
  module V1
    class UserBlueprint < BaseBlueprint

      field :first_name
      field :last_name
      field :email

      view(:show) do
        association(:participations, blueprint: ParticipationBlueprint)
      end

      mapping do
        def participations
          learner.participations
        end
      end

    end
  end
end

I feel this is prettier than passing blocks all over the place and is usable with fields and associations and any extractor .

If you like the idea I'll make a PR

Optimization for ActiveRecord is not working.

These two singleton methods found in Base are not being utilized:

https://github.com/procore/painted-rabbit/blob/9c10456865f607c7c6e0e553bd965b790d63ab5e/lib/painted_rabbit/base.rb#L85-L110

The culprit is that the guard clause object.is_a?(ActiveRecord::Base) && object.respond_to?(:klass) will never resolve to true. The reasoning is that #klass is a method found in ActiveRecord::Relation and not ActiveRecord::Base. While this problem does not break anything, the original intention to increase performance for both ActiveRecord::Relation and ActiveRecord::Base is currently not being achieved.

A few things to consider:
How should we consider optimizing for both ActiveRecord::Relation and ActiveRecord::Base objects. Do we even need to optimize for both?

Create swagger (OpenAPI) definitions from views

It would be really cool, and helpful, if we had a way to be able to generate OpenAPI/swagger definition specs (namespaced by views, if they have one) from individual blueprints.

This will likely require more definition than we're used to giving these fields, like data type, etc. (Adding this may be helpful anyways)

Automatic Eager Loading on Associations?

class ProductLineBlueprint < ApplicationBlueprint
  identifier :id
  
  fields :name

  view :extended do
    association :product_features, name: :features, blueprint: ProductFeatureBlueprint, view: :extended
  end
end
class ProductFeatureBlueprint < ApplicationBlueprint
  identifier :id
  
  fields :name, :type

  view :extended do
    association :feature_options, name: :options, blueprint: FeatureOptionBlueprint
  end
end

In this example, ProductFeatures and FeatureOptions should be eager loaded when necessary.

Conditional fields support

Consider example: we have many employees and only supervisor can see employee's salary, right now AFAIU it's only possible to set salary to nil using

class EmployeeBlueprint < Blueprinter::Base
  identifier :id
  fields :first_name, :last_name

  field :salary do |employee, params|
    if params[:current_user] == employee.supervisor
      employee_salary
    else
      nil
    end
  end
end

It's impossible to use a :view with salary field, since supervisors are different for different employees. So it would be useful to have something like

class EmployeeBlueprint < Blueprinter::Base
  identifier :id
  fields :first_name, :last_name

  field :salary, if: :supervisor?

  def supervisor?(employee, params)
    params[:current_user] == employee.supervisor
  end
end

to remove this field completely.

Document the public methods via yard

We should document all public methods via yard syntax. This way, if any developers ever come into trouble utilizing any of our public methods, they will have some reference when they look it up.

Converting null fields to ""

Hi, I have a problem, I am trying to use your gem in an API which is already serving data to our customers to make it more performant.

I need to convert nil values in fields to empty string "" instead of null in order to make our response the same we have right now, something like the default value for associations, but for fields.

I don't know if there is a way to do so, I couldn't find it yet. Could anyone guide me?

Thanks in advance

Better/more unit test on public methods in private classes.

Better/more unit test on public methods in private classes. We have a number of private classes, with exposed public methods being utilized by other classes. If those methods break but are undetectable in other tests, we should add tests specific for it.

Naming associations

Hey there.

This is both a question for clarification and perhaps a feature suggestion.

I love the association pattern. But I would like to be able to rename it in the presentation layer so that the generated JSON is semantically clean regardless of the underlying system's limitations.

An example here is that I have a Product model in Rails. This has a has_many :product_errors relation because rails don't support renaming the built-in errors relation.

Here is my blueprint class:

class ProductBlueprint < Blueprinter::Base
  identifier :sku
  association :product_errors, blueprint: ProductErrorBlueprint
end

This generates the following JSON:

[
 {
    "sku": "prod1",
    "product_errors": []
  }
]

I would like to be able to rename the association in the generated JSON to something like errors for a more precise JSON result. Perhaps something like adding as: :errors to the association would be a solution here?

class ProductBlueprint < Blueprinter::Base
  identifier :sku
  association :product_errors, as: :errors, blueprint: ProductErrorBlueprint
end

Which would then generate:

[
 {
    "sku": "prod1",
    "errors": []
  }
]

Let me know if what you think.

Thanks.

Rename Blueprint.render

Blueprint.render sounds weird when using it inside a Rails controller because there would be two render methods being used, one on the controller and one on the blueprint. See below:

def show
  render json: PostBlueprinter.render(@post)
end

Blueprint.print is a better method name, and it should alias to Blueprint.render.

Rendering null date with date_format option causes error

Hi, thanks for the gem! We're choosing library to replace roar and we've faced with the next issue:

class A < Blueprinter::Base
  field :date, datetime_format: '%FT%T%:z'
end

# this causes Blueprinter::BlueprinterError Cannot format invalid DateTime object
A.render_as_hash(OpenStruct.new(date: nil))

I suppose it should render nil as is. I'll open PR soon :)

Implement support for OpenAPI Data Types

We should implement a type (or similar) attribute on fields that corresponds to the OpenAPI spec data types.

The benefits are numerous, but notably:

  • We can more intelligently decide how to serialize primitives
  • It can empower direct OpenAPI spec generation (re: #21)

Benchmarks (comparisons with other serializers)

Hi,

How does blueprinter compare to other serializers? I.e. AMS, Panko, Fast JSON-API. When shopping for a serializer, performance is a top priority. Would be a nice addition to the readme 👍

Keep up the good work with Blueprinter!

please provide more info

Dear @dlcarter,

This gem, looks like new version of your ApplicationSerializer mentioned by you in "RailsConf 2017: ​Rails APIs: The Next Generation" talk. I'm right about it? (semantic looks almost identical). Can you provide some examples about how to integrate it with Rails application? Do you have any new remarks from using it in your application? Maybe some blog post about it?

ActiveRecord Optimization

When it comes to ActiveRecord objects, Blueprinter should not need to load all columns to extract values, it should only need to select the columns it needs as specified in the Blueprint. This would reduce the time spent querying the database as well as reducing memory footprint.

Problem n+1 query

When i used association in my export rails console are joins for each relation model,That`s very bad ...

Allow custom options

Add custom options that can be passed to render and utilized in the blueprint.

Custom options are any additional objects developers want to pass to the blueprint to help with their local methods.

Example use case:

class UserBlueprint < Blueprinter::Base
  field :company_address
  def company_address
    @options[:company].address #something like this
  end
end

class UsersController
  def index
    # other code
    render json: Blueprinter.render(user, company: company)
  end
end

ApplicationBlueprinter pattern

Is there any reason not to use the Application pattern with Blueprinter?

application_blueprint.rb:

class ApplicationBlueprint < Blueprinter::Base
  identifier :id
end

user_blueprint.rb:

class UserBlueprint < ApplicationBlueprint
  fields :name
end

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.