procore-oss / blueprinter Goto Github PK
View Code? Open in Web Editor NEWSimple, Fast, and Declarative Serialization Library for Ruby
License: MIT License
Simple, Fast, and Declarative Serialization Library for Ruby
License: MIT License
Is it possible to have an option for ignoring nil values like this one in JBuilder https://www.rubydoc.info/github/rails/jbuilder/Jbuilder:ignore_nil!
I'm using JBuilder everywhere, and we have a convention to skip items with nil values (client wants it). I'm going to use Blueprinter instead, but the last thing which blocks to do that is this missing option.
It would be helpful in some cases to return a hash.
This is already possible with the prepare
method. https://github.com/procore/blueprinter/blob/master/lib/blueprinter/base.rb#L141 but it is listed as private and does not support the same interface as render.
Suggested method signature could be render_as_hash(object, options = {})
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.
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?
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".
😃
Similarly to how field
and association
now accept a block, should identifier
also be able to accept a block?
Since performance is a high priority for our serializer - the sooner we implement some basic benchmarking the better.
https://ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc/Benchmark.html
We should introduce benchmark tests so that we can see how changes we make effect our performance from PR to PR.
Passing a :name
option to field
or association
will rename the field. This feature exists, but it should be better documented. This stems from #87
Hi,
Does blueprinter support polymorphic relations? Is there a publicly available code example demonstrating this functionality?
Thank you,
Dan
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
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
...
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
?
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 :)
Since Oj (https://github.com/ohler55/oj) is popular, performant, and above all FAST, we should either default to use it to serialize if detected, or provide a configuration to be able to set it to use Oj by default.
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.
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.
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 association
to work like the field
and accept an optional block returning an AR::Relation ?
You'r assigning a new hash to a local variable named options
thereby shadowing the options parameter received by the method
Body of Base.prepare
should be moved somewhere… maybe in ViewCollection.
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.
The default serializer for Base.field
should be PublicSendSerializer
.
Several questions were raised after reviewing Base.include_assocations
with @AllPurposeName. Here they are:
Base
or does it belong in another class? Perhaps in Optimizer
class? If it belongs somewhere else, let's move it.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
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.
These would return NameError: uninitialized constant ActiveRecord
exception if this gem is not used in a Rails app.
https://github.com/procore/painted-rabbit/blob/master/lib/painted_rabbit/base.rb#L86
https://github.com/procore/painted-rabbit/blob/master/lib/painted_rabbit/base.rb#L97
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.
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?
This Base
class seems to know too much about the internals of Field
. Does that need to be moved into the Field
object? So just call field.serialize(arg1, arg2)
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
These two singleton methods found in Base
are not being utilized:
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?
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)
We need to determine the minimum ruby version that we should support for this gem.
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.
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.
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.
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. 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.
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.
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
.
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 :)
We should implement a type
(or similar) attribute on fields that corresponds to the OpenAPI spec data types.
The benefits are numerous, but notably:
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!
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?
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.
When i used association in my export rails console are joins for each relation model,That`s very bad ...
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
Current Base.association
accepts an option called :serializer
. That should be :blueprint
.
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.