How to update only changed attributes in Hanami Model? - ruby

Given I am using Hanami Model version 0.6.1, I would like the repository update only the changed attributes of an entity.
For example:
user_instance1 = UserRepository.find(1)
user_instance1.name = 'John'
user_instance2 = UserRepository.find(1)
user_instance2.email = 'john#email.com'
UserRepository.update(user_instance1)
#expected: UPDATE USER SET NAME = 'John' WHERE ID = 1
UserRepository.update(user_instance2)
#expected: UPDATE USER SET EMAIL = 'john#email.com' WHERE ID = 1
But what it happens is that the second command overrides all fields, including those which were not changed.
I know I can use the Hanami::Entity::DirtyTracking to get all changed attributes, but I do not know how to update an entity partially with these attributes.
Is there a way to do this?

hanami entity is an immutable data structure. That's why you can't change data with setters:
>> account = AccountRepository.new.first
=> #<Account:0x00007ffbf3918010 #attributes={ name: 'Anton', ...}>
>> account.name
=> "Anton"
>> account.name = "Other"
1: from /Users/anton/.rvm/gems/ruby-2.5.0/gems/hanami-model-1.2.0/lib/hanami/entity.rb:144:in `method_missing'
NoMethodError (undefined method `name=' for #<Account:0x00007ffbf3918010>)
Instead, you can create a new one entity, for example:
# will return a new account entity with updated attributes
>> Account.new(**account, name: 'A new one')
And also, you can use #update with old entity object:
>> AccountRepository.new.update(account.id, **account, name: 'A new name')
=> #<Account:0x00007ffbf3918010 #attributes={ name: 'Anton', ...}>
>> account = AccountRepository.new.first
=> #<Account:0x00007ffbf3918010 #attributes={ name: 'Anton', ...}>
>> account.name
=> "A new name"

Related

How do I do model versioning with AutoMLImageTrainingJobRunOp?

I know in Vertex AI you can version models. You can eg upload a model and set its parent_model:
model_v2 = aip.Model.upload(parent_model=model_v1.resource_name,...
And I know in the GUI you can create an AutoML model that is a version of an existing one, but how do you do it in code?
In a pipeline I use AutoMLImageTrainingJobRunOp but it does not have a parent_model parameter.
You can enable model versioning using the below code snippet:
from google.cloud import aiplatform
DISPLAY_NAME = "model_name"
models = aiplatform.Model.list(filter=("display_name={}").format(DISPLAY_NAME))
if len(models) == 0:
model_upload = aiplatform.Model.upload(
display_name = DISPLAY_NAME, # Your model display name
version_description="Add model description here", #Add model description
version_aliases=["v1"], # Create Model Alias
labels={"release": "dev"}, #Label your model
artifact_uri = model.uri[:-6],
...
)
else:
parent_model = models[0].resource_name
version_id = models[0].version_id
model_upload = aiplatform.Model.upload(
display_name = DISPLAY_NAME,
...,
parent_model = parent_model
)
There are other parameters also mentioned in the code for serving containers, you can remove them if you don't need that.

DEPRECATION WARNING: implicit join references. Rails don't want to be a SQL parser for includes

I am upgrading an old app from rails3 to rails 4. Currently on rails 4.0
I have a lot of such deprecation warnings while running rspec:
Currently, Active Record recognizes the table in the string, and
knows to JOIN the comments table to the query, rather than loading
comments in a separate query. However, doing this without writing a
full-blown SQL parser is inherently flawed. Since we don't want to
write an SQL parser, we are removing this functionality. From now on,
you must explicitly tell Active Record when you are referencing a
table from a string:
Post.includes(:comments).where("comments.title =
'foo'").references(:comments)
If you don't rely on implicit join references you can disable the
feature entirely by setting
`config.active_record.disable_implicit_join_references = true`.
DEPRECATION WARNING: It looks like you are eager loading table(s)
(one of: product_masters, product_master_names) that are referenced
in a string SQL snippet. For example:
Post.includes(:comments).where("comments.title = 'foo'")
Here is the lines that causing error
def find_product_master
masters = ProductMaster.includes(:product_master_names).where("gcc = 1 and crossed_product_master_id is null and product_masters.supplier_id is null && (product_masters.slug = :name or product_masters.slug = :clean_name or product_master_names.slug = :name or product_master_names.slug = :clean_name) and product_masters.color = :color and (product_subfamily_id = :subfamily_id or second_product_subfamily_id = :subfamily_id)", name: line.file_name.parameterize, clean_name: line.file_name.parameterize.gsub("chateau","").parameterize, color: line.file_color, subfamily_id: line.product_subfamily_id)
masters.size == 1 ? line.update_column(:product_master_id, masters.first.id) : line.update_column(:status, "product_master_missing")
end
I tried like it was described in warning
def find_product_master
masters = ProductMaster.includes(:product_master_names).where("gcc = 1 and crossed_product_master_id is null and product_masters.supplier_id is null && (product_masters.slug = :name or product_masters.slug = :clean_name or product_master_names.slug = :name or product_master_names.slug = :clean_name) and product_masters.color = :color and (product_subfamily_id = :subfamily_id or second_product_subfamily_id = :subfamily_id)".references(:product_master_names), name: line.file_name.parameterize, clean_name: line.file_name.parameterize.gsub("chateau","").parameterize, color: line.file_color, subfamily_id: line.product_subfamily_id)
masters.size == 1 ? line.update_column(:product_master_id, masters.first.id) : line.update_column(:status, "product_master_missing")
end
But then i received an error
NoMethodError:
undefined method `references' for #<String:0x00000010f2c730>
Just chain references behind the include call instead of calling reference on a one of the parameter of where:
masters = ProductMaster
.includes(:product_master_names)
.references(:product_master_names)
.where(
# ...
)

How to get database name from a connection?

Using the driver rethinkdbdash...
Given:
const rConnection = r.db('someDb').table('someTable')
How do I get the database name or the table name this connection is using from the variable rConnection?
Similar to my answer here, here's an ugly solution that works (tested on rethinkdb, not rethinkdbdash):
nesh> let mom = (q, fn) => q.toString().match(new RegExp(fn + '\\(\\"(.*?)\\"\\)'))[1]
undefined
nesh> rql = r.db('foo').table('bar')
nesh> mom(rql, 'db')
'foo'
nesh> mom(rql, 'table')
'bar'

Enumerating settable parameters

I successfully setup a connection to my Rally project, but am not able to programatically set the "Submitted By" field when opening defects. I am misnaming the parameter, or it is not settable via the REST API. Any help on querying modifiable parameters, link to attribute names, or code to set the "Submitted By" parameter would be greatly appreciated.
My goal is to have a webpage where folks submit defects or stories, along with their email, and use that to populate their username in the "Submitted By" field. My current setup can open Stories and Defects but only anonymously since the second to last line has no effect.
I am using the Ruby API and the following is the ruby code that gets invoked when a user clicks on a button on the main page, which redirects them to localhost:4567/rally_defect where Sinatra is listening.
require 'rubygems'
require 'rally_rest_api'
require 'sinatra'
custom_headers = CustomHttpHeader.new
custom_headers.name = 'Mail 2 Rally'
custom_headers.version = '0.1'
custom_headers.vendor = 'Rally Software'
rally_server = 'https://rally1.rallydev.com/slm'
rally_username = <insert rally username>
rally_pwd = <insert rally password>
rally = RallyRestAPI.new(:base_url => rally_server,
:username => rally_username,
:password => rally_pwd,
:http_headers => custom_headers)
get '/rally_defect' do
rally.create(:defect, :name => "Test Defect",
:description => "This is a test",
:SubmittedBy => "test")
end
If you're getting started building this integration, I'd strongly recommend using rally_api instead of rally_rest_api.
rally_rest_api is in the process of being deprecated for a number of issues around performance and stability. There won't be any further development on rally_rest_api moving forward.
You can find documentation on rally_api here:
https://developer.help.rallydev.com/ruby-toolkit-rally-rest-api-json
rally_api does require Ruby 1.9.2 or higher.
rally_api's syntax is quite similar to rally_rest_api. The rally_api version of what you're attempting above would look something like the code sample below. Note that any "set-able" parameter can be set simply by including its name in the object hash and setting that to a value.
The best place to look for the Rally Webservices API object model, including Artifact attributes, allowed values, and whether they are write-able is the Webservices API documentation:
https://rally1.rallydev.com/slm/doc/webservice
When an attribute of a Rally Artifact is itself a Rally Object like a User, for example, either lookup the Object in Rally first (the example below does this), or, set the value to the REST URL reference to the object in Rally. For instance, if you know that user#company.com has ObjectID 12345678910 in Rally, then you can do:
user_ref = "/user/12345678910"
new_defect["SubmittedBy"] = user_ref
The code below shows the way to do a lookup into Rally to get the User object, given an email-formatted UserID, since I'm assuming you'll probably need to accomodate multiple different users.
require 'rubygems'
require 'rally_api'
require 'sinatra'
#Setting custom headers
headers = RallyAPI::CustomHttpHeader.new()
headers.name = 'Mail 2 Rally'
headers.vendor = "My Company"
headers.version = "0.1"
# Rally credentials
rally_username = <insert rally username>
rally_pwd = <insert rally password>
rally_server = 'https://rally1.rallydev.com/slm'
# Rally REST API config
config = {:base_url => rally_server}
config[:username] = rally_username
config[:password] = rally_pwd
config[:workspace] = "Workspace Name"
config[:project] = "Project Name"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
# New up rally connection config
#rally = RallyAPI::RallyRestJson.new(config)
# Lookup UserID for SubmittedBY
submitted_by_user_id = "user#company.com"
user_query = RallyAPI::RallyQuery.new()
user_query.type = :user
user_query.fetch = "ObjectID,UserName,DisplayName"
user_query.order = "UserName Asc"
user_query.query_string = "(UserName = \"#{submitted_by_user_id}\")"
# Query for user
user_query_results = #rally.find(user_query)
submitted_by_user = user_query_results.first
# Create the Defect
new_defect = {}
new_defect["Name"] = "Test Defect"
new_defect["Description"] = "This is a test"
new_defect["SubmittedBy"] = submitted_by_user
new_defect_create_result = #rally.create(:defect, new_defect)
puts "New Defect created FormattedID: #{new_defect_create_result.FormattedID}"

What is a dirty resource?

I just started using Datamapper.
I am trying to update an object. I get the object/model using its id:
u1 = User.get(1)
u1.name = "xyz"
u1.update
which throws a error/raises an exception. I tried again:
u1 = User.get(1)
and after that:
u1.update({:name => "xyz"})
will throw false and dirty? returns true.
After that any call to update would fail saying it is dirty.
I can do a save by:
u1.name = "xyz"
u1.save
Here are my questions:
What should I be using: Save or update?
Should I say get(id) even to just change one field?
When should I use update? What is the syntax: user.update({ ....}) or user.name = "xyz"; user.update?
What is dirty?, and is it once I make a object dirty do I have to
get the object fresh from the database to the variable?
When you fetched a resource from the db and then changed its attributes then the resource becomes 'dirty'. This means that the resource is loaded into memory and its state has changed and changes can be persisted in the db.
You use #save to persist changes made to a loaded resource and you use #update when you want to immediately persist changes without changing resource's state to 'dirty'. Here's an example session:
User.create(:name => 'Ted')
# update user via #save
user = User.get(1)
user.name = 'John'
user.dirty? # => true
user.save
# update user via #update
user = User.get(1)
user.update(:name => 'John')
user.dirty? # => false

Resources