Is it possible to force ActiveRecord's #saved_change_to_attribute? method to equal true when the object is being updated? - ruby-on-rails

I'm writing a microservice (Service A) that relies heavily on one of the other services (Service B). Service B is a Ruby on Rails application. Both Service A and Service B share a db. Service A receives a request and updates a table in the db. When Service B receives that same request, it uses ActiveRecord and a lot of callbacks on the AR model. Rather than spreading the same business logic between services, I'd like to event that request to Service B. This way, Service B can "update" the model (the word "update" is in parentheses because the services share the same db and the record is already updated) and trigger all of the callbacks.
For example, assume a request is made to Service A to update the name of a user. Service A updates the user's name in the db, and then events this change to Service B. Service B receives the request and does something similar to the following:
user = User.find(id: user_id)
user.update_attribue(:name, name)
This triggers ActiveRecord callbacks on the User model. In one of the callbacks, the following conditional statement is made: saved_change_to_name?. This returns false because the record wasn't actually updated since Service A already updated the db.
Is it possible to force this change and have saved_change_to_name? result in true?
For example:
user = User.find(id: user_id)
user.force_update_attribue(:name, name)
Service A still needs to update the db because we're moving towards an approach where each service has its own data store. Removing the conditional is also not an option here.

Related

Save global attribute value when new session starts

I have two fields in SAP Fiori App: Template_ID and Offer_ID.
I want to choose value in Offer_ID depending on Template_ID field value.
For solving this problem I've tried to do this steps:
When the user click on Template_ID field in Back-End runs the method:
CL_CUAN_CAMPAIGN_DPC->contentset_get_entityset().
This method has returning paramater et_result. In et_result I have the necessary field temp_id.
For saving temp_id value I created a global attribute in class ZCL_CUAN_CLASS.
ZCL_CUAN_CLASS=>GV_CONTENT = VALUE #( et_result[ 1 ]-temp_ID OPTIONAL ).
I'll use this global attribute as an input parameter for my second method:
CL_CUAN_CAMPAIGN_DPC->GET_OFFER_BY_TEMPLATE().
This method returns to me the internal table with the offer_id, which belongs to my choosen temp_id.
But when the user click on Offer_ID field on Web UI, in debugging I see that my global attribute is blank.
May be it's because of session or something else, but it's blank.
OData is a stateless protocol, meaning the server responds your query, then forgets you were ever there. By definition, this does not allow you to transport main memory content from one request to the next.
User interfaces on the other hand usually require state. It can be gained through one of the following options:
Stateful user interface
As Haojie points out, one solution is to store the data that was selected in the user interface and submit it as a filter criterion back to the server with the next request. Having a stateful user interface is the standard solution for stateless server apps.
Stateful persistence
Another option is to store the data permanently in the server's database, in ABAP preferredly in a business object. This object has a unique identifier, probably a GUID, that you can reference in your requests to identify the process you are working on.
Draft persistence
If not all information is available in one step, such as in a multi-step wizard, should not become "active" right away, or you want to be able to switch devices while working on a multi-step process, drafts are an option. Drafts are regular business objects, with the one specialty that they remain inert until the user triggers a final activation step.
Soft state
For performance optimizations, you can have a look at SAP Gateway's soft state mode, which allows you to buffer some data to be able to respond to related requests more quickly. This is generally discouraged though, as it contradicts the stateless paradigm of OData.
Stateful protocol
In some cases, stateless protocols like OData are not the right way to go. For example, banking apps still prefer to pertain state to avoid that users remain logged in infinitely, and thus becoming vulnerable to attacks like CSRF. If this is the case for you, you should have a look at ABAP WebDynpro for your user interface. Generally, stateful server protocols are considered inferior because they bind lots of server resources for long times and thus cannot handle larger user numbers.
When ther user click on OfferId field, it will start a NEW session and of course what you store as GV_CONTENT in class ZCL_CUAN_CLASS is lost.
What you should do is that for the second request you should send to backend with filter Template_ID so in your CL_CUAN_CAMPAIGN_DPC->GET_OFFER_BY_TEMPLATE() method, you can further process the result by Template_ID.
Or SET/GET Parameter.

Validity of domain entity method based on context or caller

I am currently reading about DDD and have a issue how to implement a certain validation.
Scenario:
I have an entity Group that contains a list of Members which in turn consists of a User and a MemberState (Member, Admin). The entity has a method MakeAdmin(User user) that turns a member into an admin.
The rule is: Only if the current user is admin of the group, it is allowed to turn the member to an admin.
I have three possible implementations in mind, with some caveats each.
Variant 1
Group enitity gets a dependency IUserContext injected via constructor to get the current user. With that the entity can check if the current user is admin and is allowed to switch the member state.
Caveat: I don't like that the domain entity has such a dependency injected and I don't think that is correct in a DDD view.
Variant 2
Method MakeAdmin() gets an additional parameter which turns it into MakeAdmin(User user, User currentUser). That removes the requirement for the IUserContext dependency in the entity.
Caveat: I am not sure if it is correct at all that the entity validates this rule, because it is not really an invariant of the entity.
Variant 3
Validate this rule in an application service, and only call MakeAdmin(User user) if validation passed.
Caveat: I consider the rule domain specific, so I think it is not correct to put it in application layer.
So what would be the best option, or is there some totally different variant?
My suggestion would be to go with your third option: Authorise access in the application/integration layer and then call into the domain.
This goes for any domain functionality really. The domain should not concern itself with whether the action may or may not be performed based on authorisation but rather only with domain concerns. If that authorisation action happens to be a domain concern in a particular Identity & Access Control bounded context that it would make sense but you would not run into those too often.

In grails app, the field in service is shared by user or not?

I have a question about the field in service is shared by user or not? My grails version is 2.3.4.
Now I have a controller with two actions and trying to set and get value from service field.
//This is a controller
class TestController{
def testService
def setValue(){
testService.setValue("123")
}
def getValue(){}
println testService.getValue()
}
}
//This is a service
class TestService{
def var
def setValue(def value){
var = value
}
def getValue(){}
return var
}
}
In other words, if several users are using the action getValue in the controller, do they share the var in the service or not?
Thank you!
Yes, by default all services are singletons, so there is only one instance of service per webapp, but functions inside are not synchronised:
By default, access to service methods is not synchronised, so nothing prevents concurrent
execution of those methods. In fact, because the service is a
singleton and may be used concurrently, you should be very careful
about storing state in a service. Or take the easy (and better) road
and never store state in a service.
You can change this behaviour by placing a service in a particular
scope. The supported scopes are:
prototype - A new service is created every time it is injected into another class
request - A new service will be created per request
flash - A new service will be created for the current and next request only
flow - In web flows the service will exist for the scope of the flow
conversation - In web flows the service will exist for the scope of the conversation. ie a root flow and its sub flows
session - A service is created for the scope of a user session
singleton (default) - Only one instance of the service ever exists

Class instance variables in ActiveRecord (Ruby On Rails)

I am trying to store a value in a model class, for example, values of couple of checkboxes.
I figured I could store them in a class instance variable, however, these values are cleared when I a user clicks elsewhere on the screen.
How can I maintain the values of my class instance variables.
For example
class Person < ActiveRecord::Base
def setAge(age)
##age = age
def getAge
return ##age
however, looks like ##age is empty after it is being set.
The rails framework reloads the classes in the development mode. Any values set in prior requests to class variable is lost in a new request. If you run your server in the production mode, your code will work.
What you are trying to do is a bad practice as concurrent requests can overwrite the state and when you spawn multiple instances of your rails server this solution will not work(as mentioned by #iltempo)
If you are trying to persist the state across two client requests, you are better off using session variables.
request 1
session[:age] = params['age']
request 2
u = User.new
u.age = session[:age]
As #nash mentioned this is not Ruby code here.
However if you store data on class level it is only valid for the current process. That means if you run multiple processes like passenger and other web servers data will not be shared between these processes.
Also age sounds to be related to a Person instance instead of the class.

Responsibilities of Service and Repository layers

Just trying to get my head round the responsibilities of the service layer and repository layer when saving an object to my persistence store.
My current under standing is this:
In my controller I have created a "Note" object from the data submitted by the user (from the form). The user then calls "Save" on the "NoteService" (which is there via dependency injection).
Within the "Save" method on the "NoteService" I carry out my business logic validation and then pass the "Note" object to the "Save" method of the "NoteRepository".
The "Save" method of the "NoteRepository" then checks to see if there is an existing primary key on this object and if so then get's that object from the db and updates it's properties using the "Note" object passed through and it's then saved back to the db.
If there is no primary key then the object is simply saved to the db and the then returned to the service with it's newly created primary key.
Your separation of concerns sounds pretty good to me. We follow the same pattern, but tend to add one more layer right about the repository layer. We call it the domain layer and perform all our business logic in there. Our service layer is just a pass through to our domain in case we need to publish any of our services to an ESB in the future.
The biggest benefit of what you are doing is not cluttering all the business and DB logic in the controller which a lot of people tend to do. You always want your controllers to be as light as possible if you really want to follow MVC.

Resources