Rails 5 API - Modifying the 'index' response object - ruby

I have a simple index function
def index
#campaign = Campaign.find_by_id(params[:campaign_id])
#characters = #campaign.characters
render json: {
characters: #characters
}
end
This works as intended, but one of the attributes is an id that is a reference to another table. To avoid the front-end having to solve for this I would prefer the backend to respond with the correct details.
Temporarily I have handled this in the create method
def create
#campaign = Campaign.find_by_id(params[:campaign_id])
#charClass = CharClass.find_by_id(params[:character_char_class_id]).class_name
#character = #campaign.characters.new(character_params)
#character.char_class_id = params[:character_char_class_id]
if #character.save!
render status: 201, json: {
message: "Successfull added this character to the campaign!",
character_name: #character.character_name + ' the ' + #charClass,
character_class: #charClass,
character_level: #character.character_level,
character_experience: #character.character_experience,
character_gold: #character.character_gold,
character_perks: get_names(#character.character_perks),
}
else
render status: 404, json: {
message: "Something went wrong: Check line 24 of the Character Controller"
}
end
end
However, this feels wrong because Characters already knows of the Char_Class relation. (Removed useless info)
create_table "characters", force: :cascade do |t|
t.string "character_class" # I added this
t.integer "char_class_id" # This was already here
t.index ["campaign_id"], name: "index_characters_on_campaign_id"
t.index ["char_class_id"], name: "index_characters_on_char_class_id"
end
So I am able to do something like Character.first.charChass.class_name and I will get the raw text output, as you see in the create: But how could I do this within the controller (or model?)
I will be doing a flow like this for numerous models so I am thinking I won't want to keep with this method.

Related

Am I logically reiterating the same code block in Ruby?

What is the difference between these two lines of Ruby code?
if params.values.any? { |value| value == "" }
and
#post = current_user.posts.build(title: params[:post][:title], content: params[:post][:content])
The contexts in which they are used are as follows, respectively:
post '/builds' do
redirect_if_not_logged_in
if params.values.any? {|value| value == ""}
erb :'builds/new', #locals: {message: "Unable to Continue!"}
else
user = User.find(session[:user_id])
#build = Build.create(title: params[:title], budget: params[:budget], user_id: params[:user.id])
redirect to "/builds/#{#build.id}"
end
end
and
post "/builds" do
redirect_if_not_logged_in
#build = current_user.builds.build(title: params[:post][:title], content: params[:build][:content])
if #build.save
redirect "/builds"
else
erb :"/builds/new.html"
end
end
if params.values.any? {|value| value == ""}
erb :'builds/new', #locals: {message: "Unable to Continue!"}
What you're doing here is returning an error message if any of the parameter values are empty. This can happen if the user didn't fill out one of the form fields on the page.
#post = current_user.posts.build(title: params[:post][:title], content: params[:post][:content])
This creates a new post object using the given parameters. If you didn't have that first code block, this might possibly set one of the values to an empty string ("").
There are other ways to do this (specifically, model-level validations), but hopefully that helps you figure out what's going on here.

Custom RSpec formatter to display passed test and result of except

Is there a way to create a custom formatter where the passed test details with a list of except is showed?
A bit of a background for this question: we are trying to migrate to RSpec for our hardware integration and system test. The results should be pushed to CouchDB. What I am trying to achieve is a reporter that could generate a similar YAML output like the following snippet:
{
"_id": "0006b6f0-c1bd-0135-1a98-455c37fe87f1",
"_rev": "1-9c9786b4b4681ee8493f182d4fc56ef9",
"sha1_repo": "68bb327b540097c10683830f0d82acbe54a47f03",
"steps": [
{
"result": "pass",
"description": "Time for Routing expect OK: 126 micro seconds (DLC and Data also OK)"
},
{
"result": "pass",
"description": "Time for Routing expect OK: 146 micro seconds (DLC and Data also OK)"
},
{
"result": "pass",
"description": "Time for Routing expect OK: 162 micro seconds (DLC and Data also OK)"
}
],
"time_start": "1513119108000",
"time_end": "1513119108000",
"result": "pass",
"testcase_title": "Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03",
"testcase_id": "TC_1zu1_BAF_Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03",
"hierarchy": [
"Hardware Integration Test",
"1 - Routing",
"1.1 Normal Routing",
"1zu1_BAF_TestCases",
"CAN_to_CAN"
]
}
With failed test there is no problem to achieve this, but we need also the results from passed test in order to be able to create long term statistics.
I can override the passed event of RSPec but the example object delivers only the description and no more info.
class EliteReporter
RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished
def example_passed(passed)
#output.printf "pass \n #{passed.example.description}"
end
end
Thank you in advance for any help.
Finally with the help of my colleague and thanks of the Tip from RSPec Emailing list I could do this.
I have created a Recorder class that collects the test results, than override the Expect methode. This way in the custom formatter I can collect all the passed results:
class ExpectWrapper
def initialize(_expect, _recorder, _description)
#expect = _expect
#recorder = _recorder
#description = _description
end
def to(matcher, failure_message=nil)
begin
expect_ret = #expect.to(matcher, failure_message) # test
# for tests that aggregate failures
if expect_ret.instance_of?(TrueClass)
#recorder.record(matcher.actual, matcher.description, #description)
else
#recorder.record_error(matcher.actual, matcher.description, failure_message, #description)
end
expect_ret
rescue RSpec::Expectations::ExpectationNotMetError => e
# for test that do not aggregate failures
#recorder.record_error(matcher.actual, matcher.description, failure_message, #description)
raise e
end
end
end
class Recorder
def self.start
##data = []
return Recorder.new
end
def record(expect, data, description)
##data << { :pass => true, :expect => expect, :value => data, :description => description }
self
end
def record_error(expect, data, failure_message, description)
##data << { :pass => false, :expect => expect, :value => data, :message => failure_message, :description => description }
self
end
def self.data
##data
end
def expect(object, value, description = "")
return ExpectWrapper.new(object.expect(value), self, description)
end
end
The custom formatter would look the following, is just an example, the data could be than put to JSON and pushed to Couch:
class EliteVerboseFormatter
RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished
def initialize(output)
#output = output
end
def example_passed(notification)
#output.puts( format_output(notification.example, Recorder) )
end
def get_test_name( group, description)
"#{group.example.example_group}/#{description}".gsub('RSpec::ExampleGroups::','')
end
def format_output( example, recorder )
test_case = get_test_name( example.example_group, example.description)
str = "**********TEST: #{test_case} ************\n"
recorder.data.each do |d|
str += sprintf("%s: ---> expected '%-10s' to '%-20s' DESC: %s \n", d[:pass] ? 'PASS' : 'FAIL', d[:expect], d[:value], d[:description])
end
str
end
def example_failed(notification)
#output.puts(format_output( notification.example, Recorder))
exception = notification.exception
message_lines = notification.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer)
exception_details = if exception
{
# drop 2 removes the description (regardless of newlines) and leading blank line
:message => message_lines.drop(2).join("\n"),
:backtrace => notification.formatted_backtrace.join("\n"),
}
end
#output.puts RSpec::Core::Formatters::ConsoleCodes.wrap(exception_details[:message], :failure)
end
end
I think you can read the Module: RSpec::Core::Formatters
you might find something helpful.
P.S. I have used Cucumber for many times, and I once wanted to custom cucumber formatter to display every step's details no matter it failed or passed. I finally got the solution by reading cucumber core documents.so I think maybe rspec core document can help you to find the solution.
I find that I cannot put the code in comment, so I put it here.
edit your code as below:
class EliteReporter
RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished
def example_passed(example)
example_failed(example)
end
end
I hope it can be helpful: )

How to check if value exists in params.permit?

I can't seem to figure out how to accomplish what I am trying to do here on my create method.
What I have right now works if there are no values, the item is deleted. However, if 1 or more param values exist, it passes and is saved. Not what I needed. I need an all or nothing scenario. I want to save only if all the permitted keys have their value. params.permit(:name, :description, :copyright)
Before an entry is saved using organizations.save!, I need to make sure none of the params that are permitted are nil or empty.
I search all over and can't seem to narrow down on an answer to my exact issue.
Here is my code:
class OrganizationsController < ApplicationController
def index
query_params = params.permit(:id, :name,)
if query_params.blank?
organizations = Organization.all
else
organizations = Organization.where(query_params)
end
render json: organizations, root: "organizations"
end
def create
organizations = Organization.new(organization_params)
if organization_params.present?
organizations.delete
else
organizations.save!
render json: organizations
end
end
private
def organization_params
params.permit(:name, :description, :copyright)
end
end
You should add validations to your model.
From your question i understand that you want to save details only if you get values in all the field, if not you don't want to save, right?. If yes, then adding validations to your model will give you what you wanted.
Add the following to your organization model
validates_presence_of :name
validates_presence_of :description
validates_presence_of :copyright
by doing so, the user won't be allowed to save the details unless and until all three fields have some value in it.
There is no need to use delete as the incomplete information will not be saved.
for more and advanced info click here
To check none of the values of organization_params hash is empty, you can do something like this:
organization_params.values.all? { |x| !x.empty? }
or, this:
organization_params.all? { |k,v| !v.empty? }
You can also check if any param value is empty:
organization_params.any? { |k,v| v.empty? }
So, your create method can be re-written as:
def create
organizations = Organization.new(organization_params)
if organization_params.any? { |k,v| v.empty? }
# at least one param is empty, so delete the record
organizations.delete
else
# all the params values are present, so save the record
organizations.save!
render json: organizations
end
end

Trying to import shapefile using RGeo Ruby gem, getting Stack Level too deep

I don't know whether this is some interaction inside my code, or perhaps naming conventions inside the shapefiles I'm using, but the following code results in a Stack level too deep message
def read_shapefile
#shp_path = Dir[File.join( #tmp_path, '*.shp') ].first
srs_database = RGeo::CoordSys::SRSDatabase::ActiveRecordTable.new
factory = RGeo::Geos.factory(:srs_database => srs_database, :srid => 96805)
#factory = RGeo::Geos::CAPIFactory.new
cartesian_preferred_factory = LandUnit.rgeo_factory_for_column(:location)
RGeo::Shapefile::Reader.open(#shp_path, factory: factory) do |file|
file.each do |record|
cartesian_cast = RGeo::Feature.cast(
record.geometry, factory: cartesian_preferred_factory, project: true)
cartesian_cast.each do |poly|
lu = LandUnit.new
lu.guid = record.attributes[#params['guid']].to_s
lu.country_code = #params['country_code']
lu.location = poly
# error happens here!
lu.save
end
end
end
end
Migrations for model:
class CreateLandUnits < ActiveRecord::Migration
def change
create_table :land_units do |t|
t.string :country_code, null: false, index: true
t.string :guid, index: true
t.geometry :location, srid: 3785
t.timestamps
end
end
end
Relevant part of schema.rb:
create_table "land_units", force: true do |t|
t.string "country_code", null: false
t.string "guid"
t.datetime "created_at"
t.datetime "updated_at"
t.spatial "location", limit: {:srid=>3785, :type=>"geometry"}
end
add_index "land_units", ["location"], :name => "index_land_units_on_location", :spatial => true
Backtrace points to active_record/transactions.rb, line 286:
def rollback_active_record_state!
remember_transaction_record_state
yield
rescue Exception
restore_transaction_record_state
raise # HERE<<<
ensure
clear_transaction_record_state
end
Tried everything. Any help would be appreciated.
Edit: In case anyone is wondering about those parameters, on the failed call they resolve to guid: '6020048.00', and country_code: 'ca'.
I should also add, that the log shows this:
(0.1ms) BEGIN
SQL (0.6ms) INSERT INTO "land_units" ("country_code", "created_at", "guid", "location", "updated_at")
VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["country_code", "ca"], ["created_at", "2015-03-23 20:59:38.771649"], ["guid", "6020048.00"],
["location", "002000000300000ec90000000100000028c164b311c749d0be4049101977159301c164b311c7508b1140491022249508cfc164b311c7671af24049103f70c23377c164b311c785400940491065d156a4f5c164b311c78493be40491082fd398873c164b311c783e2024049109c841e4650c164b311c78327ba404910ae43343896c164b311c7810e65404910b976a8c3dfc164b311c77c6113404910cf2943e8c7c164b311c777d3ba404910e3e11b1b56c164b311c771ff68404910fdcea7cc13c164b311c7716ef74049110061ae5237c164b311c76d520940491113afcec9eec164b311c7631bad4049114313691234c164b311c75f336f40491154764b6b38c164b311c75249bb40491140bebb55fac164b311c749492e40491135844d8b3fc164b311c740bbbb4049112ae8b30272c164b311c737d9274049111f6444e7acc164b311c72f4d334049111430f7e5f3c164b311c726871640491108fa115ffac164b311c71e34d2404910fe160b0597c164b311c7151806404910f28d86b4f4c164b311c70ca8b0404910e7a793a963c164b311c705b232404910deb9fc574ac164b311c703a91d404910dc20d4806ac164b311c6fb1d64404910d0ed325688c164b311c6f290ef404910c605069167c164b311c6e9e83a404910bacf71a130c164b311c6e0cba9404910af46584a4fc164b311c6f115694049105a80074a79c164b311c6f69ca64049103bc8277f0fc164b311c6facd224049102626e4a97cc164b311c6fec1f340491011192ee217c164b311c703ca8a40490ff67dac4e4ac164b311c707dc6d40490fe171e94d20c164b311c70c6bce40490fc98cc82ecdc164b311c720cc5340490fe45c78305ac164b311c73527f340490ffe8cde5378c164b311c749d0be4049101977159301"], ["updated_at", "2015-03-23 20:59:38.771649"
]]
(0.2ms) ROLLBACK
The SQL it shows executes just fine in the console! That's the crazy bit. This suggests to me that that the problem is some before_save method, but I haven't defined any.
I think I may have this figured out. This spatial reference code, "96805", doesn't have any entries at spatialreference.org. I'm not saying it doesn't exist, but its probably referenced under some other srid.
You can track it down though, I imagine it exists but is referenced using some other code. Oftentimes the same proj4 code is referenced under multiple SRID references. Anyway, for one of your shapefile sets for this mysteriously projected file, open the *.prj file in a text editor. That will show you your proj4 code. You can deal with this multiple ways from here. One thing is you can copy this proj4 code into google and try to get the official srid for it then go from there.
Alternatively, you can directly utilize the proj4 code to recast the geometry without any official srid.

Rho Mobile: Parse and create a model using JSON

I am able to parse a JSON using the following code
$httpresult = #params['body']
$jsonresult = Rho::JSON.parse($httpresult)
But I don't know how to create a model from $jsonresult.
First, using app_info you can print the result coming from the server to check if the response is valid JSON string.
Second, i think you must decode the url in order to parse it by using:
Rho::JSON.parse(Rho::RhoSupport.url_decode(#params['body']))
Once you've the data in json_result, you can put them in a pre-existing Model.
Supposing that you've already created a model with the name "Product", you can use transactions to speed up the process.
At the beginning of your module you've to require the model name:
require_source 'Product'
Then you can do this callback:
def get_callback
if #params['status'] == "ok"
json_result = Rho::JSON.parse(#params['body'])
db = ::Rho::RHO.get_src_db('Product')
db.start_transaction
Product.delete_all
begin
json_result.each do |item|
Product.create({:Brand => item["B rand"], :Name => item["Name"], :SKU => d["SKU"]})
end
db.commit
rescue Exception => e
trace_msg = e.backtrace.join("\n")
puts 'Application initialize failed: ' + e.inspect + ";Trace: #{trace_msg}"
db.rollback
end
WebView.navigate Rho::RhoConfig.start_path
else
WebView.navigate url_for :action => :show_error
end
end

Resources