I am building a REST API in Ruby using Grape.
My frontend is written in AnguarJS and the default datetime serialization made by Grape is not correctly being understood by angulars' date filter. So my idea is to format all the datetime attributes in ruby before sending it.
What is the best approach ?
Is there a generic Time serialization in Grape that I can use ?
If not, is it feasible to perform a deep lookup in a ruby list or map, find all Time instances, and replace them by a formatted string ?
My current solution below is tightly coupled for one attribute, but I would like to extend this formatting to all Time instantes.
result_json.each do | x |
x[:date] = x[:date].strftime("%Y%m%dT%H:%M:%S")
end
If you're using Grape Entity then you can extend the ApiHelper to include a new formatter. For example:
module ApiHelpers
extend Grape::API::Helpers
Grape::Entity.format_with :iso8601 do |date|
date.iso8601 if date
end
end
Once you have done it, you can use it in your Entities like this:
module Entities
class MyModel < Grape::Entity
expose :updated_at, format_with: :iso8601
end
class AnotherModel < Grape::Entity
expose :created_at, format_with: :iso8601
end
end
I believe it's the best way to tackle this problem because you shouldn't change the Time class just to solve a problem in your View layer. Using Grape Entity is a good practice too. You should always shield your API from possible changes in your Models. Also, remember that what you expose through a Rest API are "Resources" and not Models. In fact, a resource can even be a combination of multiple Models and Entities allow you to define a Resource and reuse it wherever you need it. Using entities, you can exclude fields, create fields that are a combination of others fields. It gives you flexibility.
Related
When using Grape Entity, sometimes we want to expose a few fields from an inner/related object. We can simply do this by:
expose(:bar) { |entity| entity.foo.bar }
However if many fields needs this treatment, this can get very repetitive. Is there a way to define delegation / forward so a bunch of expose all are associated with another object?
create a separate entity for foo class, let's call it API::Entities::Foo. then you can
expose :foo, using: API::Entities::Foo
you have to follow REST guidelines to serialize like this. you are dealing with resources. don't break it to flat, cumbersome data structures.
I'm creating a non-Rails application and using DataMapper as ORM.
For entities which will be mapped to SQL tables I declare classes which include DataMapper::Resource.
The question is. Is it okay to use the instances of these classes as plain objects (pass to methods, manipulate values etc.)? Or they should be used only for persisting data (for instance in Repository classes)?
I'm new in the Ruby world and do not know the conventions.
If I have a User entity, which has methods creates, all etc., is it a good idea to create another class User, which only will store information (will have state - fields and no methods)? Analogue of POJO (Plain old java object) in Java?
I can see creating a wrapper class for a plain object list having some benefits. As you mention in the comment, if you want to store data in different ways then writing distinct classes is useful.
For typical DataMapper or ActiveRecord usage, though, I don't think it's common to create wrapper classes for plain-object lists, especially if you're not adding any methods to the collection. The main reason why it's not common is that query results in ActiveRecord or DataMapper are array-like already. Additionally, you're not really gaining any added functionality by converting your model instances to hashes. Let me show some example:
# collections are array-like
User.all.map(&:name) == User.all.to_a.map(&:name)
# converting a record to a hash doesn't add much
user = User.first
user_hash = user.attributes
user.name == user_hash[:name]
That being said, there is one caveat, and that has to do with chainable methods in the ORM:
# this is valid chaining
User.all.where(name: "max")
# this raises a NoMethodError for 'where'
User.all.to_a.where(name: "max")
where is a ORM method, not an array method. So if you convert the query result to an array you couldn't access it. For this reason, making a distinction between arrays and query collections is useful.
But how much benefit do you really get from creating an empty wrapper class?
class RecordsInMemory
def initialize(query_collection)
#list = query_collection.map(&:attributes)
end
end
records_in_memory = RecordsInMemory.new(User.all)
records_in_memory.list.map(&:name)
# versus ...
records_in_memory = User.all.map(&:attributes)
records_in_memory.map(&:name)
if you think in the long run you will add methods to the plain-object list, then you should make it into a class. But otherwise I think using clearly-named variables suffices.
I've been reading the MongoDB documentation and Spring adds a _class field by default to the stored data. Is there any way to use this information to have type inference?
For example: There is a an abstract class Animal with three subclasses Dog, Cat, Bird. Say you have a class Zoo which contains a list of animals. In the database you store those Zoo Objects. Is there any function to get a List<Animal> back with Animals that can be upcasted?
I'm using Spring so I prefer to have a solution that would work using the spring-data-mongodb. But an external mapping library would be fine too. I prefer not to write it myself as it seems basic mapping functionality.
Make sure you map all types you mentioned to be stored in the same collection (e.g. using the #Document annotation). Then you can simply execute queries against the collection handing in Animal to the according method on MongoTemplate. The underlying converter will then automatically instantiate the correct types based on the information stored in _class. The same applies to the usage of Spring Data MongoDB repositories.
I have been working on implementing my own ORM. And I was wondering how the rails path helper extracts the ID from the object. For example how would I make this work for my ORM?
#contact = Contact.first
contact_path(#contact)
Any help would be greatly appreciated!
Update:
My object does have an ID attribute and responds to it. But yet the path helper returns an error.
In a nutshell you want to be activemodel compliant. This will make url helpers, form_for(#contact) and so on work.
You also get to (optionally) use a bunch of modules dealing with
things such as validations, dirty attributes etc.
There are only a handful of methods you have to implement. There's also an
ActiveModel::Lint module that tests that your implementations of these
primitives are valid, and which also serves as documentation. In particular you need to implement to_param and persisted?. I think some of the naming stuff only gets used if you do stuff like link_to 'foo', #contact
The method checks to see if you've passed it an object, or an integer. If it's an object and that object has an id method (respond_to?(:id)), it uses its ID. Pretty dead simple.
I'm implementing several classes which does not have data by itself, just logics. These classes implements access control policy to date which depends on several parameters taken from data from other models.
I initially try to find answer to "Where to store such classes?" here, and the answer was apps/models directory. That's ok, but I like to clearly separate these classes from ActiveRecord inherited classes in hierarchy, both as file and class.
So, I created classes inside Logic module, like Logic::EvaluationLogic or Logic::PhaseLogic. I also wanted to have constants which passed between these logics. I prefer to place these constants into Logic module too. Thus, I implemented like this:
# in logic/phase_logic.rb
module Logic
PHASE_INITIAL = 0
PHASE_MIDDLE = 1000
class PhaseLogic
def self.some_phase_control_code
end
end
end
# in logic/evaluation_logic.rb
module Logic
class EvaluationLogic
def self.some_other_code
Logic::PhaseLogic.self.some_phase_control_code(Logic::PHASE_INITIAL)
end
end
end
Now, it work just fine with rspec (It passes tests I wrote without issues), but not with development server, since it can't find the Logic::PHASE_INITIAL constant.
I suspect it's related to the mismatch of the autoloading scheme of Rails and what I wanted to do. I tried to tweak rails, but no luck, ended-up with eliminating module Logic wrap.
Now the question I want to ask: How I can organize these classes with Rails?
I'm using 3.2.1 at this moment.
Posted a follow-up question "How I can organize namespace of classes in app/modules with rails?"
I am not sure whether I really understand your classes, but couldn't you create a Logic module or (I would rather do this:) PhaseLogic and EvaluationLogic objects in /lib directory?
It is not said that "Model" is always descendant of ActiveRecord. If the object belongs to "business logic" then it is a model. You can have models which do not touch database in any way. So, if your classes are "business objects", place them in 'app/models' and use like any other model.
Another question is whether you should use inheritance or modules - but I would rather think about including a module in PhaseLogic, and not about defining PhaseLogic in a module. Of course, all this depends heavily on the intended role of your objects.
Because in Ruby the class of object is not important, you do not need to use inheritance. If you want to 'plug' the logic objects into other objects, just take care that all '*Logic' classes have the required methods. I know that all I said is very vague, but I think I cannot give you some more concrete suggestions without knowing more about the role of these objects.
Ah, and one more thing!
If you find yourself fighting with Rails class autoloading, just use the old require "lib/logic.rb" in all the classes where you are using Logic::PHASE_INITIAL constants.
In this case I suppose that your problem was caused by different order of loading. The logic/evaluation_logic.rb has been loaded before logic/phase_logic.rb. The problem may disappear if you create logic.rb somewhere, where class autoloading can find it, and define these constants in that file.
Don't name your classes or modules Logic use specific names. Start with extracting logic into separate classes and then try to break them into smaller ones. Use namespaces to distinguish them from each other in lib folder, after this steps you would be able to extract some logic parts to separate gems and reduce codebase and complexity of application. Also take a look into presenter pattern.